diff --git a/DEPS b/DEPS
index 042d826..b14694d8 100644
--- a/DEPS
+++ b/DEPS
@@ -39,11 +39,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c988d2c1c840689e89a7bbcf39cf10ff818708ae',
+  'skia_revision': 'bf90520f63415f539cd5792a18efbd79cb86be0a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '6c1ae369aed18a148e0062adf7203b905d78a1d2',
+  'v8_revision': '8ec2f9cfc4a1852fdcb5b1965637c258bc22ba66',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -181,13 +181,13 @@
     Var('chromium_git') + '/external/bidichecker/lib.git' + '@' + '97f2aa645b74c28c57eca56992235c79850fa9e0',
 
   'src/third_party/webgl/src':
-   Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '397fdf9245912ce37a01611b7a0ad02cd80de89f',
+   Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '421bc6095d1065bb6057e33fbab737b35d39930d',
 
   'src/third_party/webdriver/pylib':
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
 
   'src/third_party/libvpx_new/source/libvpx':
-   Var('chromium_git') + '/webm/libvpx.git' + '@' +  '7d28d12ef34f6cbb6b1e18f3b23b71392fd3ddf5',
+   Var('chromium_git') + '/webm/libvpx.git' + '@' +  'ce3f4ade670cf02e05998f4ca50e08736802f5e7',
 
   'src/third_party/ffmpeg':
    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '525a71ab8ee3b61da48138f7e1c406989d2879fc',
diff --git a/WATCHLISTS b/WATCHLISTS
index 5082c5f..75b4273 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1230,7 +1230,9 @@
     'blimp' : ['dtrainor+watch-blimp@chromium.org',
                'kmarshall+watch-blimp@chromium.org',
                'maniscalco+watch-blimp@chromium.org',
-               'nyquist+watch-blimp@chromium.org'],
+               'marcinjb+watch-blimp@chromium.org',
+               'nyquist+watch-blimp@chromium.org',
+               'sriramsr+watch-blimp@chromium.org'],
     'bookmarks': ['noyau+watch@chromium.org', 'tfarina@chromium.org'],
     'browser_chromeos': ['davemoore+watch@chromium.org'],
     'browser_components': ['browser-components-watch@chromium.org'],
diff --git a/base/profiler/native_stack_sampler_win.cc b/base/profiler/native_stack_sampler_win.cc
index bc935c0..80121b2 100644
--- a/base/profiler/native_stack_sampler_win.cc
+++ b/base/profiler/native_stack_sampler_win.cc
@@ -4,11 +4,17 @@
 
 #include <objbase.h>
 #include <windows.h>
+#include <winternl.h>
 
+#include <cstdlib>
 #include <map>
 #include <utility>
+#include <vector>
 
+#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/profiler/native_stack_sampler.h"
 #include "base/profiler/win32_stack_frame_unwinder.h"
 #include "base/strings/string_util.h"
@@ -24,17 +30,121 @@
 
 namespace {
 
+// The thread environment block internal type.
+struct TEB {
+  NT_TIB Tib;
+  // Rest of struct is ignored.
+};
+
+// Returns the thread environment block pointer for |thread_handle|.
+const TEB* GetThreadEnvironmentBlock(HANDLE thread_handle) {
+  // Define the internal types we need to invoke NtQueryInformationThread.
+  enum THREAD_INFORMATION_CLASS { ThreadBasicInformation };
+
+  struct CLIENT_ID {
+    HANDLE UniqueProcess;
+    HANDLE UniqueThread;
+  };
+
+  struct THREAD_BASIC_INFORMATION {
+    NTSTATUS ExitStatus;
+    TEB* Teb;
+    CLIENT_ID ClientId;
+    KAFFINITY AffinityMask;
+    LONG Priority;
+    LONG BasePriority;
+  };
+
+  using NtQueryInformationThreadFunction =
+      NTSTATUS (WINAPI*)(HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG,
+                         PULONG);
+
+  const NtQueryInformationThreadFunction nt_query_information_thread =
+      reinterpret_cast<NtQueryInformationThreadFunction>(
+          ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"),
+                           "NtQueryInformationThread"));
+  if (!nt_query_information_thread)
+    return nullptr;
+
+  THREAD_BASIC_INFORMATION basic_info = {0};
+  NTSTATUS status =
+      nt_query_information_thread(thread_handle, ThreadBasicInformation,
+                                  &basic_info, sizeof(THREAD_BASIC_INFORMATION),
+                                  nullptr);
+  if (status != 0)
+    return nullptr;
+
+  return basic_info.Teb;
+}
+
+// If the value at |pointer| points to the original stack, rewrite it to point
+// to the corresponding location in the copied stack.
+void RewritePointerIfInOriginalStack(uintptr_t top, uintptr_t bottom,
+                                     void* stack_copy, const void** pointer) {
+  const uintptr_t value = reinterpret_cast<uintptr_t>(*pointer);
+  if (value >= bottom && value < top) {
+    *pointer = reinterpret_cast<const void*>(
+        static_cast<unsigned char*>(stack_copy) + (value - bottom));
+  }
+}
+
+// Rewrites possible pointers to locations within the stack to point to the
+// corresponding locations in the copy, and rewrites the non-volatile registers
+// in |context| likewise. This is necessary to handle stack frames with dynamic
+// stack allocation, where a pointer to the beginning of the dynamic allocation
+// area is stored on the stack and/or in a non-volatile register.
+//
+// Eager rewriting of anything that looks like a pointer to the stack, as done
+// in this function, does not adversely affect the stack unwinding. The only
+// other values on the stack the unwinding depends on are return addresses,
+// which should not point within the stack memory. The rewriting is guaranteed
+// to catch all pointers because the stacks are guaranteed by the ABI to be
+// sizeof(void*) aligned.
+//
+// Note: this function must not access memory in the original stack as it may
+// have been changed or deallocated by this point. This is why |top| and
+// |bottom| are passed as uintptr_t.
+void RewritePointersToStackMemory(uintptr_t top, uintptr_t bottom,
+                                  CONTEXT* context, void* stack_copy) {
+#if defined(_WIN64)
+  DWORD64 CONTEXT::* const nonvolatile_registers[] = {
+    &CONTEXT::R12,
+    &CONTEXT::R13,
+    &CONTEXT::R14,
+    &CONTEXT::R15,
+    &CONTEXT::Rdi,
+    &CONTEXT::Rsi,
+    &CONTEXT::Rbx,
+    &CONTEXT::Rbp,
+    &CONTEXT::Rsp
+  };
+
+  // Rewrite pointers in the context.
+  for (size_t i = 0; i < arraysize(nonvolatile_registers); ++i) {
+    DWORD64* const reg = &(context->*nonvolatile_registers[i]);
+    RewritePointerIfInOriginalStack(top, bottom, stack_copy,
+                                    reinterpret_cast<const void**>(reg));
+  }
+
+  // Rewrite pointers on the stack.
+  const void** start = reinterpret_cast<const void**>(stack_copy);
+  const void** end = reinterpret_cast<const void**>(
+      reinterpret_cast<char*>(stack_copy) + (top - bottom));
+  for (const void** loc = start; loc < end; ++loc)
+    RewritePointerIfInOriginalStack(top, bottom, stack_copy, loc);
+#endif
+}
+
 // Walks the stack represented by |context| from the current frame downwards,
 // recording the instruction pointers for each frame in |instruction_pointers|.
-int RecordStack(CONTEXT* context,
-                int max_stack_size,
-                const void* instruction_pointers[],
-                Win32StackFrameUnwinder* frame_unwinder) {
+int RecordStack(CONTEXT* context, int max_stack_size,
+                const void* instruction_pointers[]) {
 #ifdef _WIN64
+  Win32StackFrameUnwinder frame_unwinder;
   int i = 0;
   for (; (i < max_stack_size) && context->Rip; ++i) {
     instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
-    if (!frame_unwinder->TryUnwind(context))
+    if (!frame_unwinder.TryUnwind(context))
       return i + 1;
   }
   return i;
@@ -133,29 +243,31 @@
     ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
 }
 
-// Suspends the thread with |thread_handle|, records the stack into
-// |instruction_pointers|, then resumes the thread. Returns the size of the
-// stack.
-//
-// IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
-// ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
-// target thread before it was suspended. This is why we pass instruction
-// pointers and module handles as preallocated arrays rather than vectors, since
-// vectors make it too easy to subtly allocate memory.
-int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
-                                const void* instruction_pointers[]) {
-  Win32StackFrameUnwinder frame_unwinder;
+// ScopedSuspendThread --------------------------------------------------------
 
-  if (::SuspendThread(thread_handle) == -1)
-    return 0;
+// Suspends a thread for the lifetime of the object.
+class ScopedSuspendThread {
+ public:
+  ScopedSuspendThread(HANDLE thread_handle);
+  ~ScopedSuspendThread();
 
-  int stack_depth = 0;
-  CONTEXT thread_context = {0};
-  thread_context.ContextFlags = CONTEXT_FULL;
-  if (::GetThreadContext(thread_handle, &thread_context)) {
-    stack_depth = RecordStack(&thread_context, max_stack_size,
-                              instruction_pointers, &frame_unwinder);
-  }
+  bool was_successful() const { return was_successful_; }
+
+ private:
+  HANDLE thread_handle_;
+  bool was_successful_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSuspendThread);
+};
+
+ScopedSuspendThread::ScopedSuspendThread(HANDLE thread_handle)
+    : thread_handle_(thread_handle),
+      was_successful_(::SuspendThread(thread_handle) != -1) {
+}
+
+ScopedSuspendThread::~ScopedSuspendThread() {
+  if (!was_successful_)
+    return;
 
   // Disable the priority boost that the thread would otherwise receive on
   // resume. We do this to avoid artificially altering the dynamics of the
@@ -167,11 +279,58 @@
   // conditions at the time of SuspendThread and those conditions are satisfied
   // before priority boost is reenabled. The measured length of this window is
   // ~100us, so this should occur fairly rarely.
-  ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
-  bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
+  ScopedDisablePriorityBoost disable_priority_boost(thread_handle_);
+  bool resume_thread_succeeded = ::ResumeThread(thread_handle_) != -1;
   CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
+}
 
-  return stack_depth;
+// Suspends the thread with |thread_handle|, copies its stack and resumes the
+// thread, then records the stack into |instruction_pointers|. Returns the size
+// of the stack.
+//
+// IMPORTANT NOTE: No allocations from the default heap may occur in the
+// ScopedSuspendThread scope, including indirectly via use of DCHECK/CHECK or
+// other logging statements. Otherwise this code can deadlock on heap locks in
+// the default heap acquired by the target thread before it was suspended. This
+// is why we pass instruction pointers as preallocated arrays.
+int SuspendThreadAndRecordStack(HANDLE thread_handle,
+                                const void* base_address,
+                                void* stack_copy_buffer,
+                                size_t stack_copy_buffer_size,
+                                int max_stack_size,
+                                const void* instruction_pointers[]) {
+  CONTEXT thread_context = {0};
+  thread_context.ContextFlags = CONTEXT_FULL;
+  // The stack bounds are saved to uintptr_ts for use outside
+  // ScopedSuspendThread, as the thread's memory is not safe to dereference
+  // beyond that point.
+  const uintptr_t top = reinterpret_cast<uintptr_t>(base_address);
+  uintptr_t bottom = 0u;
+
+  {
+    ScopedSuspendThread suspend_thread(thread_handle);
+
+    if (!suspend_thread.was_successful())
+      return 0;
+
+    if (!::GetThreadContext(thread_handle, &thread_context))
+      return 0;
+#if defined(_WIN64)
+    bottom = thread_context.Rsp;
+#else
+    bottom = thread_context.Esp;
+#endif
+
+    if ((top - bottom) > stack_copy_buffer_size)
+      return 0;
+
+    std::memcpy(stack_copy_buffer, reinterpret_cast<const void*>(bottom),
+                top - bottom);
+  }
+
+  RewritePointersToStackMemory(top, bottom, &thread_context, stack_copy_buffer);
+
+  return RecordStack(&thread_context, max_stack_size, instruction_pointers);
 }
 
 // NativeStackSamplerWin ------------------------------------------------------
@@ -188,6 +347,15 @@
   void ProfileRecordingStopped() override;
 
  private:
+  enum {
+    // Intended to hold the largest stack used by Chrome. The default Win32
+    // reserved stack size is 1 MB and Chrome Windows threads currently always
+    // use the default, but this allows for expansion if it occurs. The size
+    // beyond the actual stack size consists of unallocated virtual memory pages
+    // so carries little cost (just a bit of wated address space).
+    kStackCopyBufferSize = 2 * 1024 * 1024
+  };
+
   // Attempts to query the module filename, base address, and id for
   // |module_handle|, and store them in |module|. Returns true if it succeeded.
   static bool GetModuleForHandle(HMODULE module_handle,
@@ -209,9 +377,18 @@
                     std::vector<StackSamplingProfiler::Module>* modules);
 
   win::ScopedHandle thread_handle_;
+
+  // The stack base address corresponding to |thread_handle_|.
+  const void* const thread_stack_base_address_;
+
+  // Buffer to use for copies of the stack. We use the same buffer for all the
+  // samples to avoid the overhead of multiple allocations and frees.
+  const scoped_ptr<unsigned char[]> stack_copy_buffer_;
+
   // Weak. Points to the modules associated with the profile being recorded
   // between ProfileRecordingStarting() and ProfileRecordingStopped().
   std::vector<StackSamplingProfiler::Module>* current_modules_;
+
   // Maps a module handle to the corresponding Module's index within
   // current_modules_.
   std::map<HMODULE, size_t> profile_module_index_;
@@ -220,7 +397,10 @@
 };
 
 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
-    : thread_handle_(thread_handle.Take()) {
+    : thread_handle_(thread_handle.Take()),
+      thread_stack_base_address_(
+          GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase),
+      stack_copy_buffer_(new unsigned char[kStackCopyBufferSize]) {
 }
 
 NativeStackSamplerWin::~NativeStackSamplerWin() {
@@ -236,11 +416,17 @@
     StackSamplingProfiler::Sample* sample) {
   DCHECK(current_modules_);
 
+  if (!stack_copy_buffer_)
+    return;
+
   const int max_stack_size = 64;
   const void* instruction_pointers[max_stack_size] = {0};
   HMODULE module_handles[max_stack_size] = {0};
 
   int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(),
+                                                thread_stack_base_address_,
+                                                stack_copy_buffer_.get(),
+                                                kStackCopyBufferSize,
                                                 max_stack_size,
                                                 instruction_pointers);
   FindModuleHandlesForAddresses(instruction_pointers, module_handles,
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 41aa693..10f11e1 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cstdlib>
+
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_vector.h"
@@ -13,14 +15,26 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_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>
+#else
+#include <alloca.h>
+#endif
+
 // STACK_SAMPLING_PROFILER_SUPPORTED is used to conditionally enable the tests
 // below for supported platforms (currently Win x64).
 #if defined(_WIN64)
 #define STACK_SAMPLING_PROFILER_SUPPORTED 1
 #endif
 
+#if defined(OS_WIN)
+#pragma intrinsic(_ReturnAddress)
+#endif
+
 namespace base {
 
 using SamplingParams = StackSamplingProfiler::SamplingParams;
@@ -32,11 +46,19 @@
 
 namespace {
 
+// Configuration for whether to allocate dynamic stack memory.
+enum DynamicStackAllocationConfig { USE_ALLOCA, NO_ALLOCA };
+
+// 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*);
+
 // 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:
-  TargetThread();
+  TargetThread(DynamicStackAllocationConfig allocation_config);
 
   // PlatformThread::Delegate:
   void ThreadMain() override;
@@ -50,30 +72,53 @@
   void SignalThreadToFinish();
 
   // This function is guaranteed to be executing between calls to
-  // WaitForThreadStart() and SignalThreadToFinish(). 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 void SignalAndWaitUntilSignaled(WaitableEvent* thread_started_event,
-                                         WaitableEvent* finish_event);
+  // 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);
+
+  // Works like SignalAndWaitUntilSignaled() but additionally allocates memory
+  // on the stack with alloca. Note that this must be a separate function from
+  // SignalAndWaitUntilSignaled because on Windows x64 the compiler sets up
+  // dynamic frame handling whenever alloca appears in a function, even if only
+  // conditionally invoked.
+  static const void* SignalAndWaitUntilSignaledWithAlloca(
+      WaitableEvent* thread_started_event,
+      WaitableEvent* finish_event);
 
   PlatformThreadId id() const { return id_; }
 
  private:
+  // 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 DynamicStackAllocationConfig allocation_config_;
 
   DISALLOW_COPY_AND_ASSIGN(TargetThread);
 };
 
-TargetThread::TargetThread()
+TargetThread::TargetThread(DynamicStackAllocationConfig allocation_config)
     : thread_started_event_(false, false), finish_event_(false, false),
-      id_(0) {}
+      id_(0), allocation_config_(allocation_config) {}
 
 void TargetThread::ThreadMain() {
   id_ = PlatformThread::CurrentId();
-  SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_);
+  if (allocation_config_ == USE_ALLOCA) {
+    SignalAndWaitUntilSignaledWithAlloca(&thread_started_event_,
+                                         &finish_event_);
+  } else {
+    SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_);
+  }
 }
 
 void TargetThread::WaitForThreadStart() {
@@ -86,16 +131,49 @@
 
 // static
 // Disable inlining for this function so that it gets its own stack frame.
-NOINLINE void TargetThread::SignalAndWaitUntilSignaled(
+NOINLINE const void* TargetThread::SignalAndWaitUntilSignaled(
     WaitableEvent* thread_started_event,
     WaitableEvent* finish_event) {
-  thread_started_event->Signal();
-  volatile int x = 1;
-  finish_event->Wait();
-  x = 0;  // Prevent tail call to WaitableEvent::Wait().
-  ALLOW_UNUSED_LOCAL(x);
+  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::SignalAndWaitUntilSignaledWithAlloca(
+    WaitableEvent* thread_started_event,
+    WaitableEvent* finish_event) {
+  const size_t alloca_size = 100;
+  // Memset to 0 to generate a clean failure.
+  std::memset(alloca(alloca_size), 0, alloca_size);
+
+  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::GetProgramCounter() {
+#if defined(OS_WIN)
+  return _ReturnAddress();
+#else
+  return __builtin_return_address(0);
+#endif
+}
+
+
 // Called on the profiler thread when complete, to collect profiles.
 void SaveProfiles(CallStackProfiles* profiles,
                   const CallStackProfiles& pending_profiles) {
@@ -113,11 +191,13 @@
 }
 
 // Executes the function with the target thread running and executing within
-// SignalAndWaitUntilSignaled(). Performs all necessary target thread startup
-// and shutdown work before and afterward.
+// SignalAndWaitUntilSignaled() or SignalAndWaitUntilSignaledWithAlloca(),
+// depending on the value of |allocation_config|. Performs all necessary target
+// thread startup and shutdown work before and afterward.
 template <class Function>
-void WithTargetThread(Function function) {
-  TargetThread target_thread;
+void WithTargetThread(Function function,
+                      DynamicStackAllocationConfig allocation_config) {
+  TargetThread target_thread(allocation_config);
   PlatformThreadHandle target_thread_handle;
   EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
 
@@ -130,6 +210,11 @@
   PlatformThread::Join(target_thread_handle);
 }
 
+template <class Function>
+void WithTargetThread(Function function) {
+  WithTargetThread(function, NO_ALLOCA);
+}
+
 // Captures profiles as specified by |params| on the TargetThread, and returns
 // them in |profiles|. Waits up to |profiler_wait_time| for the profiler to
 // complete.
@@ -174,19 +259,19 @@
 }
 
 // Searches through the frames in |sample|, returning an iterator to the first
-// frame that has an instruction pointer between |function_address| and
-// |function_address| + |size|. Returns sample.end() if no such frames are
-// found.
+// frame that has an instruction pointer within |target_function|. Returns
+// sample.end() if no such frames are found.
 Sample::const_iterator FindFirstFrameWithinFunction(
     const Sample& sample,
-    const void* function_address,
-    int function_size) {
-  function_address = MaybeFixupFunctionAddressForILT(function_address);
+    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));
   for (auto it = sample.begin(); it != sample.end(); ++it) {
-    if ((reinterpret_cast<const void*>(it->instruction_pointer) >=
-         function_address) &&
-        (reinterpret_cast<const void*>(it->instruction_pointer) <
-         (static_cast<const unsigned char*>(function_address) + function_size)))
+    if ((it->instruction_pointer >= function_start) &&
+        (it->instruction_pointer <= function_end))
       return it;
   }
   return sample.end();
@@ -241,18 +326,13 @@
   // Check that the stack contains a frame for
   // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this
   // executable's module.
-  //
-  // Since we don't have a good way to know the function size, use 100 bytes as
-  // a reasonable window to locate the instruction pointer.
   Sample::const_iterator loc = FindFirstFrameWithinFunction(
       sample,
-      reinterpret_cast<const void*>(&TargetThread::SignalAndWaitUntilSignaled),
-      100);
+      &TargetThread::SignalAndWaitUntilSignaled);
   ASSERT_TRUE(loc != sample.end())
       << "Function at "
-      << MaybeFixupFunctionAddressForILT(
-          reinterpret_cast<const void*>(
-              &TargetThread::SignalAndWaitUntilSignaled))
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+          &TargetThread::SignalAndWaitUntilSignaled))
       << " was not found in stack:\n"
       << FormatSampleForDiagnosticOutput(sample, profile.modules);
   FilePath executable_path;
@@ -260,6 +340,49 @@
   EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename);
 }
 
+// Checks that the profiler handles stacks containing dynamically-allocated
+// stack memory.
+#if defined(STACK_SAMPLING_PROFILER_SUPPORTED)
+#define MAYBE_Alloca Alloca
+#else
+#define MAYBE_Alloca DISABLED_Alloca
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_Alloca) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_burst = 1;
+
+  std::vector<CallStackProfile> profiles;
+  WithTargetThread([&params, &profiles](
+      PlatformThreadId target_thread_id) {
+    WaitableEvent sampling_thread_completed(true, false);
+    const StackSamplingProfiler::CompletedCallback callback =
+        Bind(&SaveProfilesAndSignalEvent, Unretained(&profiles),
+             Unretained(&sampling_thread_completed));
+    StackSamplingProfiler profiler(target_thread_id, params, callback);
+    profiler.Start();
+    sampling_thread_completed.Wait();
+  }, USE_ALLOCA);
+
+  // Look up the sample.
+  ASSERT_EQ(1u, profiles.size());
+  const CallStackProfile& profile = profiles[0];
+  ASSERT_EQ(1u, profile.samples.size());
+  const Sample& sample = profile.samples[0];
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaledWithAlloca().
+  Sample::const_iterator loc = FindFirstFrameWithinFunction(
+      sample,
+      &TargetThread::SignalAndWaitUntilSignaledWithAlloca);
+  ASSERT_TRUE(loc != sample.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+          &TargetThread::SignalAndWaitUntilSignaledWithAlloca))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(sample, profile.modules);
+}
+
 // Checks that the fire-and-forget interface works.
 #if defined(STACK_SAMPLING_PROFILER_SUPPORTED)
 #define MAYBE_StartAndRunAsync StartAndRunAsync
diff --git a/base/profiler/win32_stack_frame_unwinder.cc b/base/profiler/win32_stack_frame_unwinder.cc
index 4ab3a3e..3bdd288 100644
--- a/base/profiler/win32_stack_frame_unwinder.cc
+++ b/base/profiler/win32_stack_frame_unwinder.cc
@@ -10,10 +10,61 @@
 
 namespace base {
 
-// LeafUnwindBlacklist --------------------------------------------------------
+// Win32UnwindFunctions -------------------------------------------------------
 
 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;
+
+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
+}
+
+// LeafUnwindBlacklist --------------------------------------------------------
+
 // Records modules that are known to have functions that violate the Microsoft
 // x64 calling convention and would be dangerous to manually unwind if
 // encountered as the last frame on the call stack. Functions like these have
@@ -26,13 +77,11 @@
  public:
   static LeafUnwindBlacklist* GetInstance();
 
-  // This function does not allocate memory and is safe to call between
-  // SuspendThread and ResumeThread.
+  // Returns true if |module| has been blacklisted.
   bool IsBlacklisted(const void* module) const;
 
-  // Allocates memory. Must be invoked only after ResumeThread, otherwise we
-  // risk deadlocking on a heap lock held by a suspended thread.
-  void AddModuleToBlacklist(const void* module);
+  // Records |module| for blacklisting.
+  void BlacklistModule(const void* module);
 
  private:
   friend struct DefaultSingletonTraits<LeafUnwindBlacklist>;
@@ -49,7 +98,7 @@
 
 // static
 LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() {
-  // Leaky for shutdown performance.
+  // Leaky for performance reasons.
   return Singleton<LeafUnwindBlacklist,
                    LeakySingletonTraits<LeafUnwindBlacklist>>::get();
 }
@@ -58,7 +107,7 @@
   return ContainsKey(blacklisted_modules_, module);
 }
 
-void LeafUnwindBlacklist::AddModuleToBlacklist(const void* module) {
+void LeafUnwindBlacklist::BlacklistModule(const void* module) {
   CHECK(module);
   blacklisted_modules_.insert(module);
 }
@@ -73,50 +122,15 @@
 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
 
-Win32StackFrameUnwinder::Win32UnwindFunctions::Win32UnwindFunctions() {}
-
-PRUNTIME_FUNCTION Win32StackFrameUnwinder::Win32UnwindFunctions::
-LookupFunctionEntry(DWORD64 program_counter, PDWORD64 image_base) {
-#ifdef _WIN64
-  return RtlLookupFunctionEntry(program_counter, image_base, nullptr);
-#else
-  NOTREACHED();
-  return nullptr;
-#endif
-}
-
-void Win32StackFrameUnwinder::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(0, image_base, program_counter, runtime_function,
-                   context, &handler_data, &establisher_frame, &nvcontext);
-#else
-  NOTREACHED();
-#endif
-}
-
-
 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
-    : Win32StackFrameUnwinder(&win32_unwind_functions_) {
+    : Win32StackFrameUnwinder(make_scoped_ptr(new Win32UnwindFunctions)) {
 }
 
-Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {
-  if (pending_blacklisted_module_) {
-    LeafUnwindBlacklist::GetInstance()->AddModuleToBlacklist(
-        pending_blacklisted_module_);
-  }
-}
+Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
 
 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context) {
 #ifdef _WIN64
   CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_);
-  CHECK(!pending_blacklisted_module_);
 
   ULONG64 image_base;
   // Try to look up unwind metadata for the current function.
@@ -179,8 +193,8 @@
       if (unwind_info_present_for_all_frames_) {
         // Unwind information was present for all previous frames, so we can
         // be confident this is case 2. Record the module to be blacklisted.
-        pending_blacklisted_module_ =
-            reinterpret_cast<const void *>(image_base);
+        LeafUnwindBlacklist::GetInstance()->BlacklistModule(
+            reinterpret_cast<const void *>(image_base));
       } else {
         // We started off on a function without unwind information. It's very
         // likely that all frames up to this point have been good, and this
@@ -202,11 +216,10 @@
 }
 
 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
-    UnwindFunctions* unwind_functions)
+    scoped_ptr<UnwindFunctions> unwind_functions)
     : at_top_frame_(true),
       unwind_info_present_for_all_frames_(true),
-      pending_blacklisted_module_(nullptr),
-      unwind_functions_(unwind_functions) {
+      unwind_functions_(unwind_functions.Pass()) {
 }
 
 }  // namespace base
diff --git a/base/profiler/win32_stack_frame_unwinder.h b/base/profiler/win32_stack_frame_unwinder.h
index a45d577..4ddf76e 100644
--- a/base/profiler/win32_stack_frame_unwinder.h
+++ b/base/profiler/win32_stack_frame_unwinder.h
@@ -9,6 +9,7 @@
 
 #include "base/base_export.h"
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 
 namespace base {
 
@@ -19,7 +20,8 @@
 #endif  // !defined(_WIN64)
 
 // Instances of this class are expected to be created and destroyed for each
-// stack unwinding, outside of SuspendThread/ResumeThread.
+// 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
@@ -41,39 +43,21 @@
     DISALLOW_COPY_AND_ASSIGN(UnwindFunctions);
   };
 
-  class BASE_EXPORT Win32UnwindFunctions : public UnwindFunctions {
-   public:
-    Win32UnwindFunctions();
-
-    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;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions);
-  };
-
   Win32StackFrameUnwinder();
   ~Win32StackFrameUnwinder();
 
   bool TryUnwind(CONTEXT* context);
 
  private:
-  // This function is for test purposes only.
-  Win32StackFrameUnwinder(UnwindFunctions* unwind_functions);
+  // This function is for internal and test purposes only.
+  Win32StackFrameUnwinder(scoped_ptr<UnwindFunctions> unwind_functions);
   friend class Win32StackFrameUnwinderTest;
 
   // State associated with each stack unwinding.
   bool at_top_frame_;
   bool unwind_info_present_for_all_frames_;
-  const void* pending_blacklisted_module_;
 
-  Win32UnwindFunctions win32_unwind_functions_;
-  UnwindFunctions* const unwind_functions_;
+  scoped_ptr<UnwindFunctions> unwind_functions_;
 
   DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinder);
 };
diff --git a/base/profiler/win32_stack_frame_unwinder_unittest.cc b/base/profiler/win32_stack_frame_unwinder_unittest.cc
index a273793..c9d1c42 100644
--- a/base/profiler/win32_stack_frame_unwinder_unittest.cc
+++ b/base/profiler/win32_stack_frame_unwinder_unittest.cc
@@ -97,7 +97,8 @@
   // with a single friend declaration of this test fixture.
   scoped_ptr<Win32StackFrameUnwinder> CreateUnwinder();
 
-  TestUnwindFunctions unwind_functions_;
+  // Weak pointer to the unwind functions used by last created unwinder.
+  TestUnwindFunctions* unwind_functions_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest);
@@ -105,7 +106,9 @@
 
 scoped_ptr<Win32StackFrameUnwinder>
 Win32StackFrameUnwinderTest::CreateUnwinder() {
-  return make_scoped_ptr(new Win32StackFrameUnwinder(&unwind_functions_));
+  scoped_ptr<TestUnwindFunctions> unwind_functions(new TestUnwindFunctions);
+  unwind_functions_ = unwind_functions.get();
+  return make_scoped_ptr(new Win32StackFrameUnwinder(unwind_functions.Pass()));
 }
 
 // Checks the case where all frames have unwind information.
@@ -124,7 +127,7 @@
   CONTEXT context = {0};
   const DWORD64 original_rsp = 128;
   context.Rsp = original_rsp;
-  unwind_functions_.SetNoUnwindInfoForNextFrame();
+  unwind_functions_->SetNoUnwindInfoForNextFrame();
   EXPECT_TRUE(unwinder->TryUnwind(&context));
   EXPECT_EQ(original_rsp, context.Rip);
   EXPECT_EQ(original_rsp + 8, context.Rsp);
@@ -143,8 +146,8 @@
     CONTEXT context = {0};
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_module_with_bad_function);
     EXPECT_FALSE(unwinder->TryUnwind(&context));
   }
@@ -154,8 +157,8 @@
     // unwind info from the previously-seen module is blacklisted.
     scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
     CONTEXT context = {0};
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_module_with_bad_function);
     EXPECT_FALSE(unwinder->TryUnwind(&context));
   }
@@ -168,13 +171,13 @@
     // module.
     scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
     CONTEXT context = {0};
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_module_with_bad_function);
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_module_with_bad_function);
     EXPECT_TRUE(unwinder->TryUnwind(&context));
   }
@@ -187,12 +190,12 @@
     // previously-seen module.
     scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
     CONTEXT context = {0};
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_module_with_bad_function);
     EXPECT_TRUE(unwinder->TryUnwind(&context));
   }
@@ -208,11 +211,11 @@
     // First stack, with both the first and second frames missing unwind info.
     scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
     CONTEXT context = {0};
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
     EXPECT_TRUE(unwinder->TryUnwind(&context));
 
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_questionable_module);
     EXPECT_FALSE(unwinder->TryUnwind(&context));
   }
@@ -221,8 +224,8 @@
     // Second stack; check that the questionable module was not blacklisted.
     scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
     CONTEXT context = {0};
-    unwind_functions_.SetNoUnwindInfoForNextFrame();
-    unwind_functions_.SetImageBaseForNextFrame(
+    unwind_functions_->SetNoUnwindInfoForNextFrame();
+    unwind_functions_->SetImageBaseForNextFrame(
         image_base_for_questionable_module);
     EXPECT_TRUE(unwinder->TryUnwind(&context));
   }
diff --git a/base/time/time.h b/base/time/time.h
index e0a6ea3..e0435d0 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -685,12 +685,14 @@
   static TimeTicks FromQPCValue(LONGLONG qpc_value);
 #endif
 
-  // Get the TimeTick value at the time of the UnixEpoch. This is useful when
-  // you need to relate the value of TimeTicks to a real time and date.
-  // Note: Upon first invocation, this function takes a snapshot of the realtime
-  // clock to establish a reference point.  This function will return the same
-  // value for the duration of the application, but will be different in future
-  // application runs.
+  // Get an estimate of the TimeTick value at the time of the UnixEpoch. Because
+  // Time and TimeTicks respond differently to user-set time and NTP
+  // adjustments, this number is only an estimate. Nevertheless, this can be
+  // useful when you need to relate the value of TimeTicks to a real time and
+  // date. Note: Upon first invocation, this function takes a snapshot of the
+  // realtime clock to establish a reference point.  This function will return
+  // the same value for the duration of the application, but will be different
+  // in future application runs.
   static TimeTicks UnixEpoch();
 
   // Returns |this| snapped to the next tick, given a |tick_phase| and
diff --git a/blimp/BUILD.gn b/blimp/BUILD.gn
index 13b8e29e..8c71748 100644
--- a/blimp/BUILD.gn
+++ b/blimp/BUILD.gn
@@ -3,9 +3,6 @@
 # found in the LICENSE file.
 
 group("blimp") {
-  # TODO(maniscalco): Remove the testonly attribute once blimp_engine no longer
-  # depends on content_shell (crbug.com/538353).
-  testonly = true
   deps = [
     "//blimp/client:blimp_client",
     "//blimp/common:blimp_common",
diff --git a/blimp/engine/BUILD.gn b/blimp/engine/BUILD.gn
index 849b3cee..ecc1f61 100644
--- a/blimp/engine/BUILD.gn
+++ b/blimp/engine/BUILD.gn
@@ -2,16 +2,65 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/features.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build/config/ui.gni")
+import("//tools/grit/repack.gni")
+import("//tools/grit/grit_rule.gni")
+
+repack("pak") {
+  sources = [
+    "$root_gen_dir/blink/public/resources/blink_image_resources_100_percent.pak",
+    "$root_gen_dir/blink/public/resources/blink_resources.pak",
+    "$root_gen_dir/content/app/resources/content_resources_100_percent.pak",
+    "$root_gen_dir/content/app/strings/content_strings_en-US.pak",
+    "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
+    "$root_gen_dir/content/content_resources.pak",
+    "$root_gen_dir/net/net_resources.pak",
+    "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
+    "$root_gen_dir/ui/resources/webui_resources.pak",
+    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
+    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+  ]
+
+  deps = [
+    "//content:resources",
+    "//content/app/resources",
+    "//content/app/strings",
+    "//content/browser/tracing:resources",
+    "//net:net_resources",
+    "//third_party/WebKit/public:image_resources",
+    "//third_party/WebKit/public:resources",
+    "//ui/resources",
+    "//ui/strings",
+  ]
+
+  if (toolkit_views) {
+    deps += [ "//ui/views/resources" ]
+    sources +=
+        [ "$root_gen_dir/ui/views/resources/views_resources_100_percent.pak" ]
+  }
+
+  output = "$root_out_dir/blimp_engine.pak"
+}
+
 if (is_linux) {
+  executable("blimp_engine_app") {
+    sources = [
+      "app/blimp_main.cc",
+    ]
+
+    deps = [
+      ":pak",
+      "//blimp/engine/app",
+    ]
+  }
+
   # A stand-in for the Blimp Engine so we can get the Docker packaging working.
-  #
-  # TODO(maniscalco): Update to use the real engine once it's ready and remove
-  # the testonly attribute (crbug.com/538353).
   group("blimp_engine") {
-    testonly = true
     data_deps = [
-      "//content/shell:content_shell",
-      "//content/shell:pak",
+      ":blimp_engine_app",
+      ":pak",
     ]
   }
 }
diff --git a/blimp/engine/DEPS b/blimp/engine/DEPS
new file mode 100644
index 0000000..0d8bb180
--- /dev/null
+++ b/blimp/engine/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "+base",
+  "-chrome",
+  "+components/web_cache/renderer",
+  "+content/public",
+  "+net",
+  "+ui/base",
+  "+ui/resources",
+  "+ui/gfx",
+]
diff --git a/blimp/engine/app/BUILD.gn b/blimp/engine/app/BUILD.gn
new file mode 100644
index 0000000..48fb0257
--- /dev/null
+++ b/blimp/engine/app/BUILD.gn
@@ -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.
+
+source_set("app") {
+  sources = [
+    "blimp_content_main_delegate.cc",
+    "blimp_content_main_delegate.h",
+  ]
+
+  deps = [
+    "//base",
+    "//blimp/engine/browser",
+    "//blimp/engine/common",
+    "//blimp/engine/renderer",
+    "//content/public/app:both",
+    "//ui/base",
+  ]
+}
diff --git a/blimp/engine/app/blimp_content_main_delegate.cc b/blimp/engine/app/blimp_content_main_delegate.cc
new file mode 100644
index 0000000..a3efeffe
--- /dev/null
+++ b/blimp/engine/app/blimp_content_main_delegate.cc
@@ -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.
+
+#include "blimp/engine/app/blimp_content_main_delegate.h"
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "blimp/engine/browser/blimp_content_browser_client.h"
+#include "blimp/engine/renderer/blimp_content_renderer_client.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace blimp {
+namespace engine {
+
+BlimpContentMainDelegate::BlimpContentMainDelegate() {}
+
+BlimpContentMainDelegate::~BlimpContentMainDelegate() {}
+
+bool BlimpContentMainDelegate::BasicStartupComplete(int* exit_code) {
+  content::SetContentClient(&content_client_);
+  return false;
+}
+
+void BlimpContentMainDelegate::PreSandboxStartup() {
+  InitializeResourceBundle();
+}
+
+void BlimpContentMainDelegate::InitializeResourceBundle() {
+  base::FilePath pak_file;
+  bool pak_file_valid = PathService::Get(base::DIR_MODULE, &pak_file);
+  CHECK(pak_file_valid);
+  pak_file = pak_file.Append(FILE_PATH_LITERAL("blimp_engine.pak"));
+  ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file);
+}
+
+content::ContentBrowserClient*
+BlimpContentMainDelegate::CreateContentBrowserClient() {
+  DCHECK(!browser_client_);
+  browser_client_.reset(new BlimpContentBrowserClient);
+  return browser_client_.get();
+}
+
+content::ContentRendererClient*
+BlimpContentMainDelegate::CreateContentRendererClient() {
+  DCHECK(!renderer_client_);
+  renderer_client_.reset(new BlimpContentRendererClient);
+  return renderer_client_.get();
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/app/blimp_content_main_delegate.h b/blimp/engine/app/blimp_content_main_delegate.h
new file mode 100644
index 0000000..24aa4c3d
--- /dev/null
+++ b/blimp/engine/app/blimp_content_main_delegate.h
@@ -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.
+
+#ifndef BLIMP_ENGINE_APP_BLIMP_CONTENT_MAIN_DELEGATE_H_
+#define BLIMP_ENGINE_APP_BLIMP_CONTENT_MAIN_DELEGATE_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "blimp/engine/common/blimp_content_client.h"
+#include "content/public/app/content_main_delegate.h"
+
+namespace blimp {
+namespace engine {
+
+class BlimpContentBrowserClient;
+class BlimpContentRendererClient;
+
+class BlimpContentMainDelegate : public content::ContentMainDelegate {
+ public:
+  BlimpContentMainDelegate();
+  ~BlimpContentMainDelegate() override;
+
+  // content::ContentMainDelegate implementation.
+  bool BasicStartupComplete(int* exit_code) override;
+  void PreSandboxStartup() override;
+  content::ContentBrowserClient* CreateContentBrowserClient() override;
+  content::ContentRendererClient* CreateContentRendererClient() override;
+
+ private:
+  void InitializeResourceBundle();
+
+  scoped_ptr<BlimpContentBrowserClient> browser_client_;
+  scoped_ptr<BlimpContentRendererClient> renderer_client_;
+  BlimpContentClient content_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpContentMainDelegate);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_APP_BLIMP_CONTENT_MAIN_DELEGATE_H_
diff --git a/blimp/engine/app/blimp_main.cc b/blimp/engine/app/blimp_main.cc
new file mode 100644
index 0000000..64e3e53
--- /dev/null
+++ b/blimp/engine/app/blimp_main.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 "blimp/engine/app/blimp_content_main_delegate.h"
+#include "content/public/app/content_main.h"
+
+int main(int argc, const char** argv) {
+  blimp::engine::BlimpContentMainDelegate delegate;
+  content::ContentMainParams params(&delegate);
+  params.argc = argc;
+  params.argv = argv;
+  return content::ContentMain(params);
+}
diff --git a/blimp/engine/browser/BUILD.gn b/blimp/engine/browser/BUILD.gn
new file mode 100644
index 0000000..dc7ac4ae
--- /dev/null
+++ b/blimp/engine/browser/BUILD.gn
@@ -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.
+
+source_set("browser") {
+  sources = [
+    "blimp_browser_context.cc",
+    "blimp_browser_context.h",
+    "blimp_browser_main_parts.cc",
+    "blimp_browser_main_parts.h",
+    "blimp_content_browser_client.cc",
+    "blimp_content_browser_client.h",
+    "blimp_network_delegate.cc",
+    "blimp_network_delegate.h",
+    "blimp_permission_manager.cc",
+    "blimp_permission_manager.h",
+    "blimp_url_request_context_getter.cc",
+    "blimp_url_request_context_getter.h",
+    "blimp_window.cc",
+    "blimp_window.h",
+  ]
+
+  deps = [
+    "//base",
+    "//blimp/engine/ui",
+    "//content",
+    "//content/public/browser",
+    "//content/public/common",
+    "//content/public/utility",
+    "//net",
+    "//ui/base",
+    "//ui/resources",
+  ]
+}
diff --git a/blimp/engine/browser/blimp_browser_context.cc b/blimp/engine/browser/blimp_browser_context.cc
new file mode 100644
index 0000000..caee89b0
--- /dev/null
+++ b/blimp/engine/browser/blimp_browser_context.cc
@@ -0,0 +1,176 @@
+// 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 "blimp/engine/browser/blimp_browser_context.h"
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_util.h"
+#include "base/nix/xdg_util.h"
+#include "base/path_service.h"
+#include "blimp/engine/browser/blimp_permission_manager.h"
+#include "content/public/browser/background_sync_controller.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_switches.h"
+
+namespace blimp {
+namespace engine {
+
+// Contains URLRequestContextGetter required for resource loading.
+class BlimpResourceContext : public content::ResourceContext {
+ public:
+  BlimpResourceContext() {}
+  ~BlimpResourceContext() override {}
+
+  void set_url_request_context_getter(
+      const scoped_refptr<BlimpURLRequestContextGetter>& getter) {
+    getter_ = getter;
+  }
+
+  const scoped_refptr<BlimpURLRequestContextGetter>&
+  url_request_context_getter() {
+    return getter_;
+  }
+
+  // content::ResourceContext implementation.
+  net::HostResolver* GetHostResolver() override;
+  net::URLRequestContext* GetRequestContext() override;
+
+ private:
+  scoped_refptr<BlimpURLRequestContextGetter> getter_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpResourceContext);
+};
+
+net::HostResolver* BlimpResourceContext::GetHostResolver() {
+  return getter_->host_resolver();
+}
+
+net::URLRequestContext* BlimpResourceContext::GetRequestContext() {
+  return getter_->GetURLRequestContext();
+}
+
+BlimpBrowserContext::BlimpBrowserContext(bool off_the_record,
+                                         net::NetLog* net_log)
+    : resource_context_(new BlimpResourceContext),
+      ignore_certificate_errors_(false),
+      off_the_record_(off_the_record),
+      net_log_(net_log) {
+  InitWhileIOAllowed();
+}
+
+BlimpBrowserContext::~BlimpBrowserContext() {
+  if (resource_context_) {
+    content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE,
+                                       resource_context_.release());
+  }
+}
+
+void BlimpBrowserContext::InitWhileIOAllowed() {
+  // Ensures ~/.config/blimp_engine directory exists.
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  base::FilePath config_dir(base::nix::GetXDGDirectory(
+      env.get(), base::nix::kXdgConfigHomeEnvVar, base::nix::kDotConfigDir));
+  path_ = config_dir.Append("blimp_engine");
+  if (!base::PathExists(path_))
+    base::CreateDirectory(path_);
+}
+
+scoped_ptr<content::ZoomLevelDelegate>
+BlimpBrowserContext::CreateZoomLevelDelegate(const base::FilePath&) {
+  return nullptr;
+}
+
+base::FilePath BlimpBrowserContext::GetPath() const {
+  return path_;
+}
+
+bool BlimpBrowserContext::IsOffTheRecord() const {
+  return off_the_record_;
+}
+
+content::DownloadManagerDelegate*
+BlimpBrowserContext::GetDownloadManagerDelegate() {
+  return nullptr;
+}
+
+net::URLRequestContextGetter* BlimpBrowserContext::GetRequestContext() {
+  return GetDefaultStoragePartition(this)->GetURLRequestContext();
+}
+
+const scoped_refptr<BlimpURLRequestContextGetter>&
+BlimpBrowserContext::CreateRequestContext(
+    content::ProtocolHandlerMap* protocol_handlers,
+    content::URLRequestInterceptorScopedVector request_interceptors) {
+  DCHECK(!resource_context_->url_request_context_getter());
+  // net_log_ is owned by BrowserMainParts.
+  resource_context_->set_url_request_context_getter(
+      new BlimpURLRequestContextGetter(
+          ignore_certificate_errors_, GetPath(),
+          content::BrowserThread::UnsafeGetMessageLoopForThread(
+              content::BrowserThread::IO)
+              ->task_runner(),
+          content::BrowserThread::UnsafeGetMessageLoopForThread(
+              content::BrowserThread::FILE)
+              ->task_runner(),
+          protocol_handlers, request_interceptors.Pass(), net_log_));
+  return resource_context_->url_request_context_getter();
+}
+
+net::URLRequestContextGetter*
+BlimpBrowserContext::GetRequestContextForRenderProcess(int renderer_child_id) {
+  return GetRequestContext();
+}
+
+net::URLRequestContextGetter* BlimpBrowserContext::GetMediaRequestContext() {
+  return GetRequestContext();
+}
+
+net::URLRequestContextGetter*
+BlimpBrowserContext::GetMediaRequestContextForRenderProcess(
+    int renderer_child_id) {
+  return GetRequestContext();
+}
+
+net::URLRequestContextGetter*
+BlimpBrowserContext::GetMediaRequestContextForStoragePartition(
+    const base::FilePath& partition_path,
+    bool in_memory) {
+  return GetRequestContext();
+}
+
+content::ResourceContext* BlimpBrowserContext::GetResourceContext() {
+  return resource_context_.get();
+}
+
+content::BrowserPluginGuestManager* BlimpBrowserContext::GetGuestManager() {
+  return nullptr;
+}
+
+storage::SpecialStoragePolicy* BlimpBrowserContext::GetSpecialStoragePolicy() {
+  return nullptr;
+}
+
+content::PushMessagingService* BlimpBrowserContext::GetPushMessagingService() {
+  return nullptr;
+}
+
+content::SSLHostStateDelegate* BlimpBrowserContext::GetSSLHostStateDelegate() {
+  return nullptr;
+}
+
+content::PermissionManager* BlimpBrowserContext::GetPermissionManager() {
+  if (!permission_manager_)
+    permission_manager_.reset(new BlimpPermissionManager());
+  return permission_manager_.get();
+}
+
+content::BackgroundSyncController*
+BlimpBrowserContext::GetBackgroundSyncController() {
+  return nullptr;
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_browser_context.h b/blimp/engine/browser/blimp_browser_context.h
new file mode 100644
index 0000000..558e3eb
--- /dev/null
+++ b/blimp/engine/browser/blimp_browser_context.h
@@ -0,0 +1,80 @@
+// 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 BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_CONTEXT_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_CONTEXT_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "blimp/engine/browser/blimp_url_request_context_getter.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/resource_context.h"
+#include "net/url_request/url_request_job_factory.h"
+
+namespace net {
+class NetLog;
+}
+
+namespace blimp {
+namespace engine {
+
+class BlimpResourceContext;
+class PermissionManager;
+
+class BlimpBrowserContext : public content::BrowserContext {
+ public:
+  // Caller owns |net_log| and ensures it out-lives this browser context.
+  BlimpBrowserContext(bool off_the_record, net::NetLog* net_log);
+  ~BlimpBrowserContext() override;
+
+  // content::BrowserContext implementation.
+  scoped_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate(
+      const base::FilePath& partition_path) override;
+  base::FilePath GetPath() const override;
+  bool IsOffTheRecord() const override;
+  net::URLRequestContextGetter* GetRequestContext() override;
+  net::URLRequestContextGetter* GetRequestContextForRenderProcess(
+      int renderer_child_id) override;
+  net::URLRequestContextGetter* GetMediaRequestContext() override;
+  net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess(
+      int renderer_child_id) override;
+  net::URLRequestContextGetter* GetMediaRequestContextForStoragePartition(
+      const base::FilePath& partition_path,
+      bool in_memory) override;
+  content::ResourceContext* GetResourceContext() override;
+  content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
+  content::BrowserPluginGuestManager* GetGuestManager() override;
+  storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override;
+  content::PushMessagingService* GetPushMessagingService() override;
+  content::SSLHostStateDelegate* GetSSLHostStateDelegate() override;
+  content::PermissionManager* GetPermissionManager() override;
+  content::BackgroundSyncController* GetBackgroundSyncController() override;
+
+  // The content of |protocol_handlers| is swapped into the returned instance.
+  // Caller should take a reference to the returned instance via scoped_refptr.
+  const scoped_refptr<BlimpURLRequestContextGetter>& CreateRequestContext(
+      content::ProtocolHandlerMap* protocol_handlers,
+      content::URLRequestInterceptorScopedVector request_interceptors);
+
+ private:
+  // Performs initialization of the BlimpBrowserContext while IO is still
+  // allowed on the current thread.
+  void InitWhileIOAllowed();
+
+  scoped_ptr<BlimpResourceContext> resource_context_;
+  bool ignore_certificate_errors_;
+  scoped_ptr<content::PermissionManager> permission_manager_;
+  bool off_the_record_;
+  net::NetLog* net_log_;
+  base::FilePath path_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpBrowserContext);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_CONTEXT_H_
diff --git a/blimp/engine/browser/blimp_browser_main_parts.cc b/blimp/engine/browser/blimp_browser_main_parts.cc
new file mode 100644
index 0000000..a1c0474
--- /dev/null
+++ b/blimp/engine/browser/blimp_browser_main_parts.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 "blimp/engine/browser/blimp_browser_main_parts.h"
+
+#include "base/command_line.h"
+#include "blimp/engine/browser/blimp_window.h"
+#include "blimp/engine/ui/blimp_screen.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_module.h"
+#include "net/log/net_log.h"
+#include "url/gurl.h"
+
+namespace blimp {
+namespace engine {
+
+namespace {
+
+const char kDefaultURL[] = "https://www.google.com/";
+
+GURL GetStartupURL() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const base::CommandLine::StringVector& args = command_line->GetArgs();
+  if (args.empty())
+    return GURL(kDefaultURL);
+
+  GURL url(args[0]);
+  if (url.is_valid() && url.has_scheme())
+    return url;
+
+  return GURL(kDefaultURL);
+}
+
+}  // namespace
+
+BlimpBrowserMainParts::BlimpBrowserMainParts(
+    const content::MainFunctionParams& parameters) {}
+
+BlimpBrowserMainParts::~BlimpBrowserMainParts() {}
+
+void BlimpBrowserMainParts::PreMainMessageLoopRun() {
+  net_log_.reset(new net::NetLog());
+  browser_context_.reset(new BlimpBrowserContext(false, net_log_.get()));
+  BlimpWindow::Create(browser_context_.get(), GetStartupURL(), nullptr,
+                      gfx::Size());
+}
+
+int BlimpBrowserMainParts::PreCreateThreads() {
+  screen_.reset(new BlimpScreen);
+  DCHECK(!gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE));
+  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+  return 0;
+}
+
+void BlimpBrowserMainParts::PostMainMessageLoopRun() {
+  browser_context_.reset();
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_browser_main_parts.h b/blimp/engine/browser/blimp_browser_main_parts.h
new file mode 100644
index 0000000..5c2e991a
--- /dev/null
+++ b/blimp/engine/browser/blimp_browser_main_parts.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 BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_MAIN_PARTS_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_MAIN_PARTS_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "blimp/engine/browser/blimp_browser_context.h"
+#include "content/public/browser/browser_main_parts.h"
+
+namespace net {
+class NetLog;
+}
+
+namespace blimp {
+namespace engine {
+
+class BlimpScreen;
+
+class BlimpBrowserMainParts : public content::BrowserMainParts {
+ public:
+  explicit BlimpBrowserMainParts(const content::MainFunctionParams& parameters);
+  ~BlimpBrowserMainParts() override;
+
+  // content::BrowserMainParts implementation.
+  void PreMainMessageLoopRun() override;
+  void PostMainMessageLoopRun() override;
+  int PreCreateThreads() override;
+
+  BlimpBrowserContext* browser_context() { return browser_context_.get(); }
+
+ private:
+  scoped_ptr<net::NetLog> net_log_;
+  scoped_ptr<BlimpBrowserContext> browser_context_;
+  scoped_ptr<BlimpScreen> screen_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpBrowserMainParts);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_BROWSER_MAIN_PARTS_H_
diff --git a/blimp/engine/browser/blimp_content_browser_client.cc b/blimp/engine/browser/blimp_content_browser_client.cc
new file mode 100644
index 0000000..1023fbb
--- /dev/null
+++ b/blimp/engine/browser/blimp_content_browser_client.cc
@@ -0,0 +1,39 @@
+// 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 "blimp/engine/browser/blimp_content_browser_client.h"
+#include "blimp/engine/browser/blimp_browser_context.h"
+#include "blimp/engine/browser/blimp_browser_main_parts.h"
+
+namespace blimp {
+namespace engine {
+
+BlimpContentBrowserClient::BlimpContentBrowserClient() {}
+
+BlimpContentBrowserClient::~BlimpContentBrowserClient() {}
+
+content::BrowserMainParts* BlimpContentBrowserClient::CreateBrowserMainParts(
+    const content::MainFunctionParams& parameters) {
+  blimp_browser_main_parts_ = new BlimpBrowserMainParts(parameters);
+  // BrowserMainLoop takes ownership of the returned BrowserMainParts.
+  return blimp_browser_main_parts_;
+}
+
+net::URLRequestContextGetter* BlimpContentBrowserClient::CreateRequestContext(
+    content::BrowserContext* content_browser_context,
+    content::ProtocolHandlerMap* protocol_handlers,
+    content::URLRequestInterceptorScopedVector request_interceptors) {
+  BlimpBrowserContext* blimp_context =
+      static_cast<BlimpBrowserContext*>(content_browser_context);
+  return blimp_context->CreateRequestContext(protocol_handlers,
+                                             request_interceptors.Pass())
+      .get();
+}
+
+BlimpBrowserContext* BlimpContentBrowserClient::browser_context() {
+  return blimp_browser_main_parts_->browser_context();
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_content_browser_client.h b/blimp/engine/browser/blimp_content_browser_client.h
new file mode 100644
index 0000000..aa566cdc
--- /dev/null
+++ b/blimp/engine/browser/blimp_content_browser_client.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef BLIMP_ENGINE_BROWSER_BLIMP_CONTENT_BROWSER_CLIENT_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_CONTENT_BROWSER_CLIENT_H_
+
+#include "base/macros.h"
+#include "content/public/browser/content_browser_client.h"
+
+namespace blimp {
+namespace engine {
+
+class BlimpBrowserMainParts;
+class BlimpBrowserContext;
+
+class BlimpContentBrowserClient : public content::ContentBrowserClient {
+ public:
+  BlimpContentBrowserClient();
+  ~BlimpContentBrowserClient() override;
+
+  // content::ContentBrowserClient implementation.
+  content::BrowserMainParts* CreateBrowserMainParts(
+      const content::MainFunctionParams& parameters) override;
+  net::URLRequestContextGetter* CreateRequestContext(
+      content::BrowserContext* browser_context,
+      content::ProtocolHandlerMap* protocol_handlers,
+      content::URLRequestInterceptorScopedVector request_interceptors) override;
+
+  BlimpBrowserContext* browser_context();
+
+ private:
+  // Owned by BrowserMainLoop
+  BlimpBrowserMainParts* blimp_browser_main_parts_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpContentBrowserClient);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_CONTENT_BROWSER_CLIENT_H_
diff --git a/blimp/engine/browser/blimp_network_delegate.cc b/blimp/engine/browser/blimp_network_delegate.cc
new file mode 100644
index 0000000..c22da118
--- /dev/null
+++ b/blimp/engine/browser/blimp_network_delegate.cc
@@ -0,0 +1,51 @@
+// 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 "blimp/engine/browser/blimp_network_delegate.h"
+
+#include "net/base/net_errors.h"
+#include "net/base/static_cookie_policy.h"
+#include "net/url_request/url_request.h"
+
+namespace blimp {
+namespace engine {
+
+namespace {
+bool g_accept_all_cookies = true;
+
+net::StaticCookiePolicy::Type GetPolicyType() {
+  return g_accept_all_cookies
+             ? net::StaticCookiePolicy::ALLOW_ALL_COOKIES
+             : net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES;
+}
+
+}  // namespace
+
+BlimpNetworkDelegate::BlimpNetworkDelegate() {}
+
+BlimpNetworkDelegate::~BlimpNetworkDelegate() {}
+
+void BlimpNetworkDelegate::SetAcceptAllCookies(bool accept) {
+  g_accept_all_cookies = accept;
+}
+
+bool BlimpNetworkDelegate::OnCanGetCookies(const net::URLRequest& request,
+                                           const net::CookieList& cookie_list) {
+  net::StaticCookiePolicy policy(GetPolicyType());
+  int rv =
+      policy.CanGetCookies(request.url(), request.first_party_for_cookies());
+  return rv == net::OK;
+}
+
+bool BlimpNetworkDelegate::OnCanSetCookie(const net::URLRequest& request,
+                                          const std::string& cookie_line,
+                                          net::CookieOptions* options) {
+  net::StaticCookiePolicy policy(GetPolicyType());
+  int rv =
+      policy.CanSetCookie(request.url(), request.first_party_for_cookies());
+  return rv == net::OK;
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_network_delegate.h b/blimp/engine/browser/blimp_network_delegate.h
new file mode 100644
index 0000000..e8b0e2c
--- /dev/null
+++ b/blimp/engine/browser/blimp_network_delegate.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 BLIMP_ENGINE_BROWSER_BLIMP_NETWORK_DELEGATE_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_NETWORK_DELEGATE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "net/base/network_delegate_impl.h"
+
+namespace blimp {
+namespace engine {
+
+class BlimpNetworkDelegate : public net::NetworkDelegateImpl {
+ public:
+  BlimpNetworkDelegate();
+  ~BlimpNetworkDelegate() override;
+
+  // Affects subsequent cookie retrieval and setting.
+  static void SetAcceptAllCookies(bool accept);
+
+ private:
+  // net::NetworkDelegate implementation.
+  bool OnCanGetCookies(const net::URLRequest& request,
+                       const net::CookieList& cookie_list) override;
+  bool OnCanSetCookie(const net::URLRequest& request,
+                      const std::string& cookie_line,
+                      net::CookieOptions* options) override;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpNetworkDelegate);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_NETWORK_DELEGATE_H_
diff --git a/blimp/engine/browser/blimp_permission_manager.cc b/blimp/engine/browser/blimp_permission_manager.cc
new file mode 100644
index 0000000..6cace1b
--- /dev/null
+++ b/blimp/engine/browser/blimp_permission_manager.cc
@@ -0,0 +1,58 @@
+// 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 "blimp/engine/browser/blimp_permission_manager.h"
+
+#include "base/callback.h"
+#include "content/public/browser/permission_type.h"
+
+namespace blimp {
+namespace engine {
+
+BlimpPermissionManager::BlimpPermissionManager()
+    : content::PermissionManager() {}
+
+BlimpPermissionManager::~BlimpPermissionManager() {}
+
+int BlimpPermissionManager::RequestPermission(
+    content::PermissionType permission,
+    content::RenderFrameHost* render_frame_host,
+    const GURL& origin,
+    bool user_gesture,
+    const base::Callback<void(content::PermissionStatus)>& callback) {
+  callback.Run(content::PermissionStatus::PERMISSION_STATUS_DENIED);
+  return kNoPendingOperation;
+}
+
+void BlimpPermissionManager::CancelPermissionRequest(int request_id) {}
+
+void BlimpPermissionManager::ResetPermission(content::PermissionType permission,
+                                             const GURL& requesting_origin,
+                                             const GURL& embedding_origin) {}
+
+content::PermissionStatus BlimpPermissionManager::GetPermissionStatus(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  return content::PermissionStatus::PERMISSION_STATUS_DENIED;
+}
+
+void BlimpPermissionManager::RegisterPermissionUsage(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {}
+
+int BlimpPermissionManager::SubscribePermissionStatusChange(
+    content::PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin,
+    const base::Callback<void(content::PermissionStatus)>& callback) {
+  return -1;
+}
+
+void BlimpPermissionManager::UnsubscribePermissionStatusChange(
+    int subscription_id) {}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_permission_manager.h b/blimp/engine/browser/blimp_permission_manager.h
new file mode 100644
index 0000000..b9f0eb8
--- /dev/null
+++ b/blimp/engine/browser/blimp_permission_manager.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 BLIMP_ENGINE_BROWSER_BLIMP_PERMISSION_MANAGER_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_PERMISSION_MANAGER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "content/public/browser/permission_manager.h"
+
+namespace blimp {
+namespace engine {
+
+class BlimpPermissionManager : public content::PermissionManager {
+ public:
+  BlimpPermissionManager();
+  ~BlimpPermissionManager() override;
+
+  // content::PermissionManager implementation:
+  int RequestPermission(
+      content::PermissionType permission,
+      content::RenderFrameHost* render_frame_host,
+      const GURL& requesting_origin,
+      bool user_gesture,
+      const base::Callback<void(content::PermissionStatus)>& callback) override;
+  void CancelPermissionRequest(int request_id) override;
+  void ResetPermission(content::PermissionType permission,
+                       const GURL& requesting_origin,
+                       const GURL& embedding_origin) override;
+  content::PermissionStatus GetPermissionStatus(
+      content::PermissionType permission,
+      const GURL& requesting_origin,
+      const GURL& embedding_origin) override;
+  void RegisterPermissionUsage(content::PermissionType permission,
+                               const GURL& requesting_origin,
+                               const GURL& embedding_origin) override;
+  int SubscribePermissionStatusChange(
+      content::PermissionType permission,
+      const GURL& requesting_origin,
+      const GURL& embedding_origin,
+      const base::Callback<void(content::PermissionStatus)>& callback) override;
+  void UnsubscribePermissionStatusChange(int subscription_id) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BlimpPermissionManager);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_PERMISSION_MANAGER_H_
diff --git a/blimp/engine/browser/blimp_url_request_context_getter.cc b/blimp/engine/browser/blimp_url_request_context_getter.cc
new file mode 100644
index 0000000..e49c5f2
--- /dev/null
+++ b/blimp/engine/browser/blimp_url_request_context_getter.cc
@@ -0,0 +1,224 @@
+// 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 "blimp/engine/browser/blimp_url_request_context_getter.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/worker_pool.h"
+#include "blimp/engine/browser/blimp_network_delegate.h"
+#include "blimp/engine/common/blimp_content_client.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cookie_store_factory.h"
+#include "content/public/common/content_switches.h"
+#include "net/base/cache_type.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/mapped_host_resolver.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/transport_security_state.h"
+#include "net/proxy/proxy_config_service.h"
+#include "net/proxy/proxy_service.h"
+#include "net/ssl/channel_id_service.h"
+#include "net/ssl/default_channel_id_store.h"
+#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/url_request/data_protocol_handler.h"
+#include "net/url_request/file_protocol_handler.h"
+#include "net/url_request/static_http_user_agent_settings.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_storage.h"
+#include "net/url_request/url_request_intercepting_job_factory.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "url/url_constants.h"
+
+namespace blimp {
+namespace engine {
+
+namespace {
+
+void InstallProtocolHandlers(net::URLRequestJobFactoryImpl* job_factory,
+                             content::ProtocolHandlerMap* protocol_handlers) {
+  for (content::ProtocolHandlerMap::iterator it = protocol_handlers->begin();
+       it != protocol_handlers->end(); ++it) {
+    bool set_protocol = job_factory->SetProtocolHandler(
+        it->first, make_scoped_ptr(it->second.release()));
+    DCHECK(set_protocol);
+  }
+  protocol_handlers->clear();
+}
+
+}  // namespace
+
+BlimpURLRequestContextGetter::BlimpURLRequestContextGetter(
+    bool ignore_certificate_errors,
+    const base::FilePath& base_path,
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_loop_task_runner,
+    const scoped_refptr<base::SingleThreadTaskRunner>& file_loop_task_runner,
+    content::ProtocolHandlerMap* protocol_handlers,
+    content::URLRequestInterceptorScopedVector request_interceptors,
+    net::NetLog* net_log)
+    : ignore_certificate_errors_(ignore_certificate_errors),
+      base_path_(base_path),
+      io_loop_task_runner_(io_loop_task_runner),
+      file_loop_task_runner_(file_loop_task_runner),
+      net_log_(net_log),
+      request_interceptors_(request_interceptors.Pass()) {
+  // Must first be created on the UI thread.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  std::swap(protocol_handlers_, *protocol_handlers);
+
+  // We must create the proxy config service on the UI loop on Linux because it
+  // must synchronously run on the glib message loop. This will be passed to
+  // the URLRequestContextStorage on the IO thread in GetURLRequestContext().
+  proxy_config_service_ = GetProxyConfigService();
+}
+
+BlimpURLRequestContextGetter::~BlimpURLRequestContextGetter() {}
+
+scoped_ptr<net::NetworkDelegate>
+BlimpURLRequestContextGetter::CreateNetworkDelegate() {
+  return make_scoped_ptr(new BlimpNetworkDelegate).Pass();
+}
+
+scoped_ptr<net::ProxyConfigService>
+BlimpURLRequestContextGetter::GetProxyConfigService() {
+  return net::ProxyService::CreateSystemProxyConfigService(
+      io_loop_task_runner_, file_loop_task_runner_);
+}
+
+scoped_ptr<net::ProxyService> BlimpURLRequestContextGetter::GetProxyService() {
+  return net::ProxyService::CreateUsingSystemProxyResolver(
+      proxy_config_service_.Pass(), 0, url_request_context_->net_log());
+}
+
+net::URLRequestContext* BlimpURLRequestContextGetter::GetURLRequestContext() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (!url_request_context_) {
+    const base::CommandLine& command_line =
+        *base::CommandLine::ForCurrentProcess();
+
+    url_request_context_.reset(new net::URLRequestContext());
+    url_request_context_->set_net_log(net_log_);
+    network_delegate_ = CreateNetworkDelegate();
+    url_request_context_->set_network_delegate(network_delegate_.get());
+    storage_.reset(
+        new net::URLRequestContextStorage(url_request_context_.get()));
+    storage_->set_cookie_store(
+        content::CreateCookieStore(content::CookieStoreConfig()));
+    storage_->set_channel_id_service(make_scoped_ptr(
+        new net::ChannelIDService(new net::DefaultChannelIDStore(NULL),
+                                  base::WorkerPool::GetTaskRunner(true))));
+    storage_->set_http_user_agent_settings(
+        make_scoped_ptr(new net::StaticHttpUserAgentSettings(
+            "en-us,en", GetBlimpEngineUserAgent())));
+
+    scoped_ptr<net::HostResolver> host_resolver(
+        net::HostResolver::CreateDefaultResolver(
+            url_request_context_->net_log()));
+
+    storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
+    storage_->set_transport_security_state(
+        make_scoped_ptr(new net::TransportSecurityState));
+    storage_->set_proxy_service(GetProxyService());
+    storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
+    storage_->set_http_auth_handler_factory(
+        net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
+    storage_->set_http_server_properties(
+        make_scoped_ptr(new net::HttpServerPropertiesImpl()));
+
+    base::FilePath cache_path = base_path_.Append(FILE_PATH_LITERAL("Cache"));
+    net::HttpCache::DefaultBackend* main_backend =
+        new net::HttpCache::DefaultBackend(
+            net::DISK_CACHE, net::CACHE_BACKEND_DEFAULT, cache_path, 0,
+            content::BrowserThread::GetMessageLoopProxyForThread(
+                content::BrowserThread::CACHE));
+
+    net::HttpNetworkSession::Params network_session_params;
+    network_session_params.cert_verifier =
+        url_request_context_->cert_verifier();
+    network_session_params.transport_security_state =
+        url_request_context_->transport_security_state();
+    network_session_params.channel_id_service =
+        url_request_context_->channel_id_service();
+    network_session_params.proxy_service =
+        url_request_context_->proxy_service();
+    network_session_params.ssl_config_service =
+        url_request_context_->ssl_config_service();
+    network_session_params.http_auth_handler_factory =
+        url_request_context_->http_auth_handler_factory();
+    network_session_params.network_delegate = network_delegate_.get();
+    network_session_params.http_server_properties =
+        url_request_context_->http_server_properties();
+    network_session_params.net_log = url_request_context_->net_log();
+    network_session_params.ignore_certificate_errors =
+        ignore_certificate_errors_;
+    if (command_line.HasSwitch(switches::kHostResolverRules)) {
+      scoped_ptr<net::MappedHostResolver> mapped_host_resolver(
+          new net::MappedHostResolver(host_resolver.Pass()));
+      mapped_host_resolver->SetRulesFromString(
+          command_line.GetSwitchValueASCII(switches::kHostResolverRules));
+      host_resolver = mapped_host_resolver.Pass();
+    }
+
+    // Give |storage_| ownership at the end in case it's |mapped_host_resolver|.
+    storage_->set_host_resolver(host_resolver.Pass());
+    network_session_params.host_resolver =
+        url_request_context_->host_resolver();
+
+    storage_->set_http_transaction_factory(
+        make_scoped_ptr(
+            new net::HttpCache(network_session_params, main_backend))
+            .Pass());
+
+    scoped_ptr<net::URLRequestJobFactoryImpl> job_factory(
+        new net::URLRequestJobFactoryImpl());
+    // Keep ProtocolHandlers added in sync with
+    // BlimpContentBrowserClient::IsHandledURL().
+    InstallProtocolHandlers(job_factory.get(), &protocol_handlers_);
+    bool set_protocol = job_factory->SetProtocolHandler(
+        url::kDataScheme, make_scoped_ptr(new net::DataProtocolHandler));
+    DCHECK(set_protocol);
+
+    // Set up interceptors in the reverse order so that the last inceptor is at
+    // the end of the linked list of job factories.
+    scoped_ptr<net::URLRequestJobFactory> top_job_factory = job_factory.Pass();
+    for (auto i = request_interceptors_.rbegin();
+         i != request_interceptors_.rend(); ++i) {
+      top_job_factory.reset(new net::URLRequestInterceptingJobFactory(
+          top_job_factory.Pass(), make_scoped_ptr(*i)));
+    }
+    request_interceptors_.weak_clear();
+    // Save the head of the job factory list at storage_.
+    storage_->set_job_factory(top_job_factory.Pass());
+  }
+
+  return url_request_context_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+BlimpURLRequestContextGetter::GetNetworkTaskRunner() const {
+  return content::BrowserThread::GetMessageLoopProxyForThread(
+      content::BrowserThread::IO);
+}
+
+net::HostResolver* BlimpURLRequestContextGetter::host_resolver() {
+  return url_request_context_->host_resolver();
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_url_request_context_getter.h b/blimp/engine/browser/blimp_url_request_context_getter.h
new file mode 100644
index 0000000..d0ae01b
--- /dev/null
+++ b/blimp/engine/browser/blimp_url_request_context_getter.h
@@ -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.
+
+#ifndef BLIMP_ENGINE_BROWSER_BLIMP_URL_REQUEST_CONTEXT_GETTER_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_URL_REQUEST_CONTEXT_GETTER_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/content_browser_client.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace base {
+class MessageLoop;
+class SingleThreadTaskRunner;
+}
+
+namespace net {
+class HostResolver;
+class NetworkDelegate;
+class NetLog;
+class ProxyConfigService;
+class ProxyService;
+class URLRequestContextStorage;
+}
+
+namespace blimp {
+namespace engine {
+
+class BlimpURLRequestContextGetter : public net::URLRequestContextGetter {
+ public:
+  // The content of |protocol_handlers| is is swapped into the new instance.
+  BlimpURLRequestContextGetter(
+      bool ignore_certificate_errors,
+      const base::FilePath& base_path,
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_loop_task_runner,
+      const scoped_refptr<base::SingleThreadTaskRunner>& file_loop_task_runner,
+      content::ProtocolHandlerMap* protocol_handlers,
+      content::URLRequestInterceptorScopedVector request_interceptors,
+      net::NetLog* net_log);
+
+  // net::URLRequestContextGetter implementation.
+  net::URLRequestContext* GetURLRequestContext() override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
+      const override;
+
+  net::HostResolver* host_resolver();
+
+ protected:
+  ~BlimpURLRequestContextGetter() override;
+
+  // Used by subclasses to create their own implementation of NetworkDelegate
+  // and net::ProxyService.
+  virtual scoped_ptr<net::NetworkDelegate> CreateNetworkDelegate();
+  virtual scoped_ptr<net::ProxyConfigService> GetProxyConfigService();
+  virtual scoped_ptr<net::ProxyService> GetProxyService();
+
+ private:
+  bool ignore_certificate_errors_;
+  base::FilePath base_path_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_loop_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> file_loop_task_runner_;
+  net::NetLog* net_log_;
+
+  scoped_ptr<net::ProxyConfigService> proxy_config_service_;
+  scoped_ptr<net::NetworkDelegate> network_delegate_;
+  scoped_ptr<net::URLRequestContextStorage> storage_;
+  scoped_ptr<net::URLRequestContext> url_request_context_;
+  content::ProtocolHandlerMap protocol_handlers_;
+  content::URLRequestInterceptorScopedVector request_interceptors_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpURLRequestContextGetter);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_URL_REQUEST_CONTEXT_GETTER_H_
diff --git a/blimp/engine/browser/blimp_window.cc b/blimp/engine/browser/blimp_window.cc
new file mode 100644
index 0000000..bc79448
--- /dev/null
+++ b/blimp/engine/browser/blimp_window.cc
@@ -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.
+
+#include "blimp/engine/browser/blimp_window.h"
+
+#include "base/strings/string_util.h"
+#include "blimp/engine/browser/blimp_browser_main_parts.h"
+#include "blimp/engine/browser/blimp_content_browser_client.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+
+namespace blimp {
+namespace engine {
+
+namespace {
+const int kDefaultWindowWidthDip = 1;
+const int kDefaultWindowHeightDip = 1;
+
+// TODO(haibinlu): cleanup BlimpWindows on shutdown. See crbug/540498.
+typedef std::vector<BlimpWindow*> BlimpWindows;
+base::LazyInstance<BlimpWindows>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
+
+// Returns the default size if |size| has 0 for width and/or height;
+// otherwise, returns |size|.
+gfx::Size AdjustWindowSize(const gfx::Size& size) {
+  return size.IsEmpty()
+             ? gfx::Size(kDefaultWindowWidthDip, kDefaultWindowHeightDip)
+             : size;
+}
+}  // namespace
+
+BlimpWindow::~BlimpWindow() {
+  BlimpWindows* instances = g_instances.Pointer();
+  BlimpWindows::iterator it(
+      std::find(instances->begin(), instances->end(), this));
+  DCHECK(it != instances->end());
+  instances->erase(it);
+}
+
+// static
+void BlimpWindow::Create(content::BrowserContext* browser_context,
+                         const GURL& url,
+                         content::SiteInstance* site_instance,
+                         const gfx::Size& initial_size) {
+  content::WebContents::CreateParams create_params(browser_context,
+                                                   site_instance);
+  scoped_ptr<content::WebContents> web_contents(
+      content::WebContents::Create(create_params));
+  BlimpWindow* win = DoCreate(web_contents.Pass(), initial_size);
+  if (!url.is_empty())
+    win->LoadURL(url);
+}
+
+void BlimpWindow::LoadURL(const GURL& url) {
+  content::NavigationController::LoadURLParams params(url);
+  params.transition_type = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+  web_contents_->GetController().LoadURLWithParams(params);
+  web_contents_->Focus();
+}
+
+void BlimpWindow::AddNewContents(content::WebContents* source,
+                                 content::WebContents* new_contents,
+                                 WindowOpenDisposition disposition,
+                                 const gfx::Rect& initial_rect,
+                                 bool user_gesture,
+                                 bool* was_blocked) {
+  DoCreate(scoped_ptr<content::WebContents>(new_contents), initial_rect.size());
+}
+
+content::WebContents* BlimpWindow::OpenURLFromTab(
+    content::WebContents* source,
+    const content::OpenURLParams& params) {
+  // CURRENT_TAB is the only one we implement for now.
+  if (params.disposition != CURRENT_TAB)
+    return nullptr;
+  // TOOD(haibinlu): add helper method to get LoadURLParams from OpenURLParams.
+  content::NavigationController::LoadURLParams load_url_params(params.url);
+  load_url_params.source_site_instance = params.source_site_instance;
+  load_url_params.referrer = params.referrer;
+  load_url_params.frame_tree_node_id = params.frame_tree_node_id;
+  load_url_params.transition_type = params.transition;
+  load_url_params.extra_headers = params.extra_headers;
+  load_url_params.should_replace_current_entry =
+      params.should_replace_current_entry;
+
+  if (params.transferred_global_request_id != content::GlobalRequestID()) {
+    load_url_params.is_renderer_initiated = params.is_renderer_initiated;
+    load_url_params.transferred_global_request_id =
+        params.transferred_global_request_id;
+  } else if (params.is_renderer_initiated) {
+    load_url_params.is_renderer_initiated = true;
+  }
+
+  source->GetController().LoadURLWithParams(load_url_params);
+  return source;
+}
+
+void BlimpWindow::RequestToLockMouse(content::WebContents* web_contents,
+                                     bool user_gesture,
+                                     bool last_unlocked_by_target) {
+  web_contents->GotResponseToLockMouseRequest(true);
+}
+
+void BlimpWindow::CloseContents(content::WebContents* source) {
+  delete this;
+}
+
+void BlimpWindow::ActivateContents(content::WebContents* contents) {
+  contents->GetRenderViewHost()->Focus();
+}
+
+void BlimpWindow::DeactivateContents(content::WebContents* contents) {
+  contents->GetRenderViewHost()->Blur();
+}
+
+BlimpWindow::BlimpWindow(scoped_ptr<content::WebContents> web_contents)
+    : web_contents_(web_contents.Pass()) {
+  web_contents_->SetDelegate(this);
+  g_instances.Get().push_back(this);
+}
+
+// static
+BlimpWindow* BlimpWindow::DoCreate(
+    scoped_ptr<content::WebContents> web_contents,
+    const gfx::Size& initial_size) {
+  BlimpWindow* win = new BlimpWindow(web_contents.Pass());
+  content::RenderWidgetHostView* host_view =
+      win->web_contents_->GetRenderWidgetHostView();
+  if (host_view)
+    host_view->SetSize(AdjustWindowSize(initial_size));
+  return win;
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/browser/blimp_window.h b/blimp/engine/browser/blimp_window.h
new file mode 100644
index 0000000..fadf3b63
--- /dev/null
+++ b/blimp/engine/browser/blimp_window.h
@@ -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.
+#ifndef BLIMP_ENGINE_BROWSER_BLIMP_WINDOW_H_
+#define BLIMP_ENGINE_BROWSER_BLIMP_WINDOW_H_
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace content {
+class BrowserContext;
+class SiteInstance;
+class WebContents;
+}
+
+class GURL;
+
+namespace blimp {
+namespace engine {
+
+// Owns and controls a WebContents instance corresponding to a window on
+// Blimp client.
+class BlimpWindow : public content::WebContentsDelegate {
+ public:
+  // This also unregisters itself with a singleton registry.
+  ~BlimpWindow() override;
+
+  // Creates a new blimp window with |initial_size| and navigates to the |url|.
+  // Caller retains ownership of |browser_context| and |site_instance| and
+  // ensures |browser_context| and |site_instance| outlives BlimpWindow.
+  static void Create(content::BrowserContext* browser_context,
+                     const GURL& url,
+                     content::SiteInstance* site_instance,
+                     const gfx::Size& initial_size);
+
+  // Navigates to |url|.
+  void LoadURL(const GURL& url);
+
+  // content::WebContentsDelegate implementation.
+  content::WebContents* OpenURLFromTab(
+      content::WebContents* source,
+      const content::OpenURLParams& params) override;
+  void AddNewContents(content::WebContents* source,
+                      content::WebContents* new_contents,
+                      WindowOpenDisposition disposition,
+                      const gfx::Rect& initial_rect,
+                      bool user_gesture,
+                      bool* was_blocked) override;
+  void RequestToLockMouse(content::WebContents* web_contents,
+                          bool user_gesture,
+                          bool last_unlocked_by_target) override;
+  void CloseContents(content::WebContents* source) override;
+  void ActivateContents(content::WebContents* contents) override;
+  void DeactivateContents(content::WebContents* contents) override;
+
+ private:
+  // The newly created instance registers itself with a singleton registry.
+  explicit BlimpWindow(scoped_ptr<content::WebContents> web_contents);
+
+  // Helper to create a new BlimpWindow given |web_contents|.
+  // The newly window is owned by a singleton registry.
+  static BlimpWindow* DoCreate(scoped_ptr<content::WebContents> web_contents,
+                               const gfx::Size& initial_size);
+
+  scoped_ptr<content::WebContents> web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpWindow);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_BROWSER_BLIMP_WINDOW_H_
diff --git a/blimp/engine/common/BUILD.gn b/blimp/engine/common/BUILD.gn
new file mode 100644
index 0000000..48632dc
--- /dev/null
+++ b/blimp/engine/common/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+source_set("common") {
+  sources = [
+    "blimp_content_client.cc",
+    "blimp_content_client.h",
+  ]
+
+  deps = [
+    "//base",
+    "//content/public/common",
+    "//ui/base",
+  ]
+}
diff --git a/blimp/engine/common/blimp_content_client.cc b/blimp/engine/common/blimp_content_client.cc
new file mode 100644
index 0000000..00c2c2d
--- /dev/null
+++ b/blimp/engine/common/blimp_content_client.cc
@@ -0,0 +1,50 @@
+// 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 "blimp/engine/common/blimp_content_client.h"
+
+#include "content/public/common/user_agent.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace blimp {
+namespace engine {
+
+// TODO(haibinlu) Generate proper version. See crbug/537367.
+const char kProduct[] = "Chrome/20.77.33.5";
+
+std::string GetBlimpEngineUserAgent() {
+  return content::BuildUserAgentFromProduct(kProduct);
+}
+
+BlimpContentClient::~BlimpContentClient() {}
+
+std::string BlimpContentClient::GetUserAgent() const {
+  return GetBlimpEngineUserAgent();
+}
+
+base::string16 BlimpContentClient::GetLocalizedString(int message_id) const {
+  return l10n_util::GetStringUTF16(message_id);
+}
+
+base::StringPiece BlimpContentClient::GetDataResource(
+    int resource_id,
+    ui::ScaleFactor scale_factor) const {
+  return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
+      resource_id, scale_factor);
+}
+
+base::RefCountedStaticMemory* BlimpContentClient::GetDataResourceBytes(
+    int resource_id) const {
+  return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+      resource_id);
+}
+
+gfx::Image& BlimpContentClient::GetNativeImageNamed(int resource_id) const {
+  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+      resource_id);
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/common/blimp_content_client.h b/blimp/engine/common/blimp_content_client.h
new file mode 100644
index 0000000..8826a62
--- /dev/null
+++ b/blimp/engine/common/blimp_content_client.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef BLIMP_ENGINE_COMMON_BLIMP_CONTENT_CLIENT_H_
+#define BLIMP_ENGINE_COMMON_BLIMP_CONTENT_CLIENT_H_
+
+#include <string>
+
+#include "content/public/common/content_client.h"
+
+namespace blimp {
+namespace engine {
+
+std::string GetBlimpEngineUserAgent();
+
+class BlimpContentClient : public content::ContentClient {
+ public:
+  ~BlimpContentClient() override;
+
+  // content::ContentClient implementation.
+  std::string GetUserAgent() const override;
+  base::string16 GetLocalizedString(int message_id) const override;
+  base::StringPiece GetDataResource(
+      int resource_id,
+      ui::ScaleFactor scale_factor) const override;
+  base::RefCountedStaticMemory* GetDataResourceBytes(
+      int resource_id) const override;
+  gfx::Image& GetNativeImageNamed(int resource_id) const override;
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_COMMON_BLIMP_CONTENT_CLIENT_H_
diff --git a/blimp/engine/renderer/BUILD.gn b/blimp/engine/renderer/BUILD.gn
new file mode 100644
index 0000000..8d9db08
--- /dev/null
+++ b/blimp/engine/renderer/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+source_set("renderer") {
+  sources = [
+    "blimp_content_renderer_client.cc",
+    "blimp_content_renderer_client.h",
+  ]
+
+  deps = [
+    "//components/web_cache/renderer",
+    "//content/public/renderer",
+  ]
+}
diff --git a/blimp/engine/renderer/blimp_content_renderer_client.cc b/blimp/engine/renderer/blimp_content_renderer_client.cc
new file mode 100644
index 0000000..6b6e7b7a
--- /dev/null
+++ b/blimp/engine/renderer/blimp_content_renderer_client.cc
@@ -0,0 +1,23 @@
+// 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 "blimp/engine/renderer/blimp_content_renderer_client.h"
+
+#include "components/web_cache/renderer/web_cache_render_process_observer.h"
+#include "content/public/renderer/render_thread.h"
+
+namespace blimp {
+namespace engine {
+
+BlimpContentRendererClient::BlimpContentRendererClient() {}
+
+BlimpContentRendererClient::~BlimpContentRendererClient() {}
+
+void BlimpContentRendererClient::RenderThreadStarted() {
+  web_cache_observer_.reset(new web_cache::WebCacheRenderProcessObserver());
+  content::RenderThread::Get()->AddObserver(web_cache_observer_.get());
+}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/renderer/blimp_content_renderer_client.h b/blimp/engine/renderer/blimp_content_renderer_client.h
new file mode 100644
index 0000000..0006200
--- /dev/null
+++ b/blimp/engine/renderer/blimp_content_renderer_client.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef BLIMP_ENGINE_RENDERER_BLIMP_CONTENT_RENDERER_CLIENT_H_
+#define BLIMP_ENGINE_RENDERER_BLIMP_CONTENT_RENDERER_CLIENT_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/renderer/content_renderer_client.h"
+
+namespace web_cache {
+class WebCacheRenderProcessObserver;
+}
+
+namespace blimp {
+namespace engine {
+
+class BlimpContentRendererClient : public content::ContentRendererClient {
+ public:
+  BlimpContentRendererClient();
+  ~BlimpContentRendererClient() override;
+
+  // content::ContentRendererClient implementation.
+  void RenderThreadStarted() override;
+
+ private:
+  // This observer manages the process-global web cache.
+  scoped_ptr<web_cache::WebCacheRenderProcessObserver> web_cache_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpContentRendererClient);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_RENDERER_BLIMP_CONTENT_RENDERER_CLIENT_H_
diff --git a/blimp/engine/ui/BUILD.gn b/blimp/engine/ui/BUILD.gn
new file mode 100644
index 0000000..4e88178
--- /dev/null
+++ b/blimp/engine/ui/BUILD.gn
@@ -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.
+
+source_set("ui") {
+  sources = [
+    "blimp_screen.cc",
+    "blimp_screen.h",
+  ]
+
+  deps = [
+    "//ui/gfx",
+  ]
+}
diff --git a/blimp/engine/ui/blimp_screen.cc b/blimp/engine/ui/blimp_screen.cc
new file mode 100644
index 0000000..2eba20f3
--- /dev/null
+++ b/blimp/engine/ui/blimp_screen.cc
@@ -0,0 +1,77 @@
+// 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 "blimp/engine/ui/blimp_screen.h"
+
+namespace blimp {
+namespace engine {
+
+namespace {
+
+const int64 kDisplayId = 1;
+const int kDefaultDisplayWidth = 1;
+const int kDefaultDisplayHeight = 1;
+const float kDefaultScale = 1.0f;
+const int kNumDisplays = 1;
+
+}  // namespace
+
+BlimpScreen::BlimpScreen() : display_(kDisplayId) {
+  display_.SetScaleAndBounds(
+      kDefaultScale, gfx::Rect(kDefaultDisplayWidth, kDefaultDisplayHeight));
+}
+
+BlimpScreen::~BlimpScreen() {}
+
+void BlimpScreen::UpdateDisplaySize(const gfx::Size& size) {
+  display_.SetScaleAndBounds(kDefaultScale, gfx::Rect(size));
+}
+
+gfx::Point BlimpScreen::GetCursorScreenPoint() {
+  return gfx::Point();
+}
+
+gfx::NativeWindow BlimpScreen::GetWindowUnderCursor() {
+  NOTIMPLEMENTED();
+  return gfx::NativeWindow(nullptr);
+}
+
+gfx::NativeWindow BlimpScreen::GetWindowAtScreenPoint(const gfx::Point& point) {
+  NOTIMPLEMENTED();
+  return gfx::NativeWindow(nullptr);
+}
+
+int BlimpScreen::GetNumDisplays() const {
+  return kNumDisplays;
+}
+
+std::vector<gfx::Display> BlimpScreen::GetAllDisplays() const {
+  return std::vector<gfx::Display>(1, display_);
+}
+
+gfx::Display BlimpScreen::GetDisplayNearestWindow(
+    gfx::NativeWindow window) const {
+  return display_;
+}
+
+gfx::Display BlimpScreen::GetDisplayNearestPoint(
+    const gfx::Point& point) const {
+  return display_;
+}
+
+gfx::Display BlimpScreen::GetDisplayMatching(
+    const gfx::Rect& match_rect) const {
+  return display_;
+}
+
+gfx::Display BlimpScreen::GetPrimaryDisplay() const {
+  return display_;
+}
+
+void BlimpScreen::AddObserver(gfx::DisplayObserver* observer) {}
+
+void BlimpScreen::RemoveObserver(gfx::DisplayObserver* observer) {}
+
+}  // namespace engine
+}  // namespace blimp
diff --git a/blimp/engine/ui/blimp_screen.h b/blimp/engine/ui/blimp_screen.h
new file mode 100644
index 0000000..99858b8a
--- /dev/null
+++ b/blimp/engine/ui/blimp_screen.h
@@ -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.
+
+#ifndef BLIMP_ENGINE_UI_BLIMP_SCREEN_H_
+#define BLIMP_ENGINE_UI_BLIMP_SCREEN_H_
+
+#include <vector>
+
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+
+namespace blimp {
+namespace engine {
+
+// Presents the client's single screen.
+class BlimpScreen : public gfx::Screen {
+ public:
+  BlimpScreen();
+  ~BlimpScreen() override;
+
+  // Updates the size reported by the primary display.
+  void UpdateDisplaySize(const gfx::Size& size);
+
+  // gfx::Screen implementation.
+  gfx::Point GetCursorScreenPoint() override;
+  gfx::NativeWindow GetWindowUnderCursor() override;
+  gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override;
+  int GetNumDisplays() const override;
+  std::vector<gfx::Display> GetAllDisplays() const override;
+  gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override;
+  gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override;
+  gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override;
+  gfx::Display GetPrimaryDisplay() const override;
+  void AddObserver(gfx::DisplayObserver* observer) override;
+  void RemoveObserver(gfx::DisplayObserver* observer) override;
+
+ private:
+  gfx::Display display_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlimpScreen);
+};
+
+}  // namespace engine
+}  // namespace blimp
+
+#endif  // BLIMP_ENGINE_UI_BLIMP_SCREEN_H_
diff --git a/build/android/copy_ex.gypi b/build/android/copy_ex.gypi
new file mode 100644
index 0000000..bae0dd24
--- /dev/null
+++ b/build/android/copy_ex.gypi
@@ -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.
+
+# Copy files to a directory with the option to clear directory first.
+#
+# Variables:
+#   dest_path - directory to copy files to.
+#   src_files - a list of files to copy.
+#   clear - optional, if set, clear directory before copying files.
+#   stamp - optional, path to touch on success.
+#
+# Exmaple
+#  {
+#    'target_name': 'copy_assets',
+#    'type': 'none',
+#    'variables': {
+#      'dest_path': 'apk/assets/path',
+#      'src_files': ['path1/fr.pak'],
+#      'clear': 1,
+#    },
+#    'includes': [ '../build/android/copy_ex.gypi' ],
+#  },
+#
+{
+  'variables': {
+    'clear%': 0,
+    'stamp%': '',
+  },
+  'actions': [{
+    'action_name': '<(_target_name)_copy_ex',
+    'variables': {
+      'additional_args':[],
+      'conditions': [
+        ['clear == 1', {
+          'additional_args': ['--clear'],
+        }],
+        ['stamp != ""', {
+          'additional_args': ['--stamp', '<(stamp)'],
+        }],
+      ],
+    },
+    'inputs': [
+      '<(DEPTH)/build/android/gyp/copy_ex.py',
+      '<(DEPTH)/build/android/gyp/generate_copy_ex_outputs.py',
+      '<@(src_files)',
+    ],
+    'outputs': [
+      '<(stamp)',
+      '<!@pymod_do_main(generate_copy_ex_outputs --dest-path <(dest_path) --src-files <(src_files))'
+    ],
+    'action': [
+      'python', '<(DEPTH)/build/android/gyp/copy_ex.py',
+      '--dest', '<(dest_path)',
+      '--files', '<(src_files)',
+      '<(additional_args)',
+    ],
+  }],
+}
diff --git a/build/android/gyp/generate_copy_ex_outputs.py b/build/android/gyp/generate_copy_ex_outputs.py
new file mode 100755
index 0000000..e425b4a6
--- /dev/null
+++ b/build/android/gyp/generate_copy_ex_outputs.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+# Generate outputs according source files and destination path for
+# copy_ex.gypi
+
+import argparse
+import os
+import sys
+
+def DoMain(argv):
+  parser = argparse.ArgumentParser(prog='generate_copy_ex_outputs')
+  parser.add_argument('--src-files',
+                      nargs = '+',
+                      help = 'a list of files to copy')
+  parser.add_argument('--dest-path',
+                      required = True,
+                      help = 'the directory to copy file to')
+  options = parser.parse_args(argv)
+  # Quote each element so filename spaces don't mess up gyp's attempt to parse
+  # it into a list.
+  return ' '.join('"%s"' % os.path.join(options.dest_path,
+                                        os.path.basename(src))
+                  for src in options.src_files)
+
+if __name__ == '__main__':
+  results = DoMain(sys.argv[1:])
+  if results:
+    print results
+
diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py
index 96a61511..cbc475e 100644
--- a/build/android/pylib/constants/__init__.py
+++ b/build/android/pylib/constants/__init__.py
@@ -158,11 +158,11 @@
 ANDROID_SDK_VERSION = version_codes.MARSHMALLOW
 ANDROID_SDK_BUILD_TOOLS_VERSION = '23.0.0'
 ANDROID_SDK_ROOT = os.path.join(DIR_SOURCE_ROOT,
-                                'third_party/android_tools/sdk')
+                                'third_party', 'android_tools', 'sdk')
 ANDROID_SDK_TOOLS = os.path.join(ANDROID_SDK_ROOT,
                                  'build-tools', ANDROID_SDK_BUILD_TOOLS_VERSION)
 ANDROID_NDK_ROOT = os.path.join(DIR_SOURCE_ROOT,
-                                'third_party/android_tools/ndk')
+                                'third_party', 'android_tools', 'ndk')
 
 PROGUARD_SCRIPT_PATH = os.path.join(
     ANDROID_SDK_ROOT, 'tools', 'proguard', 'bin', 'proguard.sh')
diff --git a/build/android/pylib/instrumentation/test_jar.py b/build/android/pylib/instrumentation/test_jar.py
index cabcc8a..634cf470 100644
--- a/build/android/pylib/instrumentation/test_jar.py
+++ b/build/android/pylib/instrumentation/test_jar.py
@@ -165,8 +165,10 @@
     return sorted(tests_missing_annotations)
 
   def _IsTestValidForSdkRange(self, test_name, attached_min_sdk_level):
-    required_min_sdk_level = int(
-        self.GetTestAnnotations(test_name).get('MinAndroidSdkLevel', 0))
+    required_min_sdk_level = self.GetTestAnnotations(
+      test_name).get('MinAndroidSdkLevel', None)
+    if required_min_sdk_level:
+      required_min_sdk_level = int(required_min_sdk_level['value'])
     return (required_min_sdk_level is None or
             attached_min_sdk_level >= required_min_sdk_level)
 
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 5266aed..9473566 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -156,7 +156,8 @@
       Whether the feature being tested is FirstRunExperience.
     """
     annotations = self.test_pkg.GetTestAnnotations(test)
-    return 'FirstRunExperience' == annotations.get('Feature', None)
+    feature = annotations.get('Feature', None)
+    return feature and 'FirstRunExperience' in feature['value']
 
   def _IsPerfTest(self, test):
     """Determines whether a test is a performance test.
@@ -276,10 +277,10 @@
     timeout_scale = 1
     if 'TimeoutScale' in annotations:
       try:
-        timeout_scale = int(annotations['TimeoutScale'])
+        timeout_scale = int(annotations['TimeoutScale']['value'])
       except ValueError:
         logging.warning('Non-integer value of TimeoutScale ignored. (%s)',
-                        annotations['TimeoutScale'])
+                        annotations['TimeoutScale']['value'])
     if self.options.wait_for_debugger:
       timeout_scale *= 100
     return timeout_scale
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py
index a2d002ba..450cfaf 100644
--- a/build/android/pylib/uiautomator/test_runner.py
+++ b/build/android/pylib/uiautomator/test_runner.py
@@ -62,7 +62,8 @@
     self.device.ClearApplicationState(self._package)
     if self.flags:
       annotations = self.test_pkg.GetTestAnnotations(test)
-      if 'FirstRunExperience' == annotations.get('Feature', None):
+      feature = annotations.get('Feature', None)
+      if feature and 'FirstRunExperience' in feature['value']:
         self.flags.RemoveFlags(['--disable-fre'])
       else:
         self.flags.AddFlags(['--disable-fre'])
diff --git a/build/android/pylib/utils/proguard.py b/build/android/pylib/utils/proguard.py
index 251cc4d..4f5a5213 100644
--- a/build/android/pylib/utils/proguard.py
+++ b/build/android/pylib/utils/proguard.py
@@ -18,7 +18,9 @@
 _PROGUARD_METHOD_RE = re.compile(r'\s*?- Method:\s*(\S*)[(].*$')
 _PROGUARD_ANNOTATION_RE = re.compile(r'\s*?- Annotation \[L(\S*);\]:$')
 _PROGUARD_ANNOTATION_CONST_RE = (
-    re.compile(r'\s*?- Constant element value.*$'))
+    re.compile(r'\s*?- Constant element value \[(\S*) .*\]$'))
+_PROGUARD_ANNOTATION_ARRAY_RE = (
+    re.compile(r'\s*?- Array element value \[(\S*)\]:$'))
 _PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'\s*?- \S+? \[(.*)\]$')
 
 _PROGUARD_PATH_SDK = os.path.join(
@@ -44,11 +46,11 @@
           {
             'class': '',
             'superclass': '',
-            'annotations': {},
+            'annotations': {/* dict -- see below */},
             'methods': [
               {
                 'method': '',
-                'annotations': {},
+                'annotations': {/* dict -- see below */},
               },
               ...
             ],
@@ -56,6 +58,26 @@
           ...
         ],
       }
+
+    Annotations dict format:
+      {
+        'empty-annotation-class-name': None,
+        'annotation-class-name': {
+          'field': 'primitive-value',
+          'field': [ 'array-item-1', 'array-item-2', ... ],
+          /* Object fields are not supported yet, coming soon! */
+          'field': {
+            /* Object value */
+            'field': 'primitive-value',
+            'field': [ 'array-item-1', 'array-item-2', ... ],
+            'field': { /* Object value */ }
+          }
+        }
+      }
+
+    Note that for top-level annotations their class names are used for
+    identification, whereas for any nested annotations the corresponding
+    field names are used.
   """
 
   with tempfile.NamedTemporaryFile() as proguard_output:
@@ -69,15 +91,66 @@
                        '-dump', proguard_output.name])
     return Parse(proguard_output)
 
+class _ParseState(object):
+  def __init__(self):
+    self.class_result = None
+    self.method_result = None
+    self.annotation = None
+    self.annotation_field = None
+
+  def ResetPerSection(self):
+    self.InitMethod(None)
+
+  def InitClass(self, class_result):
+    self.InitMethod(None)
+    self.class_result = class_result
+
+  def InitMethod(self, method_result):
+    self.annotation = None
+    self.ResetAnnotationField()
+    self.method_result = method_result
+    if method_result:
+      self.class_result['methods'].append(method_result)
+
+  def InitAnnotation(self, annotation):
+    self.annotation = annotation
+    self.GetAnnotations()[annotation] = None
+    self.ResetAnnotationField()
+
+  def ResetAnnotationField(self):
+    self.annotation_field = None
+
+  def InitAnnotationField(self, field, value):
+    if not self.GetCurrentAnnotation():
+      self.GetAnnotations()[self.annotation] = {}
+    self.GetCurrentAnnotation()[field] = value
+    self.annotation_field = field
+
+  def UpdateCurrentAnnotationField(self, value):
+    ann = self.GetCurrentAnnotation()
+    assert ann
+    if type(ann[self.annotation_field]) is list:
+      ann[self.annotation_field].append(value)
+    else:
+      ann[self.annotation_field] = value
+      self.ResetAnnotationField()
+
+  def GetAnnotations(self):
+    if self.method_result:
+      return self.method_result['annotations']
+    else:
+      return self.class_result['annotations']
+
+  def GetCurrentAnnotation(self):
+    assert self.annotation
+    return self.GetAnnotations()[self.annotation]
+
 def Parse(proguard_output):
   results = {
     'classes': [],
   }
 
-  annotation = None
-  annotation_has_value = False
-  class_result = None
-  method_result = None
+  state = _ParseState()
 
   for line in proguard_output:
     line = line.strip('\r\n')
@@ -91,57 +164,56 @@
         'methods': [],
       }
       results['classes'].append(class_result)
-      annotation = None
-      annotation_has_value = False
-      method_result = None
+      state.InitClass(class_result)
       continue
 
-    if not class_result:
+    if not state.class_result:
       continue
 
     m = _PROGUARD_SUPERCLASS_RE.match(line)
     if m:
-      class_result['superclass'] = m.group(1).replace('/', '.')
+      state.class_result['superclass'] = m.group(1).replace('/', '.')
       continue
 
     m = _PROGUARD_SECTION_RE.match(line)
     if m:
-      annotation = None
-      annotation_has_value = False
-      method_result = None
+      state.ResetPerSection()
       continue
 
     m = _PROGUARD_METHOD_RE.match(line)
     if m:
-      method_result = {
+      state.InitMethod({
         'method': m.group(1),
         'annotations': {},
-      }
-      class_result['methods'].append(method_result)
-      annotation = None
-      annotation_has_value = False
+      })
       continue
 
     m = _PROGUARD_ANNOTATION_RE.match(line)
     if m:
       # Ignore the annotation package.
-      annotation = m.group(1).split('/')[-1]
-      if method_result:
-        method_result['annotations'][annotation] = None
-      else:
-        class_result['annotations'][annotation] = None
+      state.InitAnnotation(m.group(1).split('/')[-1])
       continue
 
-    if annotation:
-      if not annotation_has_value:
-        m = _PROGUARD_ANNOTATION_CONST_RE.match(line)
-        annotation_has_value = bool(m)
-      else:
+    if state.annotation:
+      if state.annotation_field:
         m = _PROGUARD_ANNOTATION_VALUE_RE.match(line)
         if m:
-          if method_result:
-            method_result['annotations'][annotation] = m.group(1)
-          else:
-            class_result['annotations'][annotation] = m.group(1)
-        annotation_has_value = None
+          state.UpdateCurrentAnnotationField(m.group(1))
+          continue
+        m = _PROGUARD_ANNOTATION_CONST_RE.match(line)
+        if m:
+          if not m.group(1) == '(default)':
+            state.ResetAnnotationField()
+        else:
+          m = _PROGUARD_ANNOTATION_ARRAY_RE.match(line)
+          if m:
+            state.ResetAnnotationField()
+      if not state.annotation_field:
+        m = _PROGUARD_ANNOTATION_CONST_RE.match(line)
+        if m:
+          state.InitAnnotationField(m.group(1), None)
+          continue
+        m = _PROGUARD_ANNOTATION_ARRAY_RE.match(line)
+        if m:
+          state.InitAnnotationField(m.group(1), [])
   return results
diff --git a/build/android/pylib/utils/proguard_test.py b/build/android/pylib/utils/proguard_test.py
index e5d5e013..e8c81cc 100644
--- a/build/android/pylib/utils/proguard_test.py
+++ b/build/android/pylib/utils/proguard_test.py
@@ -8,6 +8,9 @@
 
 class TestParse(unittest.TestCase):
 
+  def setUp(self):
+    self.maxDiff = None
+
   def testClass(self):
     actual = proguard.Parse(
       ['- Program class: org/example/Test',
@@ -52,7 +55,12 @@
        '  - Annotation [Lorg/example/Annotation;]:',
        '  - Annotation [Lorg/example/AnnotationWithValue;]:',
        '    - Constant element value [attr \'13\']',
-       '      - Utf8 [val]'])
+       '      - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationWithTwoValues;]:',
+       '    - Constant element value [attr1 \'13\']',
+       '      - Utf8 [val1]',
+       '    - Constant element value [attr2 \'13\']',
+       '      - Utf8 [val2]'])
     expected = {
       'classes': [
         {
@@ -60,7 +68,39 @@
           'superclass': '',
           'annotations': {
             'Annotation': None,
-            'AnnotationWithValue': 'val'
+            'AnnotationWithValue': {'attr': 'val'},
+            'AnnotationWithTwoValues': {'attr1': 'val1', 'attr2': 'val2'}
+          },
+          'methods': []
+        }
+      ]
+    }
+    self.assertEquals(expected, actual)
+
+  def testClassAnnotationWithArrays(self):
+    actual = proguard.Parse(
+      ['- Program class: org/example/Test',
+       '  - Annotation [Lorg/example/AnnotationWithEmptyArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '  - Annotation [Lorg/example/AnnotationWithOneElemArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationWithTwoElemArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val1]',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val2]'])
+    expected = {
+      'classes': [
+        {
+          'class': 'org.example.Test',
+          'superclass': '',
+          'annotations': {
+            'AnnotationWithEmptyArray': {'arrayAttr': []},
+            'AnnotationWithOneElemArray': {'arrayAttr': ['val']},
+            'AnnotationWithTwoElemArray': {'arrayAttr': ['val1', 'val2']}
           },
           'methods': []
         }
@@ -76,7 +116,12 @@
        '  - Annotation [Lorg/example/Annotation;]:',
        '  - Annotation [Lorg/example/AnnotationWithValue;]:',
        '    - Constant element value [attr \'13\']',
-       '      - Utf8 [val]'])
+       '      - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationWithTwoValues;]:',
+       '    - Constant element value [attr1 \'13\']',
+       '      - Utf8 [val1]',
+       '    - Constant element value [attr2 \'13\']',
+       '      - Utf8 [val2]'])
     expected = {
       'classes': [
         {
@@ -88,7 +133,94 @@
               'method': 'Test',
               'annotations': {
                 'Annotation': None,
-                'AnnotationWithValue': 'val'
+                'AnnotationWithValue': {'attr': 'val'},
+                'AnnotationWithTwoValues': {'attr1': 'val1', 'attr2': 'val2'}
+              },
+            }
+          ]
+        }
+      ]
+    }
+    self.assertEquals(expected, actual)
+
+  def testMethodAnnotationWithArrays(self):
+    actual = proguard.Parse(
+      ['- Program class: org/example/Test',
+       'Methods (count = 1):',
+       '- Method:       Test()V',
+       '  - Annotation [Lorg/example/AnnotationWithEmptyArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '  - Annotation [Lorg/example/AnnotationWithOneElemArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationWithTwoElemArray;]:',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val1]',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val2]'])
+    expected = {
+      'classes': [
+        {
+          'class': 'org.example.Test',
+          'superclass': '',
+          'annotations': {},
+          'methods': [
+            {
+              'method': 'Test',
+              'annotations': {
+                'AnnotationWithEmptyArray': {'arrayAttr': []},
+                'AnnotationWithOneElemArray': {'arrayAttr': ['val']},
+                'AnnotationWithTwoElemArray': {'arrayAttr': ['val1', 'val2']}
+              },
+            }
+          ]
+        }
+      ]
+    }
+    self.assertEquals(expected, actual)
+
+  def testMethodAnnotationWithPrimitivesAndArrays(self):
+    actual = proguard.Parse(
+      ['- Program class: org/example/Test',
+       'Methods (count = 1):',
+       '- Method:       Test()V',
+       '  - Annotation [Lorg/example/AnnotationPrimitiveThenArray;]:',
+       '    - Constant element value [attr \'13\']',
+       '      - Utf8 [val]',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationArrayThenPrimitive;]:',
+       '    - Array element value [arrayAttr]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val]',
+       '    - Constant element value [attr \'13\']',
+       '      - Utf8 [val]',
+       '  - Annotation [Lorg/example/AnnotationTwoArrays;]:',
+       '    - Array element value [arrayAttr1]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val1]',
+       '    - Array element value [arrayAttr2]:',
+       '      - Constant element value [(default) \'13\']',
+       '        - Utf8 [val2]'])
+    expected = {
+      'classes': [
+        {
+          'class': 'org.example.Test',
+          'superclass': '',
+          'annotations': {},
+          'methods': [
+            {
+              'method': 'Test',
+              'annotations': {
+                'AnnotationPrimitiveThenArray': {'attr': 'val',
+                                                 'arrayAttr': ['val']},
+                'AnnotationArrayThenPrimitive': {'arrayAttr': ['val'],
+                                                 'attr': 'val'},
+                'AnnotationTwoArrays': {'arrayAttr1': ['val1'],
+                                        'arrayAttr2': ['val2']}
               },
             }
           ]
diff --git a/build/android/tombstones.py b/build/android/tombstones.py
index f4be6bd..c961d75 100755
--- a/build/android/tombstones.py
+++ b/build/android/tombstones.py
@@ -23,7 +23,6 @@
 from devil.android import device_utils
 from devil.utils import run_tests_helper
 
-
 _TZ_UTC = {'TZ': 'UTC'}
 
 def _ListTombstones(device):
diff --git a/build/common.gypi b/build/common.gypi
index 84d9514..f5743e21 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -1515,6 +1515,16 @@
     # Compile d8 for the host toolset.
     'v8_toolset_for_d8': 'host',
 
+    # V8 extras
+    # Adding V8 extras files requires API owners review
+    # Be sure to synchronize with build/module_args/v8.gni
+
+    'v8_extra_library_files': [
+    ],
+    'v8_experimental_extra_library_files': [
+      '../third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js',
+    ],
+
     # Use brlapi from brltty for braille display support.
     'use_brlapi%': 0,
 
diff --git a/build/module_args/v8.gni b/build/module_args/v8.gni
index 4a4caed..f18bfaf 100644
--- a/build/module_args/v8.gni
+++ b/build/module_args/v8.gni
@@ -11,5 +11,10 @@
 
 v8_use_external_startup_data = !(is_chromeos || is_win || is_ios)
 
+# V8 extras
+# Adding V8 extras files requires API owners review
+# Be sure to synchronize with build/common.gypi
+
 v8_extra_library_files = []
-v8_experimental_extra_library_files = []
+v8_experimental_extra_library_files =
+    [ "../third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js" ]
diff --git a/cc/animation/scrollbar_animation_controller.cc b/cc/animation/scrollbar_animation_controller.cc
index 48b199d..2c11f73 100644
--- a/cc/animation/scrollbar_animation_controller.cc
+++ b/cc/animation/scrollbar_animation_controller.cc
@@ -12,26 +12,22 @@
 namespace cc {
 
 ScrollbarAnimationController::ScrollbarAnimationController(
-    LayerImpl* scroll_layer,
+    int scroll_layer_id,
     ScrollbarAnimationControllerClient* client,
     base::TimeDelta delay_before_starting,
     base::TimeDelta resize_delay_before_starting,
     base::TimeDelta duration)
-    : scroll_layer_(scroll_layer),
-      client_(client),
+    : client_(client),
       delay_before_starting_(delay_before_starting),
       resize_delay_before_starting_(resize_delay_before_starting),
       duration_(duration),
       is_animating_(false),
+      scroll_layer_id_(scroll_layer_id),
       currently_scrolling_(false),
       scroll_gesture_has_scrolled_(false),
-      weak_factory_(this) {
-}
+      weak_factory_(this) {}
 
-ScrollbarAnimationController::~ScrollbarAnimationController() {
-  if (is_animating_)
-    client_->StopAnimatingScrollbarAnimationController(this);
-}
+ScrollbarAnimationController::~ScrollbarAnimationController() {}
 
 void ScrollbarAnimationController::Animate(base::TimeTicks now) {
   if (!is_animating_)
@@ -42,6 +38,9 @@
 
   float progress = AnimationProgressAtTime(now);
   RunAnimationFrame(progress);
+
+  if (is_animating_)
+    client_->SetNeedsAnimateForScrollbarAnimation();
 }
 
 float ScrollbarAnimationController::AnimationProgressAtTime(
@@ -90,12 +89,15 @@
   delayed_scrollbar_fade_.Cancel();
   is_animating_ = true;
   last_awaken_time_ = base::TimeTicks();
-  client_->StartAnimatingScrollbarAnimationController(this);
+  client_->SetNeedsAnimateForScrollbarAnimation();
 }
 
 void ScrollbarAnimationController::StopAnimation() {
   is_animating_ = false;
-  client_->StopAnimatingScrollbarAnimationController(this);
+}
+
+ScrollbarSet ScrollbarAnimationController::Scrollbars() const {
+  return client_->ScrollbarsFor(scroll_layer_id_);
 }
 
 }  // namespace cc
diff --git a/cc/animation/scrollbar_animation_controller.h b/cc/animation/scrollbar_animation_controller.h
index 795c7c1..3bb1507 100644
--- a/cc/animation/scrollbar_animation_controller.h
+++ b/cc/animation/scrollbar_animation_controller.h
@@ -10,6 +10,7 @@
 #include "base/time/time.h"
 #include "cc/base/cc_export.h"
 #include "cc/layers/layer_impl.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
 namespace cc {
@@ -18,13 +19,11 @@
 
 class CC_EXPORT ScrollbarAnimationControllerClient {
  public:
-  virtual void StartAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) = 0;
-  virtual void StopAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) = 0;
   virtual void PostDelayedScrollbarAnimationTask(const base::Closure& task,
                                                  base::TimeDelta delay) = 0;
   virtual void SetNeedsRedrawForScrollbarAnimation() = 0;
+  virtual void SetNeedsAnimateForScrollbarAnimation() = 0;
+  virtual ScrollbarSet ScrollbarsFor(int scroll_layer_id) const = 0;
 
  protected:
   virtual ~ScrollbarAnimationControllerClient() {}
@@ -46,7 +45,7 @@
   virtual void DidMouseMoveNear(float distance) {}
 
  protected:
-  ScrollbarAnimationController(LayerImpl* scroll_layer,
+  ScrollbarAnimationController(int scroll_layer_id,
                                ScrollbarAnimationControllerClient* client,
                                base::TimeDelta delay_before_starting,
                                base::TimeDelta resize_delay_before_starting,
@@ -56,8 +55,8 @@
 
   void StartAnimation();
   void StopAnimation();
+  ScrollbarSet Scrollbars() const;
 
-  LayerImpl* scroll_layer_;
   ScrollbarAnimationControllerClient* client_;
 
  private:
@@ -74,6 +73,7 @@
 
   bool is_animating_;
 
+  int scroll_layer_id_;
   bool currently_scrolling_;
   bool scroll_gesture_has_scrolled_;
   base::CancelableClosure delayed_scrollbar_fade_;
diff --git a/cc/animation/scrollbar_animation_controller_linear_fade.cc b/cc/animation/scrollbar_animation_controller_linear_fade.cc
index 72fdd58..13b9754 100644
--- a/cc/animation/scrollbar_animation_controller_linear_fade.cc
+++ b/cc/animation/scrollbar_animation_controller_linear_fade.cc
@@ -12,28 +12,27 @@
 
 scoped_ptr<ScrollbarAnimationControllerLinearFade>
 ScrollbarAnimationControllerLinearFade::Create(
-    LayerImpl* scroll_layer,
+    int scroll_layer_id,
     ScrollbarAnimationControllerClient* client,
     base::TimeDelta delay_before_starting,
     base::TimeDelta resize_delay_before_starting,
     base::TimeDelta duration) {
   return make_scoped_ptr(new ScrollbarAnimationControllerLinearFade(
-      scroll_layer, client, delay_before_starting, resize_delay_before_starting,
-      duration));
+      scroll_layer_id, client, delay_before_starting,
+      resize_delay_before_starting, duration));
 }
 
 ScrollbarAnimationControllerLinearFade::ScrollbarAnimationControllerLinearFade(
-    LayerImpl* scroll_layer,
+    int scroll_layer_id,
     ScrollbarAnimationControllerClient* client,
     base::TimeDelta delay_before_starting,
     base::TimeDelta resize_delay_before_starting,
     base::TimeDelta duration)
-    : ScrollbarAnimationController(scroll_layer,
+    : ScrollbarAnimationController(scroll_layer_id,
                                    client,
                                    delay_before_starting,
                                    resize_delay_before_starting,
-                                   duration) {
-}
+                                   duration) {}
 
 ScrollbarAnimationControllerLinearFade::
     ~ScrollbarAnimationControllerLinearFade() {
@@ -53,17 +52,11 @@
 
 void ScrollbarAnimationControllerLinearFade::ApplyOpacityToScrollbars(
     float opacity) {
-  if (!scroll_layer_->scrollbars())
-    return;
-
-  LayerImpl::ScrollbarSet* scrollbars = scroll_layer_->scrollbars();
-  for (LayerImpl::ScrollbarSet::iterator it = scrollbars->begin();
-       it != scrollbars->end(); ++it) {
-    ScrollbarLayerImplBase* scrollbar = *it;
-
-    if (scrollbar->is_overlay_scrollbar())
-      scrollbar->OnOpacityAnimated(scrollbar->CanScrollOrientation() ? opacity
-                                                                     : 0);
+  for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) {
+    if (!scrollbar->is_overlay_scrollbar())
+      continue;
+    scrollbar->OnOpacityAnimated(scrollbar->CanScrollOrientation() ? opacity
+                                                                   : 0);
   }
 }
 
diff --git a/cc/animation/scrollbar_animation_controller_linear_fade.h b/cc/animation/scrollbar_animation_controller_linear_fade.h
index 05940a0..f6f40112 100644
--- a/cc/animation/scrollbar_animation_controller_linear_fade.h
+++ b/cc/animation/scrollbar_animation_controller_linear_fade.h
@@ -16,7 +16,7 @@
     : public ScrollbarAnimationController {
  public:
   static scoped_ptr<ScrollbarAnimationControllerLinearFade> Create(
-      LayerImpl* scroll_layer,
+      int scroll_layer_id,
       ScrollbarAnimationControllerClient* client,
       base::TimeDelta delay_before_starting,
       base::TimeDelta resize_delay_before_starting,
@@ -28,7 +28,7 @@
 
  protected:
   ScrollbarAnimationControllerLinearFade(
-      LayerImpl* scroll_layer,
+      int scroll_layer_id,
       ScrollbarAnimationControllerClient* client,
       base::TimeDelta delay_before_starting,
       base::TimeDelta resize_delay_before_starting,
diff --git a/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc b/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
index 6d2d3e8..2c8f993 100644
--- a/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
+++ b/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
@@ -21,16 +21,10 @@
       public ScrollbarAnimationControllerClient {
  public:
   ScrollbarAnimationControllerLinearFadeTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
+        did_request_redraw_(false),
+        did_request_animate_(false) {}
 
-  void StartAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override {
-    is_animating_ = true;
-  }
-  void StopAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override {
-    is_animating_ = false;
-  }
   void PostDelayedScrollbarAnimationTask(const base::Closure& start_fade,
                                          base::TimeDelta delay) override {
     start_fade_ = start_fade;
@@ -39,6 +33,12 @@
   void SetNeedsRedrawForScrollbarAnimation() override {
     did_request_redraw_ = true;
   }
+  void SetNeedsAnimateForScrollbarAnimation() override {
+    did_request_animate_ = true;
+  }
+  ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override {
+    return host_impl_.ScrollbarsFor(scroll_layer_id);
+  }
 
  protected:
   void SetUp() override {
@@ -62,13 +62,12 @@
     LayerImpl* scroll_layer_ptr = scroll_layer.get();
     clip_layer_->AddChild(scroll_layer.Pass());
 
-    scrollbar_layer_->SetScrollLayerAndClipLayerByIds(scroll_layer_ptr->id(),
-                                                      clip_layer_->id());
+    scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id());
     clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
 
     scrollbar_controller_ = ScrollbarAnimationControllerLinearFade::Create(
-        scroll_layer_ptr, this, base::TimeDelta::FromSeconds(2),
+        scroll_layer_ptr->id(), this, base::TimeDelta::FromSeconds(2),
         base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(3));
   }
 
@@ -84,8 +83,8 @@
 
   base::Closure start_fade_;
   base::TimeDelta delay_;
-  bool is_animating_;
   bool did_request_redraw_;
+  bool did_request_animate_;
 };
 
 class VerticalScrollbarAnimationControllerLinearFadeTest
@@ -263,8 +262,10 @@
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollBegin();
+  EXPECT_FALSE(did_request_animate_);
 
   scrollbar_controller_->DidScrollUpdate(false);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   EXPECT_TRUE(start_fade_.Equals(base::Closure()));
@@ -272,23 +273,30 @@
   time += base::TimeDelta::FromSeconds(100);
 
   scrollbar_controller_->Animate(time);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
   scrollbar_controller_->DidScrollEnd();
+  EXPECT_FALSE(did_request_animate_);
   start_fade_.Run();
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
 
   time += base::TimeDelta::FromSeconds(2);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
@@ -298,84 +306,101 @@
   scrollbar_controller_->DidScrollEnd();
 
   start_fade_.Run();
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
 
   time += base::TimeDelta::FromSeconds(2);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
-  EXPECT_FALSE(is_animating_);
 }
 
 TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) {
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate(false);
+  EXPECT_FALSE(did_request_animate_);
 
   start_fade_.Run();
-  EXPECT_TRUE(is_animating_);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
   scrollbar_controller_->DidScrollUpdate(false);
+  EXPECT_FALSE(did_request_animate_);
 
   start_fade_.Run();
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   time += base::TimeDelta::FromSeconds(2);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate(false);
   start_fade_.Run();
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
-  EXPECT_FALSE(is_animating_);
 }
 
 TEST_F(ScrollbarAnimationControllerLinearFadeTest,
@@ -384,31 +409,37 @@
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate(false);
   start_fade_.Run();
-  EXPECT_TRUE(is_animating_);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   scrollbar_controller_->DidScrollBegin();
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   scrollbar_controller_->DidScrollEnd();
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
-  EXPECT_FALSE(is_animating_);
 }
 
 TEST_F(ScrollbarAnimationControllerLinearFadeTest,
@@ -416,30 +447,38 @@
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate(false);
+  EXPECT_FALSE(did_request_animate_);
   start_fade_.Run();
-  EXPECT_TRUE(is_animating_);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   scrollbar_controller_->DidScrollBegin();
   EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
-  EXPECT_TRUE(is_animating_);
   scrollbar_controller_->Animate(time);
+  EXPECT_TRUE(did_request_animate_);
+  did_request_animate_ = false;
   EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate(false);
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity());
 
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollEnd();
+  EXPECT_FALSE(did_request_animate_);
   EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity());
 }
 
diff --git a/cc/animation/scrollbar_animation_controller_thinning.cc b/cc/animation/scrollbar_animation_controller_thinning.cc
index 6ced8098..d8a2d6d 100644
--- a/cc/animation/scrollbar_animation_controller_thinning.cc
+++ b/cc/animation/scrollbar_animation_controller_thinning.cc
@@ -19,23 +19,23 @@
 
 scoped_ptr<ScrollbarAnimationControllerThinning>
 ScrollbarAnimationControllerThinning::Create(
-    LayerImpl* scroll_layer,
+    int scroll_layer_id,
     ScrollbarAnimationControllerClient* client,
     base::TimeDelta delay_before_starting,
     base::TimeDelta resize_delay_before_starting,
     base::TimeDelta duration) {
   return make_scoped_ptr(new ScrollbarAnimationControllerThinning(
-      scroll_layer, client, delay_before_starting, resize_delay_before_starting,
-      duration));
+      scroll_layer_id, client, delay_before_starting,
+      resize_delay_before_starting, duration));
 }
 
 ScrollbarAnimationControllerThinning::ScrollbarAnimationControllerThinning(
-    LayerImpl* scroll_layer,
+    int scroll_layer_id,
     ScrollbarAnimationControllerClient* client,
     base::TimeDelta delay_before_starting,
     base::TimeDelta resize_delay_before_starting,
     base::TimeDelta duration)
-    : ScrollbarAnimationController(scroll_layer,
+    : ScrollbarAnimationController(scroll_layer_id,
                                    client,
                                    delay_before_starting,
                                    resize_delay_before_starting,
@@ -135,24 +135,18 @@
 void ScrollbarAnimationControllerThinning::ApplyOpacityAndThumbThicknessScale(
     float opacity,
     float thumb_thickness_scale) {
-  if (!scroll_layer_->scrollbars())
-    return;
+  for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) {
+    if (!scrollbar->is_overlay_scrollbar())
+      continue;
+    float effective_opacity =
+        scrollbar->CanScrollOrientation()
+            ? AdjustScale(opacity, scrollbar->opacity(), opacity_change_)
+            : 0;
 
-  LayerImpl::ScrollbarSet* scrollbars = scroll_layer_->scrollbars();
-  for (LayerImpl::ScrollbarSet::iterator it = scrollbars->begin();
-       it != scrollbars->end(); ++it) {
-    ScrollbarLayerImplBase* scrollbar = *it;
-    if (scrollbar->is_overlay_scrollbar()) {
-      float effectiveOpacity =
-          scrollbar->CanScrollOrientation()
-              ? AdjustScale(opacity, scrollbar->opacity(), opacity_change_)
-              : 0;
-
-      scrollbar->OnOpacityAnimated(effectiveOpacity);
-      scrollbar->SetThumbThicknessScaleFactor(AdjustScale(
-          thumb_thickness_scale, scrollbar->thumb_thickness_scale_factor(),
-          thickness_change_));
-    }
+    scrollbar->OnOpacityAnimated(effective_opacity);
+    scrollbar->SetThumbThicknessScaleFactor(AdjustScale(
+        thumb_thickness_scale, scrollbar->thumb_thickness_scale_factor(),
+        thickness_change_));
   }
 }
 
diff --git a/cc/animation/scrollbar_animation_controller_thinning.h b/cc/animation/scrollbar_animation_controller_thinning.h
index f22d94c..44362bd 100644
--- a/cc/animation/scrollbar_animation_controller_thinning.h
+++ b/cc/animation/scrollbar_animation_controller_thinning.h
@@ -18,7 +18,7 @@
     : public ScrollbarAnimationController {
  public:
   static scoped_ptr<ScrollbarAnimationControllerThinning> Create(
-      LayerImpl* scroll_layer,
+      int scroll_layer_id,
       ScrollbarAnimationControllerClient* client,
       base::TimeDelta delay_before_starting,
       base::TimeDelta resize_delay_before_starting,
@@ -38,7 +38,7 @@
 
  protected:
   ScrollbarAnimationControllerThinning(
-      LayerImpl* scroll_layer,
+      int scroll_layer_id,
       ScrollbarAnimationControllerClient* client,
       base::TimeDelta delay_before_starting,
       base::TimeDelta resize_delay_before_starting,
diff --git a/cc/animation/scrollbar_animation_controller_thinning_unittest.cc b/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
index 04f81d3..3c5ff361 100644
--- a/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
@@ -23,14 +23,6 @@
   ScrollbarAnimationControllerThinningTest()
       : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
 
-  void StartAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override {
-    is_animating_ = true;
-  }
-  void StopAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override {
-    is_animating_ = false;
-  }
   void PostDelayedScrollbarAnimationTask(const base::Closure& start_fade,
                                          base::TimeDelta delay) override {
     start_fade_ = start_fade;
@@ -39,6 +31,12 @@
   void SetNeedsRedrawForScrollbarAnimation() override {
     did_request_redraw_ = true;
   }
+  void SetNeedsAnimateForScrollbarAnimation() override {
+    did_request_animate_ = true;
+  }
+  ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override {
+    return host_impl_.ScrollbarsFor(scroll_layer_id);
+  }
 
  protected:
   void SetUp() override {
@@ -63,13 +61,12 @@
                                              kIsLeftSideVerticalScrollbar,
                                              kIsOverlayScrollbar);
 
-    scrollbar_layer_->SetScrollLayerAndClipLayerByIds(scroll_layer_ptr->id(),
-                                                      clip_layer_->id());
+    scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id());
     clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
 
     scrollbar_controller_ = ScrollbarAnimationControllerThinning::Create(
-        scroll_layer_ptr, this, base::TimeDelta::FromSeconds(2),
+        scroll_layer_ptr->id(), this, base::TimeDelta::FromSeconds(2),
         base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(3));
   }
 
@@ -83,8 +80,8 @@
 
   base::Closure start_fade_;
   base::TimeDelta delay_;
-  bool is_animating_;
   bool did_request_redraw_;
+  bool did_request_animate_;
 };
 
 // Check initialization of scrollbar.
diff --git a/cc/blink/web_scrollbar_layer_impl.cc b/cc/blink/web_scrollbar_layer_impl.cc
index a6813cb..8248149 100644
--- a/cc/blink/web_scrollbar_layer_impl.cc
+++ b/cc/blink/web_scrollbar_layer_impl.cc
@@ -67,11 +67,4 @@
       scroll_layer ? scroll_layer->id() : cc::Layer::INVALID_ID);
 }
 
-void WebScrollbarLayerImpl::setClipLayer(blink::WebLayer* layer) {
-  cc::Layer* clip_layer =
-      layer ? static_cast<WebLayerImpl*>(layer)->layer() : 0;
-  layer_->layer()->ToScrollbarLayer()->SetClipLayer(
-      clip_layer ? clip_layer->id() : cc::Layer::INVALID_ID);
-}
-
 }  // namespace cc_blink
diff --git a/cc/blink/web_scrollbar_layer_impl.h b/cc/blink/web_scrollbar_layer_impl.h
index d4963d9..734e069 100644
--- a/cc/blink/web_scrollbar_layer_impl.h
+++ b/cc/blink/web_scrollbar_layer_impl.h
@@ -35,7 +35,6 @@
   // blink::WebScrollbarLayer implementation.
   blink::WebLayer* layer() override;
   void setScrollLayer(blink::WebLayer* layer) override;
-  void setClipLayer(blink::WebLayer* layer) override;
 
  private:
   scoped_ptr<WebLayerImpl> layer_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index afacef8..e71bc79 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -10,7 +10,6 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/animation/animation_registrar.h"
-#include "cc/animation/scrollbar_animation_controller.h"
 #include "cc/base/math_util.h"
 #include "cc/base/simple_enclosed_region.h"
 #include "cc/debug/debug_colors.h"
@@ -18,8 +17,8 @@
 #include "cc/debug/micro_benchmark_impl.h"
 #include "cc/debug/traced_value.h"
 #include "cc/input/scroll_state.h"
+#include "cc/layers/layer.h"
 #include "cc/layers/layer_utils.h"
-#include "cc/layers/painted_scrollbar_layer_impl.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/quads/debug_border_draw_quad.h"
 #include "cc/quads/render_pass.h"
@@ -50,7 +49,7 @@
       layer_id_(id),
       layer_tree_impl_(tree_impl),
       scroll_offset_(scroll_offset),
-      scroll_clip_layer_(nullptr),
+      scroll_clip_layer_id_(Layer::INVALID_ID),
       should_scroll_on_main_thread_(false),
       have_wheel_event_handlers_(false),
       have_scroll_event_handlers_(false),
@@ -118,6 +117,7 @@
 
   if (!copy_requests_.empty() && layer_tree_impl_->IsActiveTree())
     layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this);
+  layer_tree_impl_->UnregisterScrollLayer(this);
   layer_tree_impl_->UnregisterLayer(this);
 
   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
@@ -459,7 +459,20 @@
 }
 
 void LayerImpl::SetScrollClipLayer(int scroll_clip_layer_id) {
-  scroll_clip_layer_ = layer_tree_impl()->LayerById(scroll_clip_layer_id);
+  if (scroll_clip_layer_id_ == scroll_clip_layer_id)
+    return;
+
+  layer_tree_impl()->UnregisterScrollLayer(this);
+  scroll_clip_layer_id_ = scroll_clip_layer_id;
+  layer_tree_impl()->RegisterScrollLayer(this);
+}
+
+LayerImpl* LayerImpl::scroll_clip_layer() const {
+  return layer_tree_impl()->LayerById(scroll_clip_layer_id_);
+}
+
+bool LayerImpl::scrollable() const {
+  return scroll_clip_layer_id_ != Layer::INVALID_ID;
 }
 
 bool LayerImpl::user_scrollable(ScrollbarOrientation orientation) const {
@@ -575,8 +588,7 @@
   layer->SetUseParentBackfaceVisibility(use_parent_backface_visibility_);
   layer->SetTransformAndInvertibility(transform_, transform_is_invertible_);
 
-  layer->SetScrollClipLayer(scroll_clip_layer_ ? scroll_clip_layer_->id()
-                                               : Layer::INVALID_ID);
+  layer->SetScrollClipLayer(scroll_clip_layer_id_);
   layer->set_user_scrollable_horizontal(user_scrollable_horizontal_);
   layer->set_user_scrollable_vertical(user_scrollable_vertical_);
 
@@ -661,15 +673,12 @@
 }
 
 gfx::Vector2dF LayerImpl::FixedContainerSizeDelta() const {
-  if (!scroll_clip_layer_)
+  LayerImpl* scroll_clip_layer =
+      layer_tree_impl()->LayerById(scroll_clip_layer_id_);
+  if (!scroll_clip_layer)
     return gfx::Vector2dF();
 
-  gfx::Vector2dF delta_from_scroll = scroll_clip_layer_->bounds_delta();
-
-  // In virtual-viewport mode, we don't need to compensate for pinch zoom or
-  // scale since the fixed container is the outer viewport, which sits below
-  // the page scale.
-  return delta_from_scroll;
+  return scroll_clip_layer->bounds_delta();
 }
 
 base::DictionaryValue* LayerImpl::LayerTreeAsJson() const {
@@ -874,6 +883,8 @@
 void LayerImpl::UpdatePropertyTreeOpacity() {
   if (effect_tree_index_ != -1) {
     EffectTree& effect_tree = layer_tree_impl()->property_trees()->effect_tree;
+    if (effect_tree_index_ >= static_cast<int>(effect_tree.size()))
+      return;
     EffectNode* node = effect_tree.Node(effect_tree_index_);
     // A LayerImpl's own current state is insufficient for determining whether
     // it owns an OpacityNode, since this depends on the state of the
@@ -958,7 +969,7 @@
 
   bounds_ = bounds;
 
-  ScrollbarParametersDidChange(true);
+  layer_tree_impl()->DidUpdateScrollState(id());
   if (masks_to_bounds())
     NoteLayerPropertyChangedForSubtree();
   else
@@ -979,7 +990,7 @@
   else if (this == layer_tree_impl()->OuterViewportContainerLayer())
     transform_tree.SetOuterViewportBoundsDelta(bounds_delta);
 
-  ScrollbarParametersDidChange(true);
+  layer_tree_impl()->DidUpdateScrollState(id());
 
   if (masks_to_bounds()) {
     // If layer is clipping, then update the clip node using the new bounds.
@@ -1479,8 +1490,8 @@
 void LayerImpl::DidUpdateScrollOffset() {
   DCHECK(scroll_offset_);
 
+  layer_tree_impl()->DidUpdateScrollState(id());
   NoteLayerPropertyChangedForSubtree();
-  ScrollbarParametersDidChange(false);
 
   UpdatePropertyTreeScrollOffset();
 
@@ -1514,7 +1525,9 @@
 }
 
 gfx::ScrollOffset LayerImpl::MaxScrollOffset() const {
-  if (!scroll_clip_layer_ || bounds().IsEmpty())
+  LayerImpl* scroll_clip_layer =
+      layer_tree_impl()->LayerById(scroll_clip_layer_id_);
+  if (!scroll_clip_layer || bounds().IsEmpty())
     return gfx::ScrollOffset();
 
   LayerImpl const* page_scale_layer = layer_tree_impl()->PageScaleLayer();
@@ -1524,7 +1537,7 @@
 
   float scale_factor = 1.f;
   for (LayerImpl const* current_layer = this;
-       current_layer != scroll_clip_layer_->parent();
+       current_layer != scroll_clip_layer->parent();
        current_layer = current_layer->parent()) {
     if (current_layer == page_scale_layer)
       scale_factor = layer_tree_impl()->current_page_scale_factor();
@@ -1536,8 +1549,8 @@
                                std::floor(scaled_scroll_bounds.height()));
 
   gfx::ScrollOffset max_offset(
-      scaled_scroll_bounds.width() - scroll_clip_layer_->bounds().width(),
-      scaled_scroll_bounds.height() - scroll_clip_layer_->bounds().height());
+      scaled_scroll_bounds.width() - scroll_clip_layer->bounds().width(),
+      scaled_scroll_bounds.height() - scroll_clip_layer->bounds().height());
   // We need the final scroll offset to be in CSS coords.
   max_offset.Scale(1 / scale_factor);
   max_offset.SetToMax(gfx::ScrollOffset());
@@ -1560,147 +1573,6 @@
   return delta;
 }
 
-void LayerImpl::SetScrollbarPosition(ScrollbarLayerImplBase* scrollbar_layer,
-                                     LayerImpl* scrollbar_clip_layer,
-                                     bool on_resize) const {
-  DCHECK(scrollbar_layer);
-  LayerImpl* page_scale_layer = layer_tree_impl()->PageScaleLayer();
-
-  DCHECK(this != page_scale_layer);
-  DCHECK(scrollbar_clip_layer);
-  gfx::RectF clip_rect(gfx::PointF(),
-                       scrollbar_clip_layer->BoundsForScrolling());
-
-  // See comment in MaxScrollOffset() regarding the use of the content layer
-  // bounds here.
-  gfx::RectF scroll_rect(gfx::PointF(), BoundsForScrolling());
-
-  if (scroll_rect.size().IsEmpty())
-    return;
-
-  gfx::ScrollOffset current_offset;
-  for (LayerImpl const* current_layer = this;
-       current_layer != scrollbar_clip_layer->parent();
-       current_layer = current_layer->parent()) {
-    current_offset += current_layer->CurrentScrollOffset();
-    if (current_layer == page_scale_layer) {
-      float scale_factor = layer_tree_impl()->current_page_scale_factor();
-      current_offset.Scale(scale_factor);
-      scroll_rect.Scale(scale_factor);
-    }
-  }
-
-  bool scrollbar_needs_animation = false;
-  scrollbar_needs_animation |= scrollbar_layer->SetVerticalAdjust(
-      scrollbar_clip_layer->bounds_delta().y());
-  if (scrollbar_layer->orientation() == HORIZONTAL) {
-    float visible_ratio = clip_rect.width() / scroll_rect.width();
-    scrollbar_needs_animation |=
-        scrollbar_layer->SetCurrentPos(current_offset.x());
-    scrollbar_needs_animation |=
-        scrollbar_layer->SetMaximum(scroll_rect.width() - clip_rect.width());
-    scrollbar_needs_animation |=
-        scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
-  } else {
-    float visible_ratio = clip_rect.height() / scroll_rect.height();
-    bool y_offset_did_change =
-        scrollbar_layer->SetCurrentPos(current_offset.y());
-    scrollbar_needs_animation |= y_offset_did_change;
-    scrollbar_needs_animation |=
-        scrollbar_layer->SetMaximum(scroll_rect.height() - clip_rect.height());
-    scrollbar_needs_animation |=
-        scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
-    if (y_offset_did_change && layer_tree_impl()->IsActiveTree() &&
-        this == layer_tree_impl()->OuterViewportScrollLayer()) {
-      TRACE_COUNTER_ID1("cc", "scroll_offset_y", this->id(),
-                        current_offset.y());
-    }
-  }
-  if (scrollbar_needs_animation) {
-    layer_tree_impl()->set_needs_update_draw_properties();
-    // TODO(wjmaclean) The scrollbar animator for the pinch-zoom scrollbars
-    // should activate for every scroll on the main frame, not just the
-    // scrolls that move the pinch virtual viewport (i.e. trigger from
-    // either inner or outer viewport).
-    if (scrollbar_animation_controller_) {
-      // Non-overlay scrollbars shouldn't trigger animations.
-      if (scrollbar_layer->is_overlay_scrollbar())
-        scrollbar_animation_controller_->DidScrollUpdate(on_resize);
-    }
-  }
-}
-
-void LayerImpl::DidBecomeActive() {
-  if (layer_tree_impl_->settings().scrollbar_animator ==
-      LayerTreeSettings::NO_ANIMATOR) {
-    return;
-  }
-
-  bool need_scrollbar_animation_controller = scrollable() && scrollbars_;
-  if (!need_scrollbar_animation_controller) {
-    scrollbar_animation_controller_ = nullptr;
-    return;
-  }
-
-  if (scrollbar_animation_controller_)
-    return;
-
-  scrollbar_animation_controller_ =
-      layer_tree_impl_->CreateScrollbarAnimationController(this);
-}
-
-void LayerImpl::ClearScrollbars() {
-  if (!scrollbars_)
-    return;
-
-  scrollbars_.reset(nullptr);
-}
-
-void LayerImpl::AddScrollbar(ScrollbarLayerImplBase* layer) {
-  DCHECK(layer);
-  DCHECK(!scrollbars_ || scrollbars_->find(layer) == scrollbars_->end());
-  if (!scrollbars_)
-    scrollbars_.reset(new ScrollbarSet());
-
-  scrollbars_->insert(layer);
-}
-
-void LayerImpl::RemoveScrollbar(ScrollbarLayerImplBase* layer) {
-  DCHECK(scrollbars_);
-  DCHECK(layer);
-  DCHECK(scrollbars_->find(layer) != scrollbars_->end());
-
-  scrollbars_->erase(layer);
-  if (scrollbars_->empty())
-    scrollbars_ = nullptr;
-}
-
-bool LayerImpl::HasScrollbar(ScrollbarOrientation orientation) const {
-  if (!scrollbars_)
-    return false;
-
-  for (ScrollbarSet::iterator it = scrollbars_->begin();
-       it != scrollbars_->end();
-       ++it)
-    if ((*it)->orientation() == orientation)
-      return true;
-
-  return false;
-}
-
-void LayerImpl::ScrollbarParametersDidChange(bool on_resize) {
-  if (!scrollbars_)
-    return;
-
-  for (ScrollbarSet::iterator it = scrollbars_->begin();
-       it != scrollbars_->end();
-       ++it) {
-    bool is_scroll_layer = (*it)->ScrollLayerId() == layer_id_;
-    bool scroll_layer_resized = is_scroll_layer && on_resize;
-    (*it)->ScrollbarParametersDidChange(scroll_layer_resized);
-  }
-}
-
 void LayerImpl::SetNeedsPushProperties() {
   if (needs_push_properties_)
     return;
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 4e949809..543691fb 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -23,7 +23,6 @@
 #include "cc/base/synced_property.h"
 #include "cc/debug/frame_timing_request.h"
 #include "cc/input/input_handler.h"
-#include "cc/input/scrollbar.h"
 #include "cc/layers/draw_properties.h"
 #include "cc/layers/layer_lists.h"
 #include "cc/layers/layer_position_constraint.h"
@@ -62,7 +61,6 @@
 class RenderPass;
 class RenderPassId;
 class Renderer;
-class ScrollbarAnimationController;
 class ScrollbarLayerImplBase;
 class SimpleEnclosedRegion;
 class Tile;
@@ -451,9 +449,6 @@
   gfx::ScrollOffset MaxScrollOffset() const;
   gfx::ScrollOffset ClampScrollOffsetToLimits(gfx::ScrollOffset offset) const;
   gfx::Vector2dF ClampScrollToMaxScrollOffset();
-  void SetScrollbarPosition(ScrollbarLayerImplBase* scrollbar_layer,
-                            LayerImpl* scrollbar_clip_layer,
-                            bool on_resize) const;
   void SetScrollCompensationAdjustment(const gfx::Vector2dF& scroll_offset) {
     scroll_compensation_adjustment_ = scroll_offset;
   }
@@ -466,8 +461,9 @@
   gfx::Vector2dF ScrollBy(const gfx::Vector2dF& scroll);
 
   void SetScrollClipLayer(int scroll_clip_layer_id);
-  LayerImpl* scroll_clip_layer() const { return scroll_clip_layer_; }
-  bool scrollable() const { return !!scroll_clip_layer_; }
+  int scroll_clip_layer_id() const { return scroll_clip_layer_id_; }
+  LayerImpl* scroll_clip_layer() const;
+  bool scrollable() const;
 
   void set_user_scrollable_horizontal(bool scrollable) {
     user_scrollable_horizontal_ = scrollable;
@@ -582,7 +578,7 @@
 
   virtual SimpleEnclosedRegion VisibleOpaqueRegion() const;
 
-  virtual void DidBecomeActive();
+  virtual void DidBecomeActive() {}
 
   virtual void DidBeginTracing();
 
@@ -594,19 +590,8 @@
   // ReleaseResources call.
   virtual void RecreateResources();
 
-  ScrollbarAnimationController* scrollbar_animation_controller() const {
-    return scrollbar_animation_controller_.get();
-  }
-
-  typedef std::set<ScrollbarLayerImplBase*> ScrollbarSet;
-  ScrollbarSet* scrollbars() { return scrollbars_.get(); }
-  void ClearScrollbars();
-  void AddScrollbar(ScrollbarLayerImplBase* layer);
-  void RemoveScrollbar(ScrollbarLayerImplBase* layer);
-  bool HasScrollbar(ScrollbarOrientation orientation) const;
-  void ScrollbarParametersDidChange(bool on_resize);
   int clip_height() {
-    return scroll_clip_layer_ ? scroll_clip_layer_->bounds().height() : 0;
+    return scroll_clip_layer() ? scroll_clip_layer()->bounds().height() : 0;
   }
 
   virtual skia::RefPtr<SkPicture> GetPicture();
@@ -763,7 +748,7 @@
   // Properties synchronized from the associated Layer.
   gfx::Point3F transform_origin_;
   gfx::Size bounds_;
-  LayerImpl* scroll_clip_layer_;
+  int scroll_clip_layer_id_;
 
   gfx::Vector2dF offset_to_transform_parent_;
 
@@ -863,11 +848,6 @@
   // Manages animations for this layer.
   scoped_refptr<LayerAnimationController> layer_animation_controller_;
 
-  // Manages scrollbars for this layer
-  scoped_ptr<ScrollbarAnimationController> scrollbar_animation_controller_;
-
-  scoped_ptr<ScrollbarSet> scrollbars_;
-
   ScopedPtrVector<CopyOutputRequest> copy_requests_;
 
   // Group of properties that need to be computed based on the layer tree
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index 315544ad..7089bbe 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -578,159 +578,5 @@
                    pending_layer->CurrentScrollOffset());
 }
 
-TEST_F(LayerImplScrollTest, SetNewScrollbarParameters) {
-  gfx::ScrollOffset scroll_offset(10, 5);
-  layer()->PushScrollOffsetFromMainThread(scroll_offset);
-
-  scoped_ptr<PaintedScrollbarLayerImpl> vertical_scrollbar(
-      PaintedScrollbarLayerImpl::Create(tree(), 100, VERTICAL));
-  vertical_scrollbar->SetScrollLayerAndClipLayerByIds(
-      layer()->id(), tree()->root_layer()->id());
-
-  int expected_vertical_maximum =
-      layer()->bounds().height() - tree()->root_layer()->bounds().height();
-  EXPECT_EQ(expected_vertical_maximum, vertical_scrollbar->maximum());
-  EXPECT_EQ(scroll_offset.y(), vertical_scrollbar->current_pos());
-
-  scoped_ptr<PaintedScrollbarLayerImpl> horizontal_scrollbar(
-      PaintedScrollbarLayerImpl::Create(tree(), 101, HORIZONTAL));
-  horizontal_scrollbar->SetScrollLayerAndClipLayerByIds(
-      layer()->id(), tree()->root_layer()->id());
-
-  int expected_horizontal_maximum =
-      layer()->bounds().width() - tree()->root_layer()->bounds().width();
-  EXPECT_EQ(expected_horizontal_maximum, horizontal_scrollbar->maximum());
-  EXPECT_EQ(scroll_offset.x(), horizontal_scrollbar->current_pos());
-}
-
-class LayerImplScrollbarSyncTest : public testing::Test {
- public:
-  enum {
-    ROOT = 1,
-    IV_CLIP = 2,
-    PAGE = 3,
-    IV_SCROLL = 4,
-    SCROLLBAR = 5,
-    OLD_ROOT = 6,
-    OV_CLIP = 7,
-    OV_SCROLL = 8,
-  };
-  enum TreeID {
-    PENDING,
-    ACTIVE
-  };
-
-  LayerImplScrollbarSyncTest()
-      : host_impl_(settings(),
-                   &proxy_,
-                   &shared_bitmap_manager_,
-                   &task_graph_runner_) {
-    host_impl_.CreatePendingTree();
-
-    CreateLayers(host_impl_.pending_tree());
-    CreateLayers(host_impl_.active_tree());
-  }
-
-  void CreateLayers(LayerTreeImpl * tree) {
-    tree->SetRootLayer(LayerImpl::Create(tree, ROOT));
-    LayerImpl * root = tree->root_layer();
-    ASSERT_TRUE(root != nullptr);
-
-    int hierarchy[] = {IV_CLIP, PAGE, IV_SCROLL, OLD_ROOT, OV_CLIP, OV_SCROLL};
-    LayerImpl * parent = root;
-    for (int child_id : hierarchy) {
-      parent->AddChild(LayerImpl::Create(tree, child_id));
-      parent = tree->LayerById(child_id);
-      ASSERT_TRUE(parent != nullptr);
-    }
-
-    root->AddChild(
-        SolidColorScrollbarLayerImpl::Create(tree, SCROLLBAR, HORIZONTAL,
-                                             5, 5, false, true));
-  }
-
-  LayerImpl* layer(int id, TreeID tree_id) {
-    LayerTreeImpl* tree =
-        ((tree_id == PENDING) ?
-         host_impl_.pending_tree() : host_impl_.active_tree());
-
-    assert(tree);
-    return tree->LayerById(id);
-  }
-
-  bool LayerHasScrollbar(int id, TreeID tree_id) {
-    return layer(id, tree_id)->HasScrollbar(HORIZONTAL);
-  }
-
-  ScrollbarLayerImplBase* pending_scrollbar() {
-    LayerImpl* layer_impl = layer(SCROLLBAR, PENDING);
-    assert(layer_impl);
-    return layer_impl->ToScrollbarLayer();
-  }
-
-  LayerImpl* pending_root() {
-    LayerImpl * result = layer(ROOT, PENDING);
-    assert(result);
-    return result;
-  }
-
-  LayerImpl* active_root() {
-    LayerImpl * result = layer(ROOT, ACTIVE);
-    assert(result);
-    return result;
-  }
-
-  LayerTreeSettings settings() {
-    LayerTreeSettings settings;
-    return settings;
-  }
-
- private:
-  FakeImplProxy proxy_;
-  TestSharedBitmapManager shared_bitmap_manager_;
-  TestTaskGraphRunner task_graph_runner_;
-  FakeLayerTreeHostImpl host_impl_;
-};
-
-TEST_F(LayerImplScrollbarSyncTest, LayerImplBecomesScrollable) {
-  // In the beginning IV_SCROLL layer is not scrollable.
-  ASSERT_FALSE(layer(IV_SCROLL, PENDING)->scrollable());
-
-  // For pinch virtual viewport the clip layer is the inner viewport
-  // clip layer (IV_CLIP) and the scroll one is the outer viewport
-  // scroll layer (OV_SCROLL).
-  pending_scrollbar()->SetScrollLayerAndClipLayerByIds(OV_SCROLL, IV_CLIP);
-
-  ASSERT_TRUE(LayerHasScrollbar(OV_SCROLL, PENDING));
-  ASSERT_TRUE(LayerHasScrollbar(IV_CLIP, PENDING));
-
-  // Synchronize with the active tree.
-  TreeSynchronizer::PushProperties(pending_root(), active_root());
-
-  ASSERT_TRUE(LayerHasScrollbar(OV_SCROLL, ACTIVE));
-  ASSERT_TRUE(LayerHasScrollbar(IV_CLIP, ACTIVE));
-
-  // Make IV_SCROLL layer scrollable.
-  layer(IV_SCROLL, PENDING)->SetScrollClipLayer(IV_CLIP);
-  layer(IV_SCROLL, PENDING)->SetNeedsPushProperties();
-  ASSERT_TRUE(layer(IV_SCROLL, PENDING)->scrollable());
-
-  pending_scrollbar()->SetScrollLayerAndClipLayerByIds(OV_SCROLL, IV_CLIP);
-
-  // Now IV_CLIP layer should also receive the scrollbar.
-  ASSERT_TRUE(LayerHasScrollbar(OV_SCROLL, PENDING));
-  ASSERT_TRUE(LayerHasScrollbar(IV_CLIP, PENDING));
-  ASSERT_TRUE(LayerHasScrollbar(IV_SCROLL, PENDING));
-
-  // Synchronize with the active tree.
-  TreeSynchronizer::PushProperties(pending_root(), active_root());
-
-  ASSERT_TRUE(layer(IV_SCROLL, ACTIVE)->scrollable());
-
-  ASSERT_TRUE(LayerHasScrollbar(OV_SCROLL, ACTIVE));
-  ASSERT_TRUE(LayerHasScrollbar(IV_CLIP, ACTIVE));
-  ASSERT_TRUE(LayerHasScrollbar(IV_SCROLL, ACTIVE));
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 67f83c65..7a65992 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -45,7 +45,6 @@
     : Layer(settings),
       scrollbar_(scrollbar.Pass()),
       scroll_layer_id_(scroll_layer_id),
-      clip_layer_id_(Layer::INVALID_ID),
       internal_contents_scale_(1.f),
       thumb_thickness_(scrollbar_->ThumbThickness()),
       thumb_length_(scrollbar_->ThumbLength()),
@@ -69,14 +68,6 @@
   SetNeedsFullTreeSync();
 }
 
-void PaintedScrollbarLayer::SetClipLayer(int layer_id) {
-  if (layer_id == clip_layer_id_)
-    return;
-
-  clip_layer_id_ = layer_id;
-  SetNeedsFullTreeSync();
-}
-
 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
   return scrollbar_->IsOverlay();
 }
@@ -108,11 +99,10 @@
 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
   Layer::PushPropertiesTo(layer);
 
-  PushScrollClipPropertiesTo(layer);
-
   PaintedScrollbarLayerImpl* scrollbar_layer =
       static_cast<PaintedScrollbarLayerImpl*>(layer);
 
+  scrollbar_layer->SetScrollLayerId(scroll_layer_id_);
   scrollbar_layer->set_internal_contents_scale_and_bounds(
       internal_contents_scale_, internal_content_bounds_);
 
@@ -144,14 +134,6 @@
   return this;
 }
 
-void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
-  PaintedScrollbarLayerImpl* scrollbar_layer =
-      static_cast<PaintedScrollbarLayerImpl*>(layer);
-
-  scrollbar_layer->SetScrollLayerAndClipLayerByIds(scroll_layer_id_,
-                                                   clip_layer_id_);
-}
-
 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
   // When the LTH is set to null or has changed, then this layer should remove
   // all of its associated resources.
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index 3e376b1..b6170a64 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -31,7 +31,6 @@
   // ScrollbarLayerInterface
   int ScrollLayerId() const override;
   void SetScrollLayer(int layer_id) override;
-  void SetClipLayer(int layer_id) override;
 
   ScrollbarOrientation orientation() const override;
 
@@ -39,7 +38,6 @@
   bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer) override;
-  void PushScrollClipPropertiesTo(LayerImpl* layer) override;
 
   const gfx::Size& internal_content_bounds() const {
     return internal_content_bounds_;
@@ -83,7 +81,6 @@
 
   scoped_ptr<Scrollbar> scrollbar_;
   int scroll_layer_id_;
-  int clip_layer_id_;
 
   float internal_contents_scale_;
   gfx::Size internal_content_bounds_;
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index eac32b6..68f24af 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -46,8 +46,8 @@
   scrollbar_layer_impl->SetThumbLength(500);
   scrollbar_layer_impl->SetTrackLength(layer_size.height());
   scrollbar_layer_impl->SetCurrentPos(100.f / 4);
-  scrollbar_layer_impl->SetMaximum(100);
-  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(1.f / 2);
+  scrollbar_layer_impl->SetClipLayerLength(100.f);
+  scrollbar_layer_impl->SetScrollLayerLength(200.f);
   scrollbar_layer_impl->set_track_ui_resource_id(track_uid);
   scrollbar_layer_impl->set_thumb_ui_resource_id(thumb_uid);
 
diff --git a/cc/layers/scrollbar_layer_impl_base.cc b/cc/layers/scrollbar_layer_impl_base.cc
index 6696e49..4e18b62 100644
--- a/cc/layers/scrollbar_layer_impl_base.cc
+++ b/cc/layers/scrollbar_layer_impl_base.cc
@@ -17,19 +17,19 @@
     bool is_left_side_vertical_scrollbar,
     bool is_overlay)
     : LayerImpl(tree_impl, id),
-      scroll_layer_(nullptr),
-      clip_layer_(nullptr),
+      scroll_layer_id_(Layer::INVALID_ID),
       is_overlay_scrollbar_(is_overlay),
       thumb_thickness_scale_factor_(1.f),
       current_pos_(0.f),
-      maximum_(0),
+      clip_layer_length_(0.f),
+      scroll_layer_length_(0.f),
       orientation_(orientation),
       is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar),
-      vertical_adjust_(0.f),
-      visible_to_total_length_ratio_(1.f) {
-}
+      vertical_adjust_(0.f) {}
 
-ScrollbarLayerImplBase::~ScrollbarLayerImplBase() {}
+ScrollbarLayerImplBase::~ScrollbarLayerImplBase() {
+  layer_tree_impl()->UnregisterScrollbar(this);
+}
 
 void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) {
   float active_opacity = layer->opacity();
@@ -39,7 +39,7 @@
   layer->SetHideLayerAndSubtree(active_hidden);
   DCHECK(layer->ToScrollbarLayer());
   layer->ToScrollbarLayer()->set_is_overlay_scrollbar(is_overlay_scrollbar_);
-  PushScrollClipPropertiesTo(layer);
+  layer->ToScrollbarLayer()->SetScrollLayerId(ScrollLayerId());
 }
 
 void ScrollbarLayerImplBase::DidBecomeActive() {
@@ -47,56 +47,19 @@
   UpdatePropertyTreeOpacity();
 }
 
-void ScrollbarLayerImplBase::PushScrollClipPropertiesTo(LayerImpl* layer) {
-  DCHECK(layer->ToScrollbarLayer());
-  layer->ToScrollbarLayer()->SetScrollLayerAndClipLayerByIds(ScrollLayerId(),
-                                                             ClipLayerId());
-}
-
 ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() {
   return this;
 }
 
-namespace {
-
-typedef void (LayerImpl::*ScrollbarRegistrationOperation)(
-    ScrollbarLayerImplBase*);
-
-void RegisterScrollbarWithLayers(ScrollbarLayerImplBase* scrollbar,
-                                 LayerImpl* container_layer,
-                                 LayerImpl* scroll_layer,
-                                 ScrollbarRegistrationOperation operation) {
-  if (!container_layer || !scroll_layer)
+void ScrollbarLayerImplBase::SetScrollLayerId(int scroll_layer_id) {
+  if (scroll_layer_id_ == scroll_layer_id)
     return;
 
-  DCHECK(scrollbar);
+  layer_tree_impl()->UnregisterScrollbar(this);
 
-  // Scrollbars must be notifed of changes to their scroll and container layers
-  // and all scrollable layers in between.
-  for (LayerImpl* current_layer = scroll_layer;
-       current_layer && current_layer != container_layer->parent();
-       current_layer = current_layer->parent()) {
-    (current_layer->*operation)(scrollbar);
-  }
-}
-}  // namespace
+  scroll_layer_id_ = scroll_layer_id;
 
-void ScrollbarLayerImplBase::SetScrollLayerAndClipLayerByIds(
-    int scroll_layer_id,
-    int clip_layer_id) {
-  LayerImpl* scroll_layer = layer_tree_impl()->LayerById(scroll_layer_id);
-  LayerImpl* clip_layer = layer_tree_impl()->LayerById(clip_layer_id);
-  if (scroll_layer_ == scroll_layer && clip_layer_ == clip_layer)
-    return;
-
-  RegisterScrollbarWithLayers(
-      this, clip_layer_, scroll_layer_, &LayerImpl::RemoveScrollbar);
-  scroll_layer_ = scroll_layer;
-  clip_layer_ = clip_layer;
-  RegisterScrollbarWithLayers(
-      this, clip_layer_, scroll_layer_, &LayerImpl::AddScrollbar);
-
-  ScrollbarParametersDidChange(false);
+  layer_tree_impl()->RegisterScrollbar(this);
 }
 
 bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
@@ -107,18 +70,12 @@
   return true;
 }
 
-bool ScrollbarLayerImplBase::SetMaximum(int maximum) {
-  if (maximum_ == maximum)
-    return false;
-  maximum_ = maximum;
-  NoteLayerPropertyChanged();
-  return true;
-}
-
 bool ScrollbarLayerImplBase::CanScrollOrientation() const {
-  if (!scroll_layer_)
+  LayerImpl* scroll_layer = layer_tree_impl()->LayerById(scroll_layer_id_);
+  if (!scroll_layer)
     return false;
-  return scroll_layer_->user_scrollable(orientation()) && (0 < maximum());
+  return scroll_layer->user_scrollable(orientation()) &&
+         clip_layer_length_ < scroll_layer_length_;
 }
 
 bool ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
@@ -129,13 +86,18 @@
   return true;
 }
 
-bool ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) {
-  if (!IsThumbResizable())
+bool ScrollbarLayerImplBase::SetClipLayerLength(float clip_layer_length) {
+  if (clip_layer_length_ == clip_layer_length)
     return false;
+  clip_layer_length_ = clip_layer_length;
+  NoteLayerPropertyChanged();
+  return true;
+}
 
-  if (visible_to_total_length_ratio_ == ratio)
+bool ScrollbarLayerImplBase::SetScrollLayerLength(float scroll_layer_length) {
+  if (scroll_layer_length_ == scroll_layer_length)
     return false;
-  visible_to_total_length_ratio_ = ratio;
+  scroll_layer_length_ = scroll_layer_length;
   NoteLayerPropertyChanged();
   return true;
 }
@@ -213,14 +175,14 @@
   float track_length = TrackLength();
   int thumb_length = ThumbLength();
   int thumb_thickness = ThumbThickness();
+  float maximum = scroll_layer_length_ - clip_layer_length_;
 
   // With the length known, we can compute the thumb's position.
-  float clamped_current_pos =
-      std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_));
+  float clamped_current_pos = std::min(std::max(current_pos_, 0.f), maximum);
 
   int thumb_offset = TrackStart();
-  if (maximum_ > 0) {
-    float ratio = clamped_current_pos / maximum_;
+  if (maximum > 0) {
+    float ratio = clamped_current_pos / maximum;
     float max_offset = track_length - thumb_length;
     thumb_offset += static_cast<int>(ratio * max_offset);
   }
@@ -247,11 +209,4 @@
   return gfx::ToEnclosingRect(thumb_rect);
 }
 
-void ScrollbarLayerImplBase::ScrollbarParametersDidChange(bool on_resize) {
-  if (!clip_layer_ || !scroll_layer_)
-    return;
-
-  scroll_layer_->SetScrollbarPosition(this, clip_layer_, on_resize);
-}
-
 }  // namespace cc
diff --git a/cc/layers/scrollbar_layer_impl_base.h b/cc/layers/scrollbar_layer_impl_base.h
index 339dcc9f..ebe508b 100644
--- a/cc/layers/scrollbar_layer_impl_base.h
+++ b/cc/layers/scrollbar_layer_impl_base.h
@@ -16,24 +16,20 @@
 
 class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl {
  public:
-  int ScrollLayerId() const {
-    return scroll_layer_ ? scroll_layer_->id() : Layer::INVALID_ID;
-  }
-  int ClipLayerId() const {
-    return clip_layer_ ? clip_layer_->id() : Layer::INVALID_ID;
-  }
+  int ScrollLayerId() const { return scroll_layer_id_; }
 
-  void SetScrollLayerAndClipLayerByIds(int scroll_layer_id, int clip_layer_id);
-  void ClearScrollLayer() { scroll_layer_ = nullptr; }
-  void ClearClipLayer() { clip_layer_ = nullptr; }
+  void SetScrollLayerId(int scroll_layer_id);
 
   float current_pos() const { return current_pos_; }
   bool SetCurrentPos(float current_pos);
-  int maximum() const { return maximum_; }
-  bool SetMaximum(int maximum);
-
+  bool SetClipLayerLength(float clip_layer_length);
+  bool SetScrollLayerLength(float scroll_layer_length);
   bool SetVerticalAdjust(float vertical_adjust);
 
+  float clip_layer_length() const { return clip_layer_length_; }
+  float scroll_layer_length() const { return scroll_layer_length_; }
+  float vertical_adjust() const { return vertical_adjust_; }
+
   bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; }
   void set_is_overlay_scrollbar(bool is_overlay) {
     is_overlay_scrollbar_ = is_overlay;
@@ -49,9 +45,7 @@
   void PushPropertiesTo(LayerImpl* layer) override;
   void DidBecomeActive() override;
   ScrollbarLayerImplBase* ToScrollbarLayer() override;
-  void PushScrollClipPropertiesTo(LayerImpl* layer);
 
-  bool SetVisibleToTotalLengthRatio(float ratio);
   // Thumb quad rect in layer space.
   virtual gfx::Rect ComputeThumbQuadRect() const;
 
@@ -60,8 +54,6 @@
   }
   bool SetThumbThicknessScaleFactor(float thumb_thickness_scale_factor);
 
-  void ScrollbarParametersDidChange(bool on_resize);
-
  protected:
   ScrollbarLayerImplBase(LayerTreeImpl* tree_impl,
                          int id,
@@ -70,11 +62,6 @@
                          bool is_overlay);
   ~ScrollbarLayerImplBase() override;
 
-  float visible_to_total_length_ratio() const {
-    return visible_to_total_length_ratio_;
-  }
-  float vertical_adjust() const { return vertical_adjust_; }
-
   virtual int ThumbThickness() const = 0;
   virtual int ThumbLength() const = 0;
   virtual float TrackLength() const = 0;
@@ -84,13 +71,13 @@
   virtual bool IsThumbResizable() const = 0;
 
  private:
-  LayerImpl* scroll_layer_;
-  LayerImpl* clip_layer_;
+  int scroll_layer_id_;
   bool is_overlay_scrollbar_;
 
   float thumb_thickness_scale_factor_;
   float current_pos_;
-  int maximum_;
+  float clip_layer_length_;
+  float scroll_layer_length_;
   ScrollbarOrientation orientation_;
   bool is_left_side_vertical_scrollbar_;
 
@@ -98,11 +85,11 @@
   // height (which may differ in the presence of top-controls hiding).
   float vertical_adjust_;
 
-  float visible_to_total_length_ratio_;
-
   DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImplBase);
 };
 
+typedef std::set<ScrollbarLayerImplBase*> ScrollbarSet;
+
 }  // namespace cc
 
 #endif  // CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
diff --git a/cc/layers/scrollbar_layer_interface.h b/cc/layers/scrollbar_layer_interface.h
index e76fc27a..a396a5d 100644
--- a/cc/layers/scrollbar_layer_interface.h
+++ b/cc/layers/scrollbar_layer_interface.h
@@ -17,8 +17,6 @@
  public:
   virtual int ScrollLayerId() const = 0;
   virtual void SetScrollLayer(int layer_id) = 0;
-  virtual void SetClipLayer(int layer_id) = 0;
-  virtual void PushScrollClipPropertiesTo(LayerImpl* layer) = 0;
 
   virtual ScrollbarOrientation orientation() const = 0;
 
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 2d92f60..eb857c2f 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -53,7 +53,6 @@
     child2 =
         PaintedScrollbarLayer::Create(settings, scrollbar.Pass(), child1->id());
   }
-  child2->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
   layer_tree_root->AddChild(child1);
   layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1);
   host->SetRootLayer(layer_tree_root);
@@ -146,36 +145,6 @@
   scoped_ptr<FakeResourceTrackingLayerTreeHost> layer_tree_host_;
 };
 
-TEST_F(ScrollbarLayerTest, ResolveScrollLayerPointer) {
-  scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
-  LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
-      layer_settings(), layer_tree_host_.get(), scrollbar.Pass(), false, false,
-      0, 0);
-
-  LayerImpl* cc_child1 = layer_impl_tree_root->children()[0];
-  PaintedScrollbarLayerImpl* cc_child2 =
-      static_cast<PaintedScrollbarLayerImpl*>(
-          layer_impl_tree_root->children()[1]);
-
-  EXPECT_EQ(cc_child1->scrollbars()->size(), 1UL);
-  EXPECT_EQ(*(cc_child1->scrollbars()->begin()), cc_child2);
-}
-
-TEST_F(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) {
-  scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
-  LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
-      layer_settings(), layer_tree_host_.get(), scrollbar.Pass(), true, false,
-      0, 0);
-
-  PaintedScrollbarLayerImpl* cc_child1 =
-      static_cast<PaintedScrollbarLayerImpl*>(
-          layer_impl_tree_root->children()[0]);
-  LayerImpl* cc_child2 = layer_impl_tree_root->children()[1];
-
-  EXPECT_EQ(cc_child2->scrollbars()->size(), 1UL);
-  EXPECT_EQ(*(cc_child2->scrollbars()->begin()), cc_child1);
-}
-
 TEST_F(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
   // Create and attach a non-overlay scrollbar.
   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
@@ -231,7 +200,6 @@
   scroll_layer->AddChild(content_layer);
   layer_tree_root->AddChild(scrollbar_layer);
   scrollbar_layer->ToScrollbarLayer()->SetScrollLayer(scroll_layer->id());
-  scrollbar_layer->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
 
   layer_tree_root->SavePaintProperties();
   content_layer->SavePaintProperties();
@@ -244,7 +212,8 @@
           layer_impl_tree_root->children()[1]);
 
   EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos());
-  EXPECT_EQ(30, cc_scrollbar_layer->maximum());
+  EXPECT_EQ(30, cc_scrollbar_layer->scroll_layer_length() -
+                    cc_scrollbar_layer->clip_layer_length());
 
   layer_tree_root->SetBounds(gfx::Size(700, 1500));
   layer_tree_root->SavePaintProperties();
@@ -254,20 +223,18 @@
   content_layer->SetBounds(gfx::Size(1000, 2000));
   content_layer->SavePaintProperties();
 
-  ScrollbarAnimationController* scrollbar_controller =
-      layer_impl_tree_root->scrollbar_animation_controller();
   layer_impl_tree_root = layer_tree_host_->CommitAndCreateLayerImplTree();
-  EXPECT_EQ(scrollbar_controller,
-            layer_impl_tree_root->scrollbar_animation_controller());
 
   EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos());
-  EXPECT_EQ(300, cc_scrollbar_layer->maximum());
+  EXPECT_EQ(300, cc_scrollbar_layer->scroll_layer_length() -
+                     cc_scrollbar_layer->clip_layer_length());
 
   LayerImpl* scroll_layer_impl = layer_impl_tree_root->children()[0];
   scroll_layer_impl->ScrollBy(gfx::Vector2d(12, 34));
 
   EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos());
-  EXPECT_EQ(300, cc_scrollbar_layer->maximum());
+  EXPECT_EQ(300, cc_scrollbar_layer->scroll_layer_length() -
+                     cc_scrollbar_layer->clip_layer_length());
 }
 
 #define UPDATE_AND_EXTRACT_LAYER_POINTERS()                                  \
@@ -278,7 +245,6 @@
     root_layer_impl = root_clip_layer_impl->children()[0];                   \
     scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(          \
         root_layer_impl->children()[1]);                                     \
-    scrollbar_layer_impl->ScrollbarParametersDidChange(false);               \
   } while (false)
 
 TEST_F(ScrollbarLayerTest, UpdatePropertiesOfScrollBarWhenThumbRemoved) {
@@ -303,7 +269,6 @@
   root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0));
   scrollbar_layer->SetBounds(gfx::Size(70, 10));
   scrollbar_layer->SetScrollLayer(root_layer->id());
-  scrollbar_layer->SetClipLayer(root_clip_layer->id());
   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10));
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
@@ -345,7 +310,6 @@
   root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0));
   scrollbar_layer->SetBounds(gfx::Size(70, 10));
   scrollbar_layer->SetScrollLayer(root_layer->id());
-  scrollbar_layer->SetClipLayer(root_clip_layer->id());
   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10));
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
@@ -416,8 +380,8 @@
           layer_impl_tree_root->children()[1]);
   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
   scrollbar_layer_impl->SetCurrentPos(10.f);
-  scrollbar_layer_impl->SetMaximum(100);
-  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f);
+  scrollbar_layer_impl->SetClipLayerLength(200 / 3.f);
+  scrollbar_layer_impl->SetScrollLayerLength(100 + 200 / 3.f);
 
   // Thickness should be overridden to 3.
   {
@@ -433,7 +397,8 @@
 
   // For solid color scrollbars, position and size should reflect the
   // current viewport state.
-  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.2f);
+  scrollbar_layer_impl->SetClipLayerLength(25.f);
+  scrollbar_layer_impl->SetScrollLayerLength(125.f);
   {
     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
     AppendQuadsData data;
@@ -447,7 +412,8 @@
 
   // We shouldn't attempt div-by-zero when the maximum is zero.
   scrollbar_layer_impl->SetCurrentPos(0.f);
-  scrollbar_layer_impl->SetMaximum(0);
+  scrollbar_layer_impl->SetClipLayerLength(125.f);
+  scrollbar_layer_impl->SetScrollLayerLength(125.f);
   {
     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
     AppendQuadsData data;
@@ -456,7 +422,7 @@
     const QuadList& quads = render_pass->quad_list;
     ASSERT_EQ(1u, quads.size());
     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads.front()->material);
-    EXPECT_EQ(gfx::Rect(1, 0, 19, 3), quads.front()->rect);
+    EXPECT_EQ(gfx::Rect(1, 0, 98, 3), quads.front()->rect);
   }
 }
 
@@ -478,7 +444,6 @@
         layer_settings(), scrollbar->Orientation(), kThumbThickness,
         kTrackStart, kIsLeftSideVerticalScrollbar, child1->id());
     child2->ToScrollbarLayer()->SetScrollLayer(scroll_layer->id());
-    child2->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
     scroll_layer->AddChild(child1);
     scroll_layer->InsertChild(child2, 1);
     layer_tree_root->AddChild(scroll_layer);
@@ -498,7 +463,6 @@
 
   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
   scrollbar_layer_impl->SetCurrentPos(4.f);
-  scrollbar_layer_impl->SetMaximum(8);
 
   {
     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
@@ -555,26 +519,27 @@
 
 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) {
   horizontal_scrollbar_layer_->SetCurrentPos(0);
-  horizontal_scrollbar_layer_->SetMaximum(10);
 
   // Simple case - one third of the scrollable area is visible, so the thumb
   // should be one third as long as the track.
-  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f);
+  horizontal_scrollbar_layer_->SetClipLayerLength(5.f);
+  horizontal_scrollbar_layer_->SetScrollLayerLength(15.f);
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
   EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
 
   // The thumb's length should never be less than its thickness.
-  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f);
+  horizontal_scrollbar_layer_->SetClipLayerLength(0.01f);
+  horizontal_scrollbar_layer_->SetScrollLayerLength(15.f);
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
   EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
 }
 
 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
-  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f);
 
-  horizontal_scrollbar_layer_->SetCurrentPos(0);
-  horizontal_scrollbar_layer_->SetMaximum(100);
+  horizontal_scrollbar_layer_->SetCurrentPos(0.f);
+  horizontal_scrollbar_layer_->SetClipLayerLength(12.f);
+  horizontal_scrollbar_layer_->SetScrollLayerLength(112.f);
   EXPECT_EQ(0, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
   EXPECT_EQ(10, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
 
@@ -593,9 +558,9 @@
   SolidColorScrollbarLayerImpl* layers[2] =
       { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() };
   for (size_t i = 0; i < 2; ++i) {
-    layers[i]->SetVisibleToTotalLengthRatio(0.2f);
-    layers[i]->SetCurrentPos(25);
-    layers[i]->SetMaximum(100);
+    layers[i]->SetCurrentPos(25.f);
+    layers[i]->SetClipLayerLength(25.f);
+    layers[i]->SetScrollLayerLength(125.f);
   }
   layers[0]->SetBounds(gfx::Size(100, 3));
   layers[1]->SetBounds(gfx::Size(3, 100));
diff --git a/cc/layers/solid_color_scrollbar_layer.cc b/cc/layers/solid_color_scrollbar_layer.cc
index 4ed55ca..62a3180 100644
--- a/cc/layers/solid_color_scrollbar_layer.cc
+++ b/cc/layers/solid_color_scrollbar_layer.cc
@@ -43,7 +43,6 @@
     int scroll_layer_id)
     : Layer(settings),
       scroll_layer_id_(Layer::INVALID_ID),
-      clip_layer_id_(scroll_layer_id),
       orientation_(orientation),
       thumb_thickness_(thumb_thickness),
       track_start_(track_start),
@@ -58,15 +57,10 @@
 
 void SolidColorScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
   Layer::PushPropertiesTo(layer);
-  PushScrollClipPropertiesTo(layer);
-}
-
-void SolidColorScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
   SolidColorScrollbarLayerImpl* scrollbar_layer =
       static_cast<SolidColorScrollbarLayerImpl*>(layer);
 
-  scrollbar_layer->SetScrollLayerAndClipLayerByIds(scroll_layer_id_,
-                                                   clip_layer_id_);
+  scrollbar_layer->SetScrollLayerId(scroll_layer_id_);
 }
 
 void SolidColorScrollbarLayer::SetNeedsDisplayRect(const gfx::Rect& rect) {
@@ -89,14 +83,6 @@
   SetNeedsFullTreeSync();
 }
 
-void SolidColorScrollbarLayer::SetClipLayer(int layer_id) {
-  if (layer_id == clip_layer_id_)
-    return;
-
-  clip_layer_id_ = layer_id;
-  SetNeedsFullTreeSync();
-}
-
 ScrollbarOrientation SolidColorScrollbarLayer::orientation() const {
   return orientation_;
 }
diff --git a/cc/layers/solid_color_scrollbar_layer.h b/cc/layers/solid_color_scrollbar_layer.h
index 6097014..8b14bce 100644
--- a/cc/layers/solid_color_scrollbar_layer.h
+++ b/cc/layers/solid_color_scrollbar_layer.h
@@ -29,14 +29,12 @@
   ScrollbarLayerInterface* ToScrollbarLayer() override;
 
   void PushPropertiesTo(LayerImpl* layer) override;
-  void PushScrollClipPropertiesTo(LayerImpl* layer) override;
 
   void SetNeedsDisplayRect(const gfx::Rect& rect) override;
 
   // ScrollbarLayerInterface
   int ScrollLayerId() const override;
   void SetScrollLayer(int layer_id) override;
-  void SetClipLayer(int layer_id) override;
 
   ScrollbarOrientation orientation() const override;
 
@@ -51,7 +49,6 @@
 
  private:
   int scroll_layer_id_;
-  int clip_layer_id_;
   ScrollbarOrientation orientation_;
   int thumb_thickness_;
   int track_start_;
diff --git a/cc/layers/solid_color_scrollbar_layer_impl.cc b/cc/layers/solid_color_scrollbar_layer_impl.cc
index beb9cb6..0b0a24e 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl.cc
@@ -75,9 +75,11 @@
 }
 
 int SolidColorScrollbarLayerImpl::ThumbLength() const {
-  return std::max(
-      static_cast<int>(visible_to_total_length_ratio() * TrackLength()),
-      ThumbThickness());
+  float thumb_length = TrackLength();
+  if (scroll_layer_length())
+    thumb_length *= clip_layer_length() / scroll_layer_length();
+
+  return std::max(static_cast<int>(thumb_length), ThumbThickness());
 }
 
 float SolidColorScrollbarLayerImpl::TrackLength() const {
diff --git a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
index c758497..f141535 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
@@ -31,9 +31,9 @@
           is_overlay);
   scrollbar_layer_impl->SetBounds(layer_size);
   scrollbar_layer_impl->SetDrawsContent(true);
-  scrollbar_layer_impl->SetCurrentPos(100.f / 4);
-  scrollbar_layer_impl->SetMaximum(100);
-  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(1.f / 2);
+  scrollbar_layer_impl->SetCurrentPos(25.f);
+  scrollbar_layer_impl->SetClipLayerLength(100.f);
+  scrollbar_layer_impl->SetScrollLayerLength(200.f);
   // SolidColorScrollbarLayers construct with opacity = 0.f, so override.
   scrollbar_layer_impl->SetOpacity(1.f);
 
diff --git a/cc/layers/viewport.cc b/cc/layers/viewport.cc
index 80e711f..4b68de5 100644
--- a/cc/layers/viewport.cc
+++ b/cc/layers/viewport.cc
@@ -50,17 +50,11 @@
 
   ScrollResult result;
 
-  // TODO(bokan): This shouldn't be needed but removing it causes subtle
-  // viewport movement during top controls manipulation.
-  if (gfx::ToRoundedVector2d(pending_content_delta).IsZero()) {
-    result.consumed_delta = delta;
-  } else {
-    pending_content_delta -= host_impl_->ScrollLayer(OuterScrollLayer(),
-                                                     pending_content_delta,
-                                                     viewport_point,
-                                                     is_direct_manipulation);
-    result.consumed_delta = delta - AdjustOverscroll(pending_content_delta);
-  }
+  pending_content_delta -= host_impl_->ScrollLayer(OuterScrollLayer(),
+                                                   pending_content_delta,
+                                                   viewport_point,
+                                                   is_direct_manipulation);
+  result.consumed_delta = delta - AdjustOverscroll(pending_content_delta);
 
   result.content_scrolled_delta = content_delta - pending_content_delta;
   return result;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 0f5abf2..85ceb2e 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -173,7 +173,7 @@
       did_lock_scrolling_layer_(false),
       wheel_scrolling_(false),
       scroll_affects_scroll_handler_(false),
-      scroll_layer_id_when_mouse_over_scrollbar_(0),
+      scroll_layer_id_when_mouse_over_scrollbar_(Layer::INVALID_ID),
       tile_priorities_dirty_(false),
       settings_(settings),
       visible_(false),
@@ -2753,9 +2753,6 @@
     if (!layer_impl->scrollable() || layer_impl == OuterViewportScrollLayer())
       continue;
 
-    if (!layer_impl->HasScrollbar(VERTICAL))
-      continue;
-
     float height = layer_impl->clip_height();
 
     // These magical values match WebKit and are designed to scroll nearly the
@@ -2835,66 +2832,56 @@
       gfx::ScalePoint(viewport_point, active_tree_->device_scale_factor());
   LayerImpl* layer_impl =
       active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
-  if (HandleMouseOverScrollbar(layer_impl, device_viewport_point))
+  HandleMouseOverScrollbar(layer_impl);
+  if (scroll_layer_id_when_mouse_over_scrollbar_ != Layer::INVALID_ID)
     return;
 
-  if (scroll_layer_id_when_mouse_over_scrollbar_) {
-    LayerImpl* scroll_layer_impl = active_tree_->LayerById(
-        scroll_layer_id_when_mouse_over_scrollbar_);
-
-    // The check for a null scroll_layer_impl below was added to see if it will
-    // eliminate the crashes described in http://crbug.com/326635.
-    // TODO(wjmaclean) Add a unit test if this fixes the crashes.
-    ScrollbarAnimationController* animation_controller =
-        scroll_layer_impl ? scroll_layer_impl->scrollbar_animation_controller()
-                          : NULL;
-    if (animation_controller)
-      animation_controller->DidMouseMoveOffScrollbar();
-    scroll_layer_id_when_mouse_over_scrollbar_ = 0;
-  }
-
   bool scroll_on_main_thread = false;
   LayerImpl* scroll_layer_impl = FindScrollLayerForDeviceViewportPoint(
       device_viewport_point, InputHandler::GESTURE, layer_impl,
       &scroll_on_main_thread, NULL);
+  if (scroll_layer_impl == InnerViewportScrollLayer())
+    scroll_layer_impl = OuterViewportScrollLayer();
   if (scroll_on_main_thread || !scroll_layer_impl)
     return;
 
   ScrollbarAnimationController* animation_controller =
-      scroll_layer_impl->scrollbar_animation_controller();
+      ScrollbarAnimationControllerForId(scroll_layer_impl->id());
   if (!animation_controller)
     return;
 
-  // TODO(wjmaclean) Is it ok to choose distance from more than two scrollbars?
   float distance_to_scrollbar = std::numeric_limits<float>::max();
-  for (LayerImpl::ScrollbarSet::iterator it =
-           scroll_layer_impl->scrollbars()->begin();
-       it != scroll_layer_impl->scrollbars()->end();
-       ++it)
+  for (ScrollbarLayerImplBase* scrollbar :
+       ScrollbarsFor(scroll_layer_impl->id()))
     distance_to_scrollbar =
         std::min(distance_to_scrollbar,
-                 DeviceSpaceDistanceToLayer(device_viewport_point, *it));
+                 DeviceSpaceDistanceToLayer(device_viewport_point, scrollbar));
 
   animation_controller->DidMouseMoveNear(distance_to_scrollbar /
                                          active_tree_->device_scale_factor());
 }
 
-bool LayerTreeHostImpl::HandleMouseOverScrollbar(LayerImpl* layer_impl,
-    const gfx::PointF& device_viewport_point) {
-  if (layer_impl && layer_impl->ToScrollbarLayer()) {
-    int scroll_layer_id = layer_impl->ToScrollbarLayer()->ScrollLayerId();
-    layer_impl = active_tree_->LayerById(scroll_layer_id);
-    if (layer_impl && layer_impl->scrollbar_animation_controller()) {
-      scroll_layer_id_when_mouse_over_scrollbar_ = scroll_layer_id;
-      layer_impl->scrollbar_animation_controller()->DidMouseMoveNear(0);
-    } else {
-      scroll_layer_id_when_mouse_over_scrollbar_ = 0;
-    }
+void LayerTreeHostImpl::HandleMouseOverScrollbar(LayerImpl* layer_impl) {
+  int new_id = Layer::INVALID_ID;
+  if (layer_impl && layer_impl->ToScrollbarLayer())
+    new_id = layer_impl->ToScrollbarLayer()->ScrollLayerId();
 
-    return true;
-  }
+  if (new_id == scroll_layer_id_when_mouse_over_scrollbar_)
+    return;
 
-  return false;
+  ScrollbarAnimationController* old_animation_controller =
+      ScrollbarAnimationControllerForId(
+          scroll_layer_id_when_mouse_over_scrollbar_);
+  if (old_animation_controller)
+    old_animation_controller->DidMouseMoveOffScrollbar();
+
+  scroll_layer_id_when_mouse_over_scrollbar_ = new_id;
+
+  ScrollbarAnimationController* new_animation_controller =
+      ScrollbarAnimationControllerForId(
+          scroll_layer_id_when_mouse_over_scrollbar_);
+  if (new_animation_controller)
+    new_animation_controller->DidMouseMoveNear(0);
 }
 
 void LayerTreeHostImpl::PinchGestureBegin() {
@@ -3044,16 +3031,8 @@
 }
 
 void LayerTreeHostImpl::AnimateScrollbars(base::TimeTicks monotonic_time) {
-  if (scrollbar_animation_controllers_.empty())
-    return;
-
-  TRACE_EVENT0("cc", "LayerTreeHostImpl::AnimateScrollbars");
-  std::set<ScrollbarAnimationController*> controllers_copy =
-      scrollbar_animation_controllers_;
-  for (auto& it : controllers_copy)
-    it->Animate(monotonic_time);
-
-  SetNeedsAnimate();
+  for (auto& it : scrollbar_animation_controllers_)
+    it.second->Animate(monotonic_time);
 }
 
 void LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time) {
@@ -3132,15 +3111,32 @@
   return str;
 }
 
-void LayerTreeHostImpl::StartAnimatingScrollbarAnimationController(
-    ScrollbarAnimationController* controller) {
-  scrollbar_animation_controllers_.insert(controller);
-  SetNeedsAnimate();
+void LayerTreeHostImpl::RegisterScrollbarAnimationController(
+    int scroll_layer_id) {
+  if (settings().scrollbar_animator == LayerTreeSettings::NO_ANIMATOR)
+    return;
+  if (ScrollbarAnimationControllerForId(scroll_layer_id))
+    return;
+  scrollbar_animation_controllers_.add(
+      scroll_layer_id,
+      active_tree_->CreateScrollbarAnimationController(scroll_layer_id));
 }
 
-void LayerTreeHostImpl::StopAnimatingScrollbarAnimationController(
-    ScrollbarAnimationController* controller) {
-  scrollbar_animation_controllers_.erase(controller);
+void LayerTreeHostImpl::UnregisterScrollbarAnimationController(
+    int scroll_layer_id) {
+  scrollbar_animation_controllers_.erase(scroll_layer_id);
+}
+
+ScrollbarAnimationController*
+LayerTreeHostImpl::ScrollbarAnimationControllerForId(
+    int scroll_layer_id) const {
+  if (InnerViewportScrollLayer() && OuterViewportScrollLayer() &&
+      scroll_layer_id == InnerViewportScrollLayer()->id())
+    scroll_layer_id = OuterViewportScrollLayer()->id();
+  auto i = scrollbar_animation_controllers_.find(scroll_layer_id);
+  if (i == scrollbar_animation_controllers_.end())
+    return nullptr;
+  return i->second;
 }
 
 void LayerTreeHostImpl::PostDelayedScrollbarAnimationTask(
@@ -3149,10 +3145,19 @@
   client_->PostDelayedAnimationTaskOnImplThread(task, delay);
 }
 
+void LayerTreeHostImpl::SetNeedsAnimateForScrollbarAnimation() {
+  TRACE_EVENT0("cc", "LayerTreeHostImpl::SetNeedsAnimateForScrollbarAnimation");
+  SetNeedsAnimate();
+}
+
 void LayerTreeHostImpl::SetNeedsRedrawForScrollbarAnimation() {
   SetNeedsRedraw();
 }
 
+ScrollbarSet LayerTreeHostImpl::ScrollbarsFor(int scroll_layer_id) const {
+  return active_tree_->ScrollbarsFor(scroll_layer_id);
+}
+
 void LayerTreeHostImpl::AddVideoFrameController(
     VideoFrameController* controller) {
   bool was_empty = video_frame_controllers_.empty();
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index f2d5dfaa..ed724b3 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -298,6 +298,11 @@
 
   size_t SourceAnimationFrameNumberForTesting() const;
 
+  void RegisterScrollbarAnimationController(int scroll_layer_id);
+  void UnregisterScrollbarAnimationController(int scroll_layer_id);
+  ScrollbarAnimationController* ScrollbarAnimationControllerForId(
+      int scroll_layer_id) const;
+
   DrawMode GetDrawMode() const;
 
   // Viewport size in draw space: this size is in physical pixels and is used
@@ -323,13 +328,11 @@
   void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override;
 
   // ScrollbarAnimationControllerClient implementation.
-  void StartAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override;
-  void StopAnimatingScrollbarAnimationController(
-      ScrollbarAnimationController* controller) override;
   void PostDelayedScrollbarAnimationTask(const base::Closure& task,
                                          base::TimeDelta delay) override;
+  void SetNeedsAnimateForScrollbarAnimation() override;
   void SetNeedsRedrawForScrollbarAnimation() override;
+  ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override;
 
   // VideoBeginFrameSource implementation.
   void AddVideoFrameController(VideoFrameController* controller) override;
@@ -652,8 +655,7 @@
 
   void ClearCurrentlyScrollingLayer();
 
-  bool HandleMouseOverScrollbar(LayerImpl* layer_impl,
-                                const gfx::PointF& device_viewport_point);
+  void HandleMouseOverScrollbar(LayerImpl* layer_impl);
 
   LayerImpl* FindScrollLayerForDeviceViewportPoint(
       const gfx::PointF& device_viewport_point,
@@ -783,9 +785,13 @@
 
   scoped_ptr<AnimationRegistrar> animation_registrar_;
   scoped_ptr<AnimationHost> animation_host_;
-  std::set<ScrollbarAnimationController*> scrollbar_animation_controllers_;
   std::set<VideoFrameController*> video_frame_controllers_;
 
+  // Map from scroll layer ID to scrollbar animation controller.
+  // There is one animation controller per pair of overlay scrollbars.
+  base::ScopedPtrHashMap<int, scoped_ptr<ScrollbarAnimationController>>
+      scrollbar_animation_controllers_;
+
   RenderingStatsInstrumentation* rendering_stats_instrumentation_;
   MicroBenchmarkControllerImpl micro_benchmark_controller_;
   scoped_ptr<TaskGraphRunner> single_thread_synchronous_task_graph_runner_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 0de56bf5..68f775d 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1014,22 +1014,14 @@
   EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
-  // Trying to scroll without a vertical scrollbar will fail.
+  // Trying to scroll if not user_scrollable_vertical will fail.
+  host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(false);
   EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
       gfx::Point(), SCROLL_FORWARD));
   EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
       gfx::Point(), SCROLL_BACKWARD));
 
-  scoped_ptr<PaintedScrollbarLayerImpl> vertical_scrollbar(
-      PaintedScrollbarLayerImpl::Create(
-          host_impl_->active_tree(),
-          20,
-          VERTICAL));
-  vertical_scrollbar->SetBounds(gfx::Size(15, 1000));
-  host_impl_->InnerViewportScrollLayer()->AddScrollbar(
-      vertical_scrollbar.get());
-
-  // Trying to scroll with a vertical scrollbar will succeed.
+  host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(true);
   EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
       gfx::Point(), SCROLL_FORWARD));
   EXPECT_FLOAT_EQ(875.f,
@@ -1311,6 +1303,54 @@
       outer_scroll_layer->CurrentScrollOffset());
 }
 
+// Make sure scrolls smaller than a unit applied to the viewport don't get
+// dropped. crbug.com/539334.
+TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
+  LayerTreeSettings settings = DefaultSettings();
+  CreateHostImpl(settings, CreateOutputSurface());
+  host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
+
+  const gfx::Size content_size(1000, 1000);
+  const gfx::Size viewport_size(500, 500);
+  CreateBasicVirtualViewportLayers(viewport_size, content_size);
+
+  LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer();
+  LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer();
+
+  // Sanity checks.
+  EXPECT_VECTOR_EQ(
+      gfx::Vector2dF(500, 500),
+      outer_scroll_layer->MaxScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll_layer->CurrentScrollOffset());
+
+  RebuildPropertyTrees();
+
+  // Scroll only the layout viewport.
+  host_impl_->ScrollBegin(gfx::Point(250, 250), InputHandler::GESTURE);
+  host_impl_->ScrollBy(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f));
+  EXPECT_VECTOR2DF_EQ(
+      gfx::Vector2dF(0.125f, 0.125f),
+      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR2DF_EQ(
+      gfx::Vector2dF(0, 0),
+      inner_scroll_layer->CurrentScrollOffset());
+  host_impl_->ScrollEnd();
+
+  host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
+
+  // Now that we zoomed in, the scroll should be applied to the inner viewport.
+  host_impl_->ScrollBegin(gfx::Point(250, 250), InputHandler::GESTURE);
+  host_impl_->ScrollBy(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f));
+  EXPECT_VECTOR2DF_EQ(
+      gfx::Vector2dF(0.125f, 0.125f),
+      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR2DF_EQ(
+      gfx::Vector2dF(0.25f, 0.25f),
+      inner_scroll_layer->CurrentScrollOffset());
+  host_impl_->ScrollEnd();
+}
+
 // Tests that scrolls during a pinch gesture (i.e. "two-finger" scrolls) work
 // as expected. That is, scrolling during a pinch should bubble from the inner
 // to the outer viewport.
@@ -2159,9 +2199,9 @@
                                              VERTICAL, 10, 0, false, true);
     EXPECT_FLOAT_EQ(0.f, scrollbar->opacity());
 
-    LayerImpl* scroll = host_impl_->InnerViewportScrollLayer();
-    LayerImpl* root = scroll->parent()->parent();
-    scrollbar->SetScrollLayerAndClipLayerByIds(scroll->id(), root->id());
+    LayerImpl* scroll = host_impl_->active_tree()->OuterViewportScrollLayer();
+    LayerImpl* root = host_impl_->active_tree()->InnerViewportContainerLayer();
+    scrollbar->SetScrollLayerId(scroll->id());
     root->AddChild(scrollbar.Pass());
 
     host_impl_->active_tree()->DidBecomeActive();
@@ -2178,10 +2218,12 @@
 
     base::TimeTicks fake_now = base::TimeTicks::Now();
 
+    // A task will be posted to fade the initial scrollbar.
     EXPECT_FALSE(did_request_animate_);
     EXPECT_FALSE(did_request_redraw_);
-    EXPECT_EQ(base::TimeDelta(), requested_animation_delay_);
-    EXPECT_TRUE(animation_task_.Equals(base::Closure()));
+    EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+    requested_animation_delay_ = base::TimeDelta();
+    animation_task_ = base::Closure();
 
     // If no scroll happened during a scroll gesture, it should have no effect.
     host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
@@ -2293,9 +2335,9 @@
     scoped_ptr<SolidColorScrollbarLayerImpl> scrollbar =
         SolidColorScrollbarLayerImpl::Create(host_impl_->pending_tree(), 400,
                                              VERTICAL, 10, 0, false, true);
-    LayerImpl* scroll = host_impl_->pending_tree()->InnerViewportScrollLayer();
-    LayerImpl* root = scroll->parent()->parent();
-    scrollbar->SetScrollLayerAndClipLayerByIds(scroll->id(), root->id());
+    LayerImpl* scroll = host_impl_->pending_tree()->OuterViewportScrollLayer();
+    LayerImpl* root = host_impl_->pending_tree()->InnerViewportContainerLayer();
+    scrollbar->SetScrollLayerId(scroll->id());
     root->AddChild(scrollbar.Pass());
     host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
     host_impl_->pending_tree()->BuildPropertyTreesForTesting();
@@ -2308,6 +2350,8 @@
             scrollbar_layer->effect_tree_index());
     EXPECT_FLOAT_EQ(scrollbar_layer->opacity(), active_tree_node->data.opacity);
 
+    host_impl_->ScrollbarAnimationControllerForId(scroll->id())
+        ->DidMouseMoveNear(0);
     host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 5));
     host_impl_->ScrollEnd();
@@ -2335,6 +2379,126 @@
   RunTest(LayerTreeSettings::THINNING);
 }
 
+TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) {
+  LayerTreeSettings settings;
+  settings.scrollbar_animator = LayerTreeSettings::LINEAR_FADE;
+  settings.scrollbar_fade_delay_ms = 20;
+  settings.scrollbar_fade_duration_ms = 20;
+  CreateHostImpl(settings, CreateOutputSurface());
+
+  gfx::Size viewport_size(300, 200);
+  gfx::Size content_size(1000, 1000);
+
+  const int vert_1_id = 10;
+  const int horiz_1_id = 11;
+  const int vert_2_id = 12;
+  const int horiz_2_id = 13;
+  const int child_clip_id = 14;
+  const int child_scroll_id = 15;
+
+  CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
+  host_impl_->active_tree()->InnerViewportContainerLayer()->SetBounds(
+      viewport_size);
+  LayerImpl* root_scroll =
+      host_impl_->active_tree()->OuterViewportScrollLayer();
+  scoped_ptr<SolidColorScrollbarLayerImpl> vert_1_scrollbar =
+      SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), vert_1_id,
+                                           VERTICAL, 5, 5, true, true);
+  scoped_ptr<SolidColorScrollbarLayerImpl> horiz_1_scrollbar =
+      SolidColorScrollbarLayerImpl::Create(
+          host_impl_->active_tree(), horiz_1_id, HORIZONTAL, 5, 5, true, true);
+  scoped_ptr<SolidColorScrollbarLayerImpl> vert_2_scrollbar =
+      SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), vert_2_id,
+                                           VERTICAL, 5, 5, true, true);
+  scoped_ptr<SolidColorScrollbarLayerImpl> horiz_2_scrollbar =
+      SolidColorScrollbarLayerImpl::Create(
+          host_impl_->active_tree(), horiz_2_id, HORIZONTAL, 5, 5, true, true);
+  scoped_ptr<LayerImpl> child =
+      LayerImpl::Create(host_impl_->active_tree(), child_scroll_id);
+  child->SetBounds(content_size);
+  scoped_ptr<LayerImpl> child_clip =
+      LayerImpl::Create(host_impl_->active_tree(), child_clip_id);
+  child->SetBounds(viewport_size);
+  LayerImpl* child_ptr = child.get();
+  LayerImpl* child_clip_ptr = child_clip.get();
+
+  // Check scrollbar registration on the viewport layers.
+  EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->id()).size());
+  EXPECT_EQ(nullptr,
+            host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+  vert_1_scrollbar->SetScrollLayerId(root_scroll->id());
+  EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->id()).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+  horiz_1_scrollbar->SetScrollLayerId(root_scroll->id());
+  EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(root_scroll->id()).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+
+  // Changing one of the viewport layers should result in a scrollbar animation
+  // update.
+  animation_task_ = base::Closure();
+  host_impl_->active_tree()->InnerViewportContainerLayer()->SetBoundsDelta(
+      gfx::Vector2dF(10, 10));
+  EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+  animation_task_ = base::Closure();
+  host_impl_->active_tree()->OuterViewportScrollLayer()->SetCurrentScrollOffset(
+      gfx::ScrollOffset(10, 10));
+  EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+  animation_task_ = base::Closure();
+  host_impl_->active_tree()->InnerViewportScrollLayer()->SetCurrentScrollOffset(
+      gfx::ScrollOffset(10, 10));
+  EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+  animation_task_ = base::Closure();
+
+  // Check scrollbar registration on a sublayer.
+  child->SetScrollClipLayer(child_clip->id());
+  child_clip->AddChild(child.Pass());
+  root_scroll->AddChild(child_clip.Pass());
+  EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  EXPECT_EQ(nullptr,
+            host_impl_->ScrollbarAnimationControllerForId(child_scroll_id));
+  vert_2_scrollbar->SetScrollLayerId(child_scroll_id);
+  EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id));
+  horiz_2_scrollbar->SetScrollLayerId(child_scroll_id);
+  EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id));
+
+  // Changing one of the child layers should result in a scrollbar animation
+  // update.
+  animation_task_ = base::Closure();
+  child_clip_ptr->SetBounds(gfx::Size(200, 200));
+  EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+  animation_task_ = base::Closure();
+  child_ptr->SetCurrentScrollOffset(gfx::ScrollOffset(10, 10));
+  EXPECT_FALSE(animation_task_.Equals(base::Closure()));
+  animation_task_ = base::Closure();
+
+  // Check scrollbar unregistration.
+  vert_1_scrollbar.reset();
+  EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->id()).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+  horiz_1_scrollbar.reset();
+  EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->id()).size());
+  EXPECT_EQ(nullptr,
+            host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+
+  EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  vert_2_scrollbar.reset();
+  EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id));
+  horiz_2_scrollbar.reset();
+  EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_id).size());
+  EXPECT_EQ(nullptr,
+            host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
+
+  // Changing scroll offset should no longer trigger any animation.
+  host_impl_->active_tree()->InnerViewportScrollLayer()->SetCurrentScrollOffset(
+      gfx::ScrollOffset(20, 20));
+  EXPECT_TRUE(animation_task_.Equals(base::Closure()));
+  child_ptr->SetCurrentScrollOffset(gfx::ScrollOffset(20, 20));
+  EXPECT_TRUE(animation_task_.Equals(base::Closure()));
+}
+
 void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale(
     float device_scale_factor) {
   LayerTreeSettings settings;
@@ -2346,75 +2510,60 @@
   gfx::Size device_viewport_size =
       gfx::ScaleToFlooredSize(viewport_size, device_scale_factor);
   gfx::Size content_size(1000, 1000);
+  gfx::Size scrollbar_size(gfx::Size(15, viewport_size.height()));
 
   CreateHostImpl(settings, CreateOutputSurface());
   host_impl_->active_tree()->SetDeviceScaleFactor(device_scale_factor);
   host_impl_->SetViewportSize(device_viewport_size);
 
-  scoped_ptr<LayerImpl> root =
-      LayerImpl::Create(host_impl_->active_tree(), 1);
-  root->SetBounds(viewport_size);
-  root->SetHasRenderSurface(true);
-
-  scoped_ptr<LayerImpl> scroll =
-      LayerImpl::Create(host_impl_->active_tree(), 2);
-  scroll->SetScrollClipLayer(root->id());
-  scroll->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
-  scroll->SetBounds(content_size);
-  scroll->SetIsContainerForFixedPositionLayers(true);
-
-  scoped_ptr<LayerImpl> contents =
-      LayerImpl::Create(host_impl_->active_tree(), 3);
-  contents->SetDrawsContent(true);
-  contents->SetBounds(content_size);
-
-  // The scrollbar is on the right side.
-  scoped_ptr<PaintedScrollbarLayerImpl> scrollbar =
-      PaintedScrollbarLayerImpl::Create(host_impl_->active_tree(), 5, VERTICAL);
-  scrollbar->SetDrawsContent(true);
-  scrollbar->SetBounds(gfx::Size(15, viewport_size.height()));
-  scrollbar->SetPosition(gfx::Point(285, 0));
-
-  scroll->AddChild(contents.Pass());
-  root->AddChild(scroll.Pass());
-  scrollbar->SetScrollLayerAndClipLayerByIds(2, 1);
-  root->AddChild(scrollbar.Pass());
-
-  host_impl_->active_tree()->SetRootLayer(root.Pass());
-  host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 2,
-                                                      Layer::INVALID_ID);
-  host_impl_->active_tree()->DidBecomeActive();
-  DrawFrame();
-
+  CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
+  host_impl_->active_tree()->InnerViewportContainerLayer()->SetBounds(
+      viewport_size);
   LayerImpl* root_scroll =
-      host_impl_->active_tree()->InnerViewportScrollLayer();
-  ASSERT_TRUE(root_scroll->scrollbar_animation_controller());
+      host_impl_->active_tree()->OuterViewportScrollLayer();
+  // The scrollbar is on the left side.
+  scoped_ptr<SolidColorScrollbarLayerImpl> scrollbar =
+      SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), 6,
+                                           VERTICAL, 5, 5, true, true);
+  scrollbar->SetScrollLayerId(root_scroll->id());
+  scrollbar->SetDrawsContent(true);
+  scrollbar->SetBounds(scrollbar_size);
+  scrollbar->SetTouchEventHandlerRegion(gfx::Rect(scrollbar_size));
+  host_impl_->active_tree()->InnerViewportContainerLayer()->AddChild(
+      scrollbar.Pass());
+  host_impl_->active_tree()->DidBecomeActive();
+
+  DrawFrame();
+  host_impl_->active_tree()->UpdateDrawProperties(false);
+
   ScrollbarAnimationControllerThinning* scrollbar_animation_controller =
       static_cast<ScrollbarAnimationControllerThinning*>(
-          root_scroll->scrollbar_animation_controller());
+          host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()));
   scrollbar_animation_controller->set_mouse_move_distance_for_test(100.f);
 
-  host_impl_->MouseMoveAt(gfx::Point(1, 1));
+  host_impl_->MouseMoveAt(gfx::Point(200, 1));
   EXPECT_FALSE(scrollbar_animation_controller->mouse_is_near_scrollbar());
 
-  host_impl_->MouseMoveAt(gfx::Point(200, 50));
+  host_impl_->MouseMoveAt(gfx::Point(100, 50));
   EXPECT_TRUE(scrollbar_animation_controller->mouse_is_near_scrollbar());
 
-  host_impl_->MouseMoveAt(gfx::Point(184, 100));
+  host_impl_->MouseMoveAt(gfx::Point(116, 100));
   EXPECT_FALSE(scrollbar_animation_controller->mouse_is_near_scrollbar());
 
   scrollbar_animation_controller->set_mouse_move_distance_for_test(102.f);
-  host_impl_->MouseMoveAt(gfx::Point(184, 100));
+  host_impl_->MouseMoveAt(gfx::Point(116, 100));
   EXPECT_TRUE(scrollbar_animation_controller->mouse_is_near_scrollbar());
 
   did_request_redraw_ = false;
-  EXPECT_EQ(0, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
-  host_impl_->MouseMoveAt(gfx::Point(290, 100));
-  EXPECT_EQ(2, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
-  host_impl_->MouseMoveAt(gfx::Point(290, 120));
-  EXPECT_EQ(2, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
+  EXPECT_EQ(Layer::INVALID_ID,
+            host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
+  host_impl_->MouseMoveAt(gfx::Point(10, 100));
+  EXPECT_EQ(117, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
+  host_impl_->MouseMoveAt(gfx::Point(10, 120));
+  EXPECT_EQ(117, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
   host_impl_->MouseMoveAt(gfx::Point(150, 120));
-  EXPECT_EQ(0, host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
+  EXPECT_EQ(Layer::INVALID_ID,
+            host_impl_->scroll_layer_id_when_mouse_over_scrollbar());
 }
 
 TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
diff --git a/cc/trees/layer_tree_host_unittest_damage.cc b/cc/trees/layer_tree_host_unittest_damage.cc
index 20667bf..f626b55c 100644
--- a/cc/trees/layer_tree_host_unittest_damage.cc
+++ b/cc/trees/layer_tree_host_unittest_damage.cc
@@ -353,7 +353,6 @@
         layer_settings(), false, true, content_layer->id());
     scrollbar_layer->SetPosition(gfx::Point(300, 300));
     scrollbar_layer->SetBounds(gfx::Size(10, 100));
-    scrollbar_layer->ToScrollbarLayer()->SetClipLayer(scroll_clip_layer->id());
     scrollbar_layer->ToScrollbarLayer()->SetScrollLayer(content_layer->id());
     root_layer->AddChild(scrollbar_layer);
 
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 10421ae5..6b0c9fd 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -112,6 +112,108 @@
       });
 }
 
+bool LayerTreeImpl::IsViewportLayerId(int id) const {
+  if (id == inner_viewport_scroll_layer_id_ ||
+      id == outer_viewport_scroll_layer_id_)
+    return true;
+  if (InnerViewportContainerLayer() &&
+      id == InnerViewportContainerLayer()->id())
+    return true;
+  if (OuterViewportContainerLayer() &&
+      id == OuterViewportContainerLayer()->id())
+    return true;
+
+  return false;
+}
+
+void LayerTreeImpl::DidUpdateScrollState(int layer_id) {
+  if (!IsActiveTree())
+    return;
+
+  if (layer_id == Layer::INVALID_ID)
+    return;
+
+  int scroll_layer_id, clip_layer_id;
+  if (IsViewportLayerId(layer_id)) {
+    if (!InnerViewportContainerLayer())
+      return;
+
+    // For scrollbar purposes, a change to any of the four viewport layers
+    // should affect the scrollbars tied to the outermost layers, which express
+    // the sum of the entire viewport.
+    scroll_layer_id = outer_viewport_scroll_layer_id_;
+    clip_layer_id = InnerViewportContainerLayer()->id();
+  } else {
+    // If the clip layer id was passed in, then look up the scroll layer, or
+    // vice versa.
+    auto i = clip_scroll_map_.find(layer_id);
+    if (i != clip_scroll_map_.end()) {
+      scroll_layer_id = i->second;
+      clip_layer_id = layer_id;
+    } else {
+      scroll_layer_id = layer_id;
+      clip_layer_id = LayerById(scroll_layer_id)->scroll_clip_layer_id();
+    }
+  }
+  UpdateScrollbars(scroll_layer_id, clip_layer_id);
+}
+
+void LayerTreeImpl::UpdateScrollbars(int scroll_layer_id, int clip_layer_id) {
+  DCHECK(IsActiveTree());
+
+  LayerImpl* clip_layer = LayerById(clip_layer_id);
+  LayerImpl* scroll_layer = LayerById(scroll_layer_id);
+
+  if (!clip_layer || !scroll_layer)
+    return;
+
+  gfx::SizeF clip_size(clip_layer->BoundsForScrolling());
+  gfx::SizeF scroll_size(scroll_layer->BoundsForScrolling());
+
+  if (scroll_size.IsEmpty())
+    return;
+
+  gfx::ScrollOffset current_offset = scroll_layer->CurrentScrollOffset();
+  if (IsViewportLayerId(scroll_layer_id)) {
+    current_offset += InnerViewportScrollLayer()->CurrentScrollOffset();
+    clip_size.Scale(1 / current_page_scale_factor());
+  }
+
+  bool scrollbar_needs_animation = false;
+  bool scroll_layer_size_did_change = false;
+  bool y_offset_did_change = false;
+  for (ScrollbarLayerImplBase* scrollbar : ScrollbarsFor(scroll_layer_id)) {
+    if (scrollbar->orientation() == HORIZONTAL) {
+      scrollbar_needs_animation |= scrollbar->SetCurrentPos(current_offset.x());
+      scrollbar_needs_animation |=
+          scrollbar->SetClipLayerLength(clip_size.width());
+      scrollbar_needs_animation |= scroll_layer_size_did_change |=
+          scrollbar->SetScrollLayerLength(scroll_size.width());
+    } else {
+      scrollbar_needs_animation |= y_offset_did_change |=
+          scrollbar->SetCurrentPos(current_offset.y());
+      scrollbar_needs_animation |=
+          scrollbar->SetClipLayerLength(clip_size.height());
+      scrollbar_needs_animation |= scroll_layer_size_did_change |=
+          scrollbar->SetScrollLayerLength(scroll_size.height());
+    }
+    scrollbar_needs_animation |=
+        scrollbar->SetVerticalAdjust(clip_layer->bounds_delta().y());
+  }
+
+  if (y_offset_did_change && IsViewportLayerId(scroll_layer_id))
+    TRACE_COUNTER_ID1("cc", "scroll_offset_y", scroll_layer->id(),
+                      current_offset.y());
+
+  if (scrollbar_needs_animation) {
+    ScrollbarAnimationController* controller =
+        layer_tree_host_impl_->ScrollbarAnimationControllerForId(
+            scroll_layer_id);
+    if (controller)
+      controller->DidScrollUpdate(scroll_layer_size_did_change);
+  }
+}
+
 void LayerTreeImpl::SetRootLayer(scoped_ptr<LayerImpl> layer) {
   root_layer_ = layer.Pass();
 
@@ -272,32 +374,23 @@
   if (currently_scrolling_layer_id_ == new_id)
     return;
 
-  if (CurrentlyScrollingLayer() &&
-      CurrentlyScrollingLayer()->scrollbar_animation_controller())
-    CurrentlyScrollingLayer()->scrollbar_animation_controller()->DidScrollEnd();
+  ScrollbarAnimationController* old_animation_controller =
+      layer_tree_host_impl_->ScrollbarAnimationControllerForId(
+          currently_scrolling_layer_id_);
+  ScrollbarAnimationController* new_animation_controller =
+      layer_tree_host_impl_->ScrollbarAnimationControllerForId(new_id);
+
+  if (old_animation_controller)
+    old_animation_controller->DidScrollEnd();
   currently_scrolling_layer_id_ = new_id;
-  if (layer && layer->scrollbar_animation_controller())
-    layer->scrollbar_animation_controller()->DidScrollBegin();
+  if (new_animation_controller)
+    new_animation_controller->DidScrollBegin();
 }
 
 void LayerTreeImpl::ClearCurrentlyScrollingLayer() {
   SetCurrentlyScrollingLayer(NULL);
 }
 
-namespace {
-
-void ForceScrollbarParameterUpdateAfterScaleChange(LayerImpl* current_layer) {
-  if (!current_layer)
-    return;
-
-  while (current_layer) {
-    current_layer->ScrollbarParametersDidChange(false);
-    current_layer = current_layer->parent();
-  }
-}
-
-}  // namespace
-
 float LayerTreeImpl::ClampPageScaleFactorToLimits(
     float page_scale_factor) const {
   if (min_page_scale_factor_ && page_scale_factor < min_page_scale_factor_)
@@ -425,7 +518,7 @@
         ClampPageScaleFactorToLimits(current_page_scale_factor()));
 
   set_needs_update_draw_properties();
-  ForceScrollbarParameterUpdateAfterScaleChange(PageScaleLayer());
+  DidUpdateScrollState(inner_viewport_scroll_layer_id_);
   HideInnerViewportScrollbarsIfNeeded();
 }
 
@@ -439,23 +532,14 @@
 }
 
 void LayerTreeImpl::HideInnerViewportScrollbarsIfNeeded() {
-  if (!InnerViewportContainerLayer())
-    return;
-
-  LayerImpl::ScrollbarSet* scrollbars =
-      InnerViewportContainerLayer()->scrollbars();
-
-  if (!scrollbars)
-    return;
-
   float minimum_scale_to_show_at = min_page_scale_factor() * 1.05f;
   bool hide_scrollbars =
       hide_pinch_scrollbars_near_min_scale_ &&
       (current_page_scale_factor() < minimum_scale_to_show_at);
 
-  for (LayerImpl::ScrollbarSet::iterator it = scrollbars->begin();
-       it != scrollbars->end(); ++it)
-    (*it)->SetHideLayerAndSubtree(hide_scrollbars);
+  for (ScrollbarLayerImplBase* scrollbar_layer :
+       ScrollbarsFor(outer_viewport_scroll_layer_id_))
+    scrollbar_layer->SetHideLayerAndSubtree(hide_scrollbars);
 }
 
 SyncedProperty<ScaleGroup>* LayerTreeImpl::page_scale_factor() {
@@ -916,7 +1000,7 @@
 }
 
 scoped_ptr<ScrollbarAnimationController>
-LayerTreeImpl::CreateScrollbarAnimationController(LayerImpl* scrolling_layer) {
+LayerTreeImpl::CreateScrollbarAnimationController(int scroll_layer_id) {
   DCHECK(settings().scrollbar_fade_delay_ms);
   DCHECK(settings().scrollbar_fade_duration_ms);
   base::TimeDelta delay =
@@ -928,18 +1012,13 @@
   switch (settings().scrollbar_animator) {
     case LayerTreeSettings::LINEAR_FADE: {
       return ScrollbarAnimationControllerLinearFade::Create(
-          scrolling_layer,
-          layer_tree_host_impl_,
-          delay,
-          resize_delay,
+          scroll_layer_id, layer_tree_host_impl_, delay, resize_delay,
           duration);
     }
     case LayerTreeSettings::THINNING: {
-      return ScrollbarAnimationControllerThinning::Create(scrolling_layer,
-                                                          layer_tree_host_impl_,
-                                                          delay,
-                                                          resize_delay,
-                                                          duration);
+      return ScrollbarAnimationControllerThinning::Create(
+          scroll_layer_id, layer_tree_host_impl_, delay, resize_delay,
+          duration);
     }
     case LayerTreeSettings::NO_ANIMATOR:
       NOTREACHED();
@@ -1134,6 +1213,62 @@
   picture_layers_.erase(it);
 }
 
+void LayerTreeImpl::RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer) {
+  if (scrollbar_layer->ScrollLayerId() == Layer::INVALID_ID)
+    return;
+
+  scrollbar_map_.insert(std::pair<int, int>(scrollbar_layer->ScrollLayerId(),
+                                            scrollbar_layer->id()));
+  if (IsActiveTree() && scrollbar_layer->is_overlay_scrollbar())
+    layer_tree_host_impl_->RegisterScrollbarAnimationController(
+        scrollbar_layer->ScrollLayerId());
+
+  DidUpdateScrollState(scrollbar_layer->ScrollLayerId());
+}
+
+void LayerTreeImpl::UnregisterScrollbar(
+    ScrollbarLayerImplBase* scrollbar_layer) {
+  int scroll_layer_id = scrollbar_layer->ScrollLayerId();
+  if (scroll_layer_id == Layer::INVALID_ID)
+    return;
+
+  auto scrollbar_range = scrollbar_map_.equal_range(scroll_layer_id);
+  for (auto i = scrollbar_range.first; i != scrollbar_range.second; ++i)
+    if (i->second == scrollbar_layer->id()) {
+      scrollbar_map_.erase(i);
+      break;
+    }
+
+  if (IsActiveTree() && scrollbar_map_.count(scroll_layer_id) == 0)
+    layer_tree_host_impl_->UnregisterScrollbarAnimationController(
+        scroll_layer_id);
+}
+
+ScrollbarSet LayerTreeImpl::ScrollbarsFor(int scroll_layer_id) const {
+  ScrollbarSet scrollbars;
+  auto scrollbar_range = scrollbar_map_.equal_range(scroll_layer_id);
+  for (auto i = scrollbar_range.first; i != scrollbar_range.second; ++i)
+    scrollbars.insert(LayerById(i->second)->ToScrollbarLayer());
+  return scrollbars;
+}
+
+void LayerTreeImpl::RegisterScrollLayer(LayerImpl* layer) {
+  if (layer->scroll_clip_layer_id() == Layer::INVALID_ID)
+    return;
+
+  clip_scroll_map_.insert(
+      std::pair<int, int>(layer->scroll_clip_layer_id(), layer->id()));
+
+  DidUpdateScrollState(layer->id());
+}
+
+void LayerTreeImpl::UnregisterScrollLayer(LayerImpl* layer) {
+  if (layer->scroll_clip_layer_id() == Layer::INVALID_ID)
+    return;
+
+  clip_scroll_map_.erase(layer->scroll_clip_layer_id());
+}
+
 void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) {
   // Only the active tree needs to know about layers with copy requests, as
   // they are aborted if not serviced during draw.
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index ed4413a..8bc1ee7 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -5,6 +5,7 @@
 #ifndef CC_TREES_LAYER_TREE_IMPL_H_
 #define CC_TREES_LAYER_TREE_IMPL_H_
 
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -99,7 +100,7 @@
   gfx::Size DrawViewportSize() const;
   const gfx::Rect ViewportRectForTilePriority() const;
   scoped_ptr<ScrollbarAnimationController> CreateScrollbarAnimationController(
-      LayerImpl* scrolling_layer);
+      int scroll_layer_id);
   void DidAnimateScrollOffset();
   void InputScrollAnimationFinished();
   bool use_gpu_rasterization() const;
@@ -320,6 +321,13 @@
     return picture_layers_;
   }
 
+  void RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer);
+  void UnregisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer);
+  ScrollbarSet ScrollbarsFor(int scroll_layer_id) const;
+
+  void RegisterScrollLayer(LayerImpl* layer);
+  void UnregisterScrollLayer(LayerImpl* layer);
+
   void AddLayerWithCopyOutputRequest(LayerImpl* layer);
   void RemoveLayerWithCopyOutputRequest(LayerImpl* layer);
   const std::vector<LayerImpl*>& LayersWithCopyOutputRequest() const;
@@ -363,6 +371,8 @@
 
   void GatherFrameTimingRequestIds(std::vector<int64_t>* request_ids);
 
+  void DidUpdateScrollState(int layer_id);
+
   bool IsAnimatingFilterProperty(const LayerImpl* layer) const;
   bool IsAnimatingOpacityProperty(const LayerImpl* layer) const;
   bool IsAnimatingTransformProperty(const LayerImpl* layer) const;
@@ -407,6 +417,8 @@
                                     float max_page_scale_factor);
   bool SetPageScaleFactorLimits(float min_page_scale_factor,
                                 float max_page_scale_factor);
+  bool IsViewportLayerId(int id) const;
+  void UpdateScrollbars(int scroll_layer_id, int clip_layer_id);
   void DidUpdatePageScale();
   void HideInnerViewportScrollbarsIfNeeded();
   void PushTopControls(const float* top_controls_shown_ratio);
@@ -438,6 +450,17 @@
   typedef base::hash_map<int, LayerImpl*> LayerIdMap;
   LayerIdMap layer_id_map_;
 
+  // Maps from clip layer ids to scroll layer ids.  Note that this only includes
+  // the subset of clip layers that act as scrolling containers.  (This is
+  // derived from LayerImpl::scroll_clip_layer_ and exists to avoid O(n) walks.)
+  base::hash_map<int, int> clip_scroll_map_;
+
+  // Maps scroll layer ids to scrollbar layer ids.  For each scroll layer, there
+  // may be 1 or 2 scrollbar layers (for vertical and horizontal).  (This is
+  // derived from ScrollbarLayerImplBase::scroll_layer_id_ and exists to avoid
+  // O(n) walks.)
+  std::multimap<int, int> scrollbar_map_;
+
   std::vector<PictureLayerImpl*> picture_layers_;
   std::vector<LayerImpl*> layers_with_copy_output_request_;
 
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 2907cc7..77e0749 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -1364,29 +1364,27 @@
 
   scoped_ptr<LayerImpl> clip_layer = LayerImpl::Create(active_tree, 4);
   scoped_ptr<LayerImpl> page_scale_layer = LayerImpl::Create(active_tree, 5);
+  scoped_ptr<LayerImpl> outer_scroll_layer = LayerImpl::Create(active_tree, 6);
 
   scroll_layer->SetScrollClipLayer(clip_layer->id());
 
   LayerImpl* scroll_layer_ptr = scroll_layer.get();
+  LayerImpl* outer_scroll_layer_ptr = outer_scroll_layer.get();
   LayerImpl* page_scale_layer_ptr = page_scale_layer.get();
 
+  scroll_layer->AddChild(outer_scroll_layer.Pass());
   clip_layer->AddChild(page_scale_layer.Pass());
   page_scale_layer_ptr->AddChild(scroll_layer.Pass());
 
-  vertical_scrollbar_layer->SetScrollLayerAndClipLayerByIds(
-      scroll_layer_ptr->id(),
-      clip_layer->id());
-  horizontal_scrollbar_layer->SetScrollLayerAndClipLayerByIds(
-      scroll_layer_ptr->id(),
-      clip_layer->id());
+  active_tree->SetViewportLayersFromIds(Layer::INVALID_ID,  // Overscroll
+                                        page_scale_layer_ptr->id(),
+                                        scroll_layer_ptr->id(),
+                                        outer_scroll_layer_ptr->id());
+
+  vertical_scrollbar_layer->SetScrollLayerId(outer_scroll_layer_ptr->id());
+  horizontal_scrollbar_layer->SetScrollLayerId(outer_scroll_layer_ptr->id());
 
   active_tree->PushPageScaleFromMainThread(1.0f, 1.0f, 4.0f);
-  active_tree->SetViewportLayersFromIds(
-      Layer::INVALID_ID,  // Overscroll
-      page_scale_layer_ptr->id(),
-      scroll_layer_ptr->id(),
-      Layer::INVALID_ID);  // Outer Scroll
-
   EXPECT_TRUE(vertical_scrollbar_layer->hide_layer_and_subtree());
   EXPECT_TRUE(horizontal_scrollbar_layer->hide_layer_and_subtree());
 
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index 3646dcaa..9c61acb 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -10,12 +10,8 @@
 #include "base/containers/scoped_ptr_hash_map.h"
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
-#include "cc/animation/scrollbar_animation_controller.h"
-#include "cc/input/scrollbar.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
-#include "cc/layers/scrollbar_layer_impl_base.h"
-#include "cc/layers/scrollbar_layer_interface.h"
 
 namespace cc {
 
@@ -28,13 +24,6 @@
   if (!layer_impl)
     return;
 
-  layer_impl->ClearScrollbars();
-  if (ScrollbarLayerImplBase* scrollbar_layer =
-          layer_impl->ToScrollbarLayer()) {
-    scrollbar_layer->ClearClipLayer();
-    scrollbar_layer->ClearScrollLayer();
-  }
-
   OwnedLayerImplList& children = layer_impl->children();
   for (OwnedLayerImplList::iterator it = children.begin();
        it != children.end();
@@ -64,8 +53,6 @@
   scoped_ptr<LayerImpl> new_tree = SynchronizeTreesRecursive(
       &new_layers, &old_layers, layer_root, tree_impl);
 
-  UpdateScrollbarLayerPointersRecursive(&new_layers, layer_root);
-
   return new_tree.Pass();
 }
 
@@ -143,46 +130,6 @@
       new_layers, old_layers, layer, tree_impl);
 }
 
-template <typename LayerType, typename ScrollbarLayerType>
-void UpdateScrollbarLayerPointersRecursiveInternal(
-    const RawPtrLayerImplMap* new_layers,
-    LayerType* layer) {
-  if (!layer)
-    return;
-
-  for (size_t i = 0; i < layer->children().size(); ++i) {
-    UpdateScrollbarLayerPointersRecursiveInternal<
-        LayerType, ScrollbarLayerType>(new_layers, layer->child_at(i));
-  }
-
-  ScrollbarLayerType* scrollbar_layer = layer->ToScrollbarLayer();
-  if (!scrollbar_layer)
-    return;
-
-  RawPtrLayerImplMap::const_iterator iter =
-      new_layers->find(layer->id());
-  ScrollbarLayerImplBase* scrollbar_layer_impl =
-      iter != new_layers->end()
-          ? static_cast<ScrollbarLayerImplBase*>(iter->second)
-          : NULL;
-  DCHECK(scrollbar_layer_impl);
-
-  scrollbar_layer->PushScrollClipPropertiesTo(scrollbar_layer_impl);
-}
-
-void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers,
-                                           Layer* layer) {
-  UpdateScrollbarLayerPointersRecursiveInternal<Layer, ScrollbarLayerInterface>(
-      new_layers, layer);
-}
-
-void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers,
-                                           LayerImpl* layer) {
-  UpdateScrollbarLayerPointersRecursiveInternal<
-      LayerImpl,
-      ScrollbarLayerImplBase>(new_layers, layer);
-}
-
 // static
 template <typename LayerType>
 void TreeSynchronizer::PushPropertiesInternal(
@@ -202,8 +149,6 @@
 
   if (push_layer)
     layer->PushPropertiesTo(layer_impl);
-  else if (layer->ToScrollbarLayer())
-    layer->ToScrollbarLayer()->PushScrollClipPropertiesTo(layer_impl);
 
   int num_dependents_need_push_properties = 0;
   if (recurse_on_children_and_dependents) {
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index ae7b4f74..da52490 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -872,6 +872,8 @@
     "//chrome/app:chromium_strings",
     "//chrome/app:generated_resources",
     "//chrome/app:google_chrome_strings",
+    "//chrome/app:settings_chromium_strings",
+    "//chrome/app:settings_google_chrome_strings",
     "//chrome/app:settings_strings",
     "//chrome/app/resources:locale_settings",
   ]
diff --git a/chrome/VERSION b/chrome/VERSION
index 7a23a47..bdb0bbb 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=48
 MINOR=0
-BUILD=2530
+BUILD=2531
 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 1fef19f4..e1d9d7a 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -37,6 +37,7 @@
     <uses-permission-sdk-m android:name="android.permission.BLUETOOTH_ADMIN"/>
 
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
diff --git a/chrome/android/java/ResourceId.template b/chrome/android/java/ResourceId.template
index 4894b5c4..78f5ee05 100644
--- a/chrome/android/java/ResourceId.template
+++ b/chrome/android/java/ResourceId.template
@@ -9,7 +9,8 @@
 public class ResourceId {
     public static int mapToDrawableId(int enumeratedId) {
         int[] resourceList = {
-#define DEFINE_RESOURCE_ID(c_id,java_id) java_id,
+#define LINK_RESOURCE_ID(c_id,java_id) java_id,
+#define DECLARE_RESOURCE_ID(c_id,java_id) java_id,
 #include "chrome/browser/android/resource_id.h"
         };
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index a2bd6d5..c8fe8bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -35,8 +35,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -217,8 +215,6 @@
     // Time in ms that it took took us to inflate the initial layout
     private long mInflateInitialLayoutDurationMs;
 
-    private OnPreDrawListener mFirstDrawListener;
-
     private final Locale mCurrentLocale = Locale.getDefault();
 
     private AssistStatusHandler mAssistStatusHandler;
@@ -299,23 +295,16 @@
 
         // Inform the WindowAndroid of the keyboard accessory view.
         mWindowAndroid.setKeyboardAccessoryView((ViewGroup) findViewById(R.id.keyboard_accessory));
-        final View controlContainer = findViewById(R.id.control_container);
-        if (controlContainer != null) {
-            mFirstDrawListener = new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    controlContainer.getViewTreeObserver()
-                            .removeOnPreDrawListener(mFirstDrawListener);
-                    mFirstDrawListener = null;
-                    onFirstDrawComplete();
-                    return true;
-                }
-            };
-            controlContainer.getViewTreeObserver().addOnPreDrawListener(mFirstDrawListener);
-        }
         initializeToolbar();
     }
 
+    @Override
+    protected View getViewToBeDrawnBeforeInitializingNative() {
+        View controlContainer = findViewById(R.id.control_container);
+        return controlContainer != null ? controlContainer
+                : super.getViewToBeDrawnBeforeInitializingNative();
+    }
+
     /**
      * This function builds the {@link CompositorViewHolder}.  Subclasses *must* call
      * super.setContentView() before using {@link #getTabModelSelector()} or
@@ -564,7 +553,7 @@
      * @param color The color that the status bar should be set to.
      */
     protected void setStatusBarColor(Tab tab, int color) {
-        int statusBarColor = (tab != null && tab.getDefaultThemeColor() == color)
+        int statusBarColor = (tab != null && tab.isDefaultThemeColor())
                 ? Color.BLACK : ColorUtils.getDarkenedColorForStatusBar(color);
         ApiCompatibilityUtils.setStatusBarColor(getWindow(), statusBarColor);
     }
@@ -639,12 +628,6 @@
         mIntentHandler.onNewIntent(this, intent);
     }
 
-    @Override
-    public boolean hasDoneFirstDraw() {
-        return mToolbarManager != null
-                ? mToolbarManager.hasDoneFirstDraw() : super.hasDoneFirstDraw();
-    }
-
     /**
      * @return Whether the given activity contains a {@link CustomTab}.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java
index 201718e..98c4d2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java
@@ -18,6 +18,7 @@
     private final String mHost;
     private final String mTitle;
     private final int mBackgroundColor;
+    private final int mThemeColor;
 
     /**
      * Creates a FrozenNativePage to replace the given NativePage and destroys the NativePage.
@@ -33,6 +34,7 @@
         mUrl = nativePage.getUrl();
         mTitle = nativePage.getTitle();
         mBackgroundColor = nativePage.getBackgroundColor();
+        mThemeColor = nativePage.getThemeColor();
     }
 
     @Override
@@ -62,6 +64,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return mThemeColor;
+    }
+
+    @Override
     public void updateForUrl(String url) {
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
index ffc7e7e..4392a0fd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
@@ -36,6 +36,11 @@
     int getBackgroundColor();
 
     /**
+     * @return The theme color of the page.
+     */
+    int getThemeColor();
+
+    /**
      * Updates the native page based on the given url.
      */
     void updateForUrl(String url);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index 40815c1..4d66d32c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -11,6 +11,7 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -183,8 +184,11 @@
             mPopup.setAnimationStyle(R.style.OverflowMenuAnim);
         }
 
-        // Turn off window animations for low end devices.
-        if (SysUtils.isLowEndDevice()) mPopup.setAnimationStyle(0);
+        // Turn off window animations for low end devices, and on Android M, which has built-in menu
+        // animations.
+        if (SysUtils.isLowEndDevice() || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            mPopup.setAnimationStyle(0);
+        }
 
         Rect bgPadding = new Rect();
         mPopup.getBackground().getPadding(bgPadding);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashFileManager.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashFileManager.java
index 2a78e0a..ed378f50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashFileManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashFileManager.java
@@ -47,15 +47,21 @@
 
     private static final Pattern TMP_PATTERN = Pattern.compile("\\.tmp\\z");
 
-    private static Comparator<File> sFileComparator =  new Comparator<File>() {
+    /**
+     * Comparator used for sorting files by modification
+     * Note that the behavior is undecided if the files are created at the same time
+     * @return Comparator for prioritizing the more recently modified file
+     */
+    @VisibleForTesting
+    protected static final Comparator<File> sFileComparator =  new Comparator<File>() {
         @Override
         public int compare(File lhs, File rhs) {
             if (lhs == rhs) {
                 return 0;
             } else if (lhs.lastModified() < rhs.lastModified()) {
-                return -1;
-            } else {
                 return 1;
+            } else {
+                return -1;
             }
         }
     };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java
index 01e9a69d..3a94ecf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java
@@ -801,8 +801,9 @@
 
     private void updateTaskDescription(String label, Bitmap icon) {
         int color = mDefaultThemeColor;
-        if (getActivityTab() != null) color = getActivityTab().getThemeColor();
-        boolean useDefaultStatusBarColor = isIncognito() || color == mDefaultThemeColor;
+        if (getActivityTab() != null && !getActivityTab().isDefaultThemeColor()) {
+            color = getActivityTab().getThemeColor();
+        }
         ApiCompatibilityUtils.setTaskDescription(this, label, icon, color);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 56eb1b9..a6e8113 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -466,7 +466,9 @@
                             boolean canResolve = canResolveDownloadItem(mContext, downloadId);
                             completionMap.put(
                                     progress.mDownloadInfo, Pair.create(downloadId, canResolve));
-                            mDownloadNotifier.notifyDownloadSuccessful(progress.mDownloadInfo);
+                            mDownloadNotifier.notifyDownloadSuccessful(
+                                    progress.mDownloadInfo,
+                                    getLaunchIntentFromDownloadId(mContext, downloadId));
                             broadcastDownloadSuccessful(progress.mDownloadInfo);
                         }
                         break;
@@ -505,7 +507,7 @@
         try {
             downloadId = manager.addCompletedDownload(
                     downloadInfo.getFileName(), description, true, mimeType,
-                    downloadInfo.getFilePath(), downloadInfo.getContentLength(), true);
+                    downloadInfo.getFilePath(), downloadInfo.getContentLength(), false);
             if (shouldOpenAfterDownload(downloadInfo)) {
                 handleAutoOpenAfterDownload(downloadInfo, downloadId);
             }
@@ -579,7 +581,7 @@
      *
      * @param downloadId Download Identifier.
      */
-    private void removeProgressNotificationForDownload(int downloadId) {
+    void removeProgressNotificationForDownload(int downloadId) {
         mDownloadProgressMap.remove(downloadId);
         mDownloadNotifier.cancelNotification(downloadId);
         removeDownloadIdFromSharedPrefs(downloadId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotifier.java
index 053a730..9ba4672 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotifier.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.download;
 
+import android.content.Intent;
+
 import org.chromium.content.browser.DownloadInfo;
 
 /**
@@ -13,8 +15,9 @@
     /**
      * Add a download successful notification.
      * @param downloadInfo info about the successful download.
+     * @param intent Intent to launch when clicking the download notification.
      */
-    void notifyDownloadSuccessful(DownloadInfo downloadInfo);
+    void notifyDownloadSuccessful(DownloadInfo downloadInfo, Intent intent);
 
     /**
      * Add a download failed notification.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
index fb0006c..fb17f0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.util.Pair;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.chrome.R;
@@ -25,10 +26,12 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public void onAction(Object actionData) {
-        Long downloadId = (Long) actionData;
-        DownloadManagerService.getDownloadManagerService(mContext).openDownloadedContent(
-                downloadId);
+        Pair<DownloadInfo, Long> download = (Pair<DownloadInfo, Long>) actionData;
+        DownloadManagerService manager = DownloadManagerService.getDownloadManagerService(mContext);
+        manager.openDownloadedContent(download.second);
+        manager.removeProgressNotificationForDownload(download.first.getDownloadId());
     }
 
     @Override
@@ -56,7 +59,7 @@
         if (canBeResolved) {
             snackbar.setAction(
                     mContext.getString(R.string.open_downloaded_label),
-                    downloadId);
+                    Pair.create(downloadInfo, downloadId));
         }
         getSnackbarManager().showSnackbar(snackbar);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
index 2430d2d..980d3537 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
@@ -6,7 +6,9 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.support.v4.app.NotificationCompat;
 
 import org.chromium.chrome.R;
@@ -53,14 +55,23 @@
     }
 
     @Override
-    public void notifyDownloadSuccessful(DownloadInfo downloadInfo) {
-        // TODO(qinmin): figure out what needs to be done here.
+    public void notifyDownloadSuccessful(DownloadInfo downloadInfo, Intent intent) {
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(mApplicationContext)
+                .setContentTitle(downloadInfo.getFileName())
+                .setSmallIcon(android.R.drawable.stat_sys_download_done)
+                .setOngoing(false)
+                .setLocalOnly(true)
+                .setAutoCancel(true)
+                .setContentText(mApplicationContext.getResources().getString(
+                        R.string.download_notification_completed))
+                .setContentIntent(PendingIntent.getActivity(
+                        mApplicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+        updateNotification(downloadInfo.getDownloadId(), builder.build());
     }
 
     @Override
     public void notifyDownloadFailed(DownloadInfo downloadInfo) {
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(
-                mApplicationContext)
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(mApplicationContext)
                 .setContentTitle(downloadInfo.getFileName())
                 .setSmallIcon(android.R.drawable.stat_sys_download_done)
                 .setOngoing(false)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/enhancedbookmarks/EnhancedBookmarkPage.java b/chrome/android/java/src/org/chromium/chrome/browser/enhancedbookmarks/EnhancedBookmarkPage.java
index 31fe999..f30da93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/enhancedbookmarks/EnhancedBookmarkPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/enhancedbookmarks/EnhancedBookmarkPage.java
@@ -27,6 +27,7 @@
     private final Tab mTab;
     private final String mTitle;
     private final int mBackgroundColor;
+    private final int mThemeColor;
     private EnhancedBookmarkManager mManager;
 
     /**
@@ -48,6 +49,8 @@
                 ? R.string.offline_pages_saved_pages : R.string.bookmarks);
         mBackgroundColor = ApiCompatibilityUtils.getColor(activity.getResources(),
                 R.color.default_primary_color);
+        mThemeColor = ApiCompatibilityUtils.getColor(
+                activity.getResources(), R.color.default_primary_color);
 
         mManager = new EnhancedBookmarkManager(mActivity);
         Resources res = mActivity.getResources();
@@ -88,6 +91,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return mThemeColor;
+    }
+
+    @Override
     public void updateForUrl(String url) {
         mManager.updateForUrl(url);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index 9a3636c..fadf50d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -89,7 +89,28 @@
     }
 
     @Override
-    public void postInflationStartup() { }
+    public void postInflationStartup() {
+        final View firstDrawView = getViewToBeDrawnBeforeInitializingNative();
+        assert firstDrawView != null;
+        ViewTreeObserver.OnPreDrawListener firstDrawListener =
+                new ViewTreeObserver.OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                firstDrawView.getViewTreeObserver().removeOnPreDrawListener(this);
+                onFirstDrawComplete();
+                return true;
+            }
+        };
+        firstDrawView.getViewTreeObserver().addOnPreDrawListener(firstDrawListener);
+    }
+
+    /**
+     * @return The primary view that must have completed at least one draw before initializing
+     *         native.  This must be non-null.
+     */
+    protected View getViewToBeDrawnBeforeInitializingNative() {
+        return findViewById(android.R.id.content);
+    }
 
     @Override
     public void maybePreconnect() {
@@ -271,11 +292,6 @@
     }
 
     @Override
-    public boolean hasDoneFirstDraw() {
-        return true;
-    }
-
-    @Override
     public void onNewIntentWithNative(Intent intent) { }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
index 5a3e17e..db04082a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
@@ -46,11 +46,6 @@
     boolean isActivityDestroyed();
 
     /**
-     * @return Whether the activity linked to the delegate has done its first draw.
-     */
-    boolean hasDoneFirstDraw();
-
-    /**
      * Called when the first draw for the UI specific to the linked activity is complete.
      */
     void onFirstDrawComplete();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/NativeInitializationController.java b/chrome/android/java/src/org/chromium/chrome/browser/init/NativeInitializationController.java
index d1866ed..f7beba74 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/NativeInitializationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/NativeInitializationController.java
@@ -37,6 +37,7 @@
     private List<Intent> mPendingNewIntents;
     private List<ActivityResult> mPendingActivityResults;
     private boolean mWaitingForFirstDraw;
+    private boolean mHasDoneFirstDraw;
     private boolean mInitializationComplete;
 
     /**
@@ -109,7 +110,7 @@
     }
 
     private void onLibraryLoaded() {
-        if (mActivityDelegate.hasDoneFirstDraw()) {
+        if (mHasDoneFirstDraw) {
             // First draw is done
             onNativeLibraryLoaded();
         } else {
@@ -122,6 +123,8 @@
      * load has to be completed to start the chromium browser process.
      */
     public void firstDrawComplete() {
+        mHasDoneFirstDraw = true;
+
         if (mWaitingForFirstDraw) {
             mWaitingForFirstDraw = false;
             // Allow the UI thread to continue its initialization
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/BookmarksPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/BookmarksPage.java
index 22753ae8..d5296fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/BookmarksPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/BookmarksPage.java
@@ -60,6 +60,7 @@
     private final BookmarksPageView mPageView;
     private final String mTitle;
     private final int mBackgroundColor;
+    private final int mThemeColor;
 
     // Whether destroy() has been called.
     private boolean mIsDestroyed;
@@ -279,6 +280,8 @@
         mFaviconHelper = new FaviconHelper();
         mTitle = context.getResources().getString(R.string.ntp_bookmarks);
         mBackgroundColor = ApiCompatibilityUtils.getColor(context.getResources(), R.color.ntp_bg);
+        mThemeColor = ApiCompatibilityUtils.getColor(
+                context.getResources(), R.color.default_primary_color);
         mCurrentFolderId = new BookmarkId(BookmarkId.INVALID_FOLDER_ID, BookmarkType.NORMAL);
         mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
 
@@ -535,6 +538,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return mThemeColor;
+    }
+
+    @Override
     public View getView() {
         return mPageView;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
index 63697888..763afdc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
@@ -27,6 +27,7 @@
 
     private final String mTitle;
     private final int mBackgroundColor;
+    private final int mThemeColor;
     private final IncognitoNewTabPageView mIncognitoNewTabPageView;
 
     private boolean mIsLoaded;
@@ -56,6 +57,8 @@
         mTitle = activity.getResources().getString(R.string.button_new_tab);
         mBackgroundColor =
                 ApiCompatibilityUtils.getColor(activity.getResources(), R.color.ntp_bg_incognito);
+        mThemeColor = ApiCompatibilityUtils.getColor(activity.getResources(),
+                R.color.incognito_primary_color);
 
         LayoutInflater inflater = LayoutInflater.from(activity);
         mIncognitoNewTabPageView =
@@ -94,6 +97,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return mThemeColor;
+    }
+
+    @Override
     public View getView() {
         return mIncognitoNewTabPageView;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index da16622c..5d7db9bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -9,6 +9,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -87,6 +88,7 @@
     private final Profile mProfile;
     private final String mTitle;
     private final int mBackgroundColor;
+    private final int mThemeColor;
     private final NewTabPageView mNewTabPageView;
 
     private MostVisitedSites mMostVisitedSites;
@@ -424,6 +426,8 @@
 
         mTitle = activity.getResources().getString(R.string.button_new_tab);
         mBackgroundColor = ApiCompatibilityUtils.getColor(activity.getResources(), R.color.ntp_bg);
+        mThemeColor = ApiCompatibilityUtils.getColor(
+                activity.getResources(), R.color.default_primary_color);
         TemplateUrlService.getInstance().addObserver(this);
 
         // Whether to show the promo can change within the lifetime of a single NTP instance
@@ -599,6 +603,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return isLocationBarShownInNTP() ? Color.WHITE : mThemeColor;
+    }
+
+    @Override
     public View getView() {
         return mNewTabPageView;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
index e0df36aa..75b3b99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -16,6 +16,7 @@
 import android.widget.ExpandableListView;
 
 import org.chromium.base.ActivityState;
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
@@ -52,6 +53,8 @@
     private int mSnapshotWidth;
     private int mSnapshotHeight;
 
+    private final int mThemeColor;
+
     /**
      * Whether the page is in the foreground and is visible.
      */
@@ -80,6 +83,8 @@
         mRecentTabsManager = recentTabsManager;
 
         mTitle = activity.getResources().getString(R.string.recent_tabs);
+        mThemeColor = ApiCompatibilityUtils.getColor(
+                activity.getResources(), R.color.default_primary_color);
         mRecentTabsManager.setUpdatedCallback(this);
         LayoutInflater inflater = LayoutInflater.from(activity);
         mView = (ViewGroup) inflater.inflate(R.layout.recent_tabs_page, null);
@@ -144,6 +149,11 @@
     }
 
     @Override
+    public int getThemeColor() {
+        return mThemeColor;
+    }
+
+    @Override
     public View getView() {
         return mView;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 5967116..2f654a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -736,7 +736,9 @@
                 color = mDefaultThemeColor;
             }
             if (color == Color.TRANSPARENT) color = mDefaultThemeColor;
+            if (isIncognito()) color = mDefaultThemeColor;
             color |= 0xFF000000;
+
             if (mThemeColor == color) return;
             mThemeColor = color;
             for (TabObserver observer : mObservers) {
@@ -1156,6 +1158,7 @@
      *         security state.
      */
     public int getThemeColor() {
+        if (isNativePage()) return mNativePage.getThemeColor();
         return mThemeColor;
     }
 
@@ -2858,10 +2861,10 @@
     }
 
     /**
-     * @return The default toolbar color for this tab.
+     * @return Whether the theme color for this tab is the default color.
      */
-    public int getDefaultThemeColor() {
-        return mDefaultThemeColor;
+    public boolean isDefaultThemeColor() {
+        return mDefaultThemeColor == mThemeColor;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
index 440776c2..2a50fce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
@@ -5,21 +5,13 @@
 package org.chromium.chrome.browser.tab;
 
 import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
 import android.os.Handler;
 import android.text.TextUtils;
-import android.util.Log;
 
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.MathUtils;
-import org.chromium.content.browser.ContentReadbackHandler;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.WebContents;
@@ -39,13 +31,14 @@
     private final Tab mTab;
     private final Handler mHandler;
 
-    private final int mThumbnailWidth;
-    private final int mThumbnailHeight;
+    private final int mThumbnailWidthDp;
+    private final int mThumbnailHeightDp;
 
     private ContentViewCore mContentViewCore;
     private boolean mThumbnailCapturedForLoad;
     private boolean mIsRenderViewHostReady;
     private boolean mWasRenderViewHostReady;
+    private String mRequestedUrl;
 
     private final Runnable mThumbnailRunnable = new Runnable() {
         @Override
@@ -61,33 +54,11 @@
                 mThumbnailCapturedForLoad = !mTab.isHidden();
                 return;
             }
-            if (!shouldUpdateThumbnail()) return;
+            if (mTab.getWebContents() == null) return;
 
-            int snapshotWidth = Math.min(mTab.getWidth(), mThumbnailWidth);
-            int snapshotHeight = Math.min(mTab.getHeight(), mThumbnailHeight);
-
-            ContentReadbackHandler readbackHandler = getActivity().getContentReadbackHandler();
-            if (readbackHandler == null || mTab.getContentViewCore() == null) return;
-            final String requestedUrl = mTab.getUrl();
-            ContentReadbackHandler.GetBitmapCallback bitmapCallback =
-                    new ContentReadbackHandler.GetBitmapCallback() {
-                        @Override
-                        public void onFinishGetBitmap(Bitmap bitmap, int response) {
-                            // Ensure that the URLs match for the requested page, and ensure
-                            // that the page is still valid for thumbnail capturing (i.e.
-                            // not showing an error page).
-                            if (bitmap == null
-                                    || !TextUtils.equals(requestedUrl, mTab.getUrl())
-                                    || !mThumbnailCapturedForLoad
-                                    || !canUpdateHistoryThumbnail()) {
-                                return;
-                            }
-                            updateHistoryThumbnail(bitmap);
-                            bitmap.recycle();
-                        }
-                    };
-            readbackHandler.getContentBitmapAsync(1, new Rect(0, 0, snapshotWidth, snapshotHeight),
-                    mTab.getContentViewCore(), Bitmap.Config.ARGB_8888, bitmapCallback);
+            mRequestedUrl = mTab.getUrl();
+            nativeCaptureThumbnail(ThumbnailTabHelper.this, mTab.getWebContents(),
+                    mThumbnailWidthDp, mThumbnailHeightDp);
         }
     };
 
@@ -196,7 +167,7 @@
      * @param tab The Tab whose thumbnails will be generated by this helper.
      */
     public static void createForTab(Tab tab) {
-        new ThumbnailTabHelper(tab);
+        if (!tab.isIncognito()) new ThumbnailTabHelper(tab);
     }
 
     /**
@@ -210,8 +181,11 @@
         mHandler = new Handler();
 
         Resources res = tab.getWindowAndroid().getApplicationContext().getResources();
-        mThumbnailWidth = res.getDimensionPixelSize(R.dimen.most_visited_thumbnail_width);
-        mThumbnailHeight = res.getDimensionPixelSize(R.dimen.most_visited_thumbnail_height);
+        float density = res.getDisplayMetrics().density;
+        mThumbnailWidthDp = Math.round(
+                res.getDimension(R.dimen.most_visited_thumbnail_width) / density);
+        mThumbnailHeightDp = Math.round(
+                res.getDimension(R.dimen.most_visited_thumbnail_height) / density);
 
         onContentChanged();
     }
@@ -249,17 +223,6 @@
         mHandler.postDelayed(mThumbnailRunnable, THUMBNAIL_CAPTURE_DELAY_MS);
     }
 
-    private boolean shouldUpdateThumbnail() {
-        return nativeShouldUpdateThumbnail(mTab.getProfile(), mTab.getUrl());
-    }
-
-    private void updateThumbnail(Bitmap bitmap) {
-        if (mTab.getContentViewCore() != null) {
-            final boolean atTop = mTab.getContentViewCore().computeVerticalScrollOffset() == 0;
-            nativeUpdateThumbnail(mTab.getWebContents(), bitmap, atTop);
-        }
-    }
-
     private boolean canUpdateHistoryThumbnail() {
         String url = mTab.getUrl();
         if (url.startsWith(UrlConstants.CHROME_SCHEME)
@@ -276,52 +239,17 @@
                 && mTab.getHeight() > 0;
     }
 
-    private void updateHistoryThumbnail(Bitmap bitmap) {
-        if (mTab.isIncognito()) return;
-
-        // TODO(yusufo): It will probably be faster and more efficient on resources to do this on
-        // the native side, but the thumbnail_generator code has to be refactored a bit to allow
-        // creating a downsized version of a bitmap progressively.
-        if (bitmap.getWidth() != mThumbnailWidth
-                || bitmap.getHeight() != mThumbnailHeight
-                || bitmap.getConfig() != Config.ARGB_8888) {
-            try {
-                int[] dim = new int[] {
-                        bitmap.getWidth(), bitmap.getHeight()
-                };
-                // If the thumbnail size is small compared to the bitmap size downsize in
-                // two stages. This makes the final quality better.
-                float scale = Math.max(
-                        (float) mThumbnailWidth / dim[0],
-                        (float) mThumbnailHeight / dim[1]);
-                int adjustedWidth = (scale < 1)
-                        ? mThumbnailWidth * (int) (1 / Math.sqrt(scale)) : mThumbnailWidth;
-                int adjustedHeight = (scale < 1)
-                        ? mThumbnailHeight * (int) (1 / Math.sqrt(scale)) : mThumbnailHeight;
-                scale = MathUtils.scaleToFitTargetSize(dim, adjustedWidth, adjustedHeight);
-                // Horizontally center the source bitmap in the final result.
-                float leftOffset = (adjustedWidth - dim[0]) / 2.0f / scale;
-                Bitmap tmpBitmap = Bitmap.createBitmap(adjustedWidth,
-                        adjustedHeight, Config.ARGB_8888);
-                Canvas c = new Canvas(tmpBitmap);
-                c.scale(scale, scale);
-                c.drawBitmap(bitmap, leftOffset, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
-                if (scale < 1) {
-                    tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
-                            mThumbnailWidth, mThumbnailHeight, true);
-                }
-                updateThumbnail(tmpBitmap);
-                tmpBitmap.recycle();
-            } catch (OutOfMemoryError ex) {
-                Log.w(TAG, "OutOfMemoryError while updating the history thumbnail.");
-            }
-        } else {
-            updateThumbnail(bitmap);
-        }
+    @CalledByNative
+    private boolean shouldSaveCapturedThumbnail() {
+        // Ensure that the URLs match for the requested page, and ensure
+        // that the page is still valid for thumbnail capturing (i.e.
+        // not showing an error page).
+        return TextUtils.equals(mRequestedUrl, mTab.getUrl())
+                && mThumbnailCapturedForLoad
+                && canUpdateHistoryThumbnail();
     }
 
     private static native void nativeInitThumbnailHelper(WebContents webContents);
-    private static native void nativeUpdateThumbnail(
-            WebContents webContents, Bitmap bitmap, boolean atTop);
-    private static native boolean nativeShouldUpdateThumbnail(Profile profile, String url);
+    private static native void nativeCaptureThumbnail(ThumbnailTabHelper thumbnailTabHelper,
+            WebContents webContents, int thumbnailWidthDp, int thumbnailHeightDp);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 3c39970c..9b81813 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -861,13 +861,6 @@
     }
 
     /**
-     * @return Whether {@link Toolbar} has drawn at least once.
-     */
-    public boolean hasDoneFirstDraw() {
-        return mToolbar.getFirstDrawTime() != 0;
-    }
-
-    /**
      * Handle all necessary tasks that can be delayed until initialization completes.
      * @param activityCreationTimeMs The time of creation for the activity this toolbar belongs to.
      * @param activityName Simple class name for the activity this toolbar belongs to.
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 201daecf..b5585a21 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1433,6 +1433,9 @@
       <message name="IDS_DOWNLOAD_NOTIFICATION_FAILED" desc="Download notification to be displayed when a download fails.">
         Download failed.
       </message>
+      <message name="IDS_DOWNLOAD_NOTIFICATION_COMPLETED" desc="Download notification to be displayed when a download completes.">
+        Download complete.
+      </message>
       <message name="IDS_DOWNLOAD_FAILED_MESSAGE" desc="Transient message shown when a file download has failed." meaning="Android">
         <ph name="FILE_NAME">%1$s<ex>http://abc.com/test.pdf</ex></ph> download failed
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index aff355a..174b782f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -1948,10 +1948,13 @@
 
     /**
      * Tests that long-press triggers the Peek Promo, and expanding the Panel dismisses it.
+     *
+     * Re-enable the test after fixing http://crbug.com/540820.
+     * @SmallTest
+     * @Feature({"ContextualSearch"})
      */
-    @SmallTest
-    @Feature({"ContextualSearch"})
     @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
+    @FlakyTest
     @CommandLineFlags.Add(ContextualSearchFieldTrial.PEEK_PROMO_ENABLED + "=true")
     public void testLongPressShowsPeekPromo() throws InterruptedException, TimeoutException {
         // Must be in undecided state in order to trigger the Peek Promo.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/CrashFileManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/CrashFileManagerTest.java
index 6d90489..0a05dc4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/CrashFileManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/CrashFileManagerTest.java
@@ -4,12 +4,16 @@
 
 package org.chromium.chrome.browser.crash;
 
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.util.Feature;
 
 import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
 import java.util.regex.Pattern;
 
 /**
@@ -20,6 +24,7 @@
 
     private File mTmpFile1;
     private File mTmpFile2;
+    private File mTmpFile3;
 
     private File mDmpFile1;
     private File mDmpFile2;
@@ -39,6 +44,9 @@
         mTmpFile2 = new File(mCrashDir, "abcde12345" + CrashFileManager.TMP_SUFFIX);
         mTmpFile2.createNewFile();
 
+        mTmpFile3 = new File(mCrashDir, "abcdefghi" + CrashFileManager.TMP_SUFFIX);
+        mTmpFile3.createNewFile();
+
         mDmpFile1 = new File(mCrashDir, "123_abc.dmp0");
         mDmpFile1.createNewFile();
 
@@ -72,25 +80,30 @@
         Pattern testPattern = Pattern.compile("^123");
         File[] actualFiles = crashFileManager.getMatchingFiles(testPattern);
         assertNotNull(actualFiles);
-        assertEquals(expectedFiles.length, actualFiles.length);
-        for (int i = 0; i < expectedFiles.length; i++) {
-            // Test to see if files are properly ordered.
-            assertEquals(expectedFiles[i], actualFiles[i]);
-        }
+        MoreAsserts.assertEquals("Failed to match file by pattern", expectedFiles, actualFiles);
+    }
+
+    @MediumTest
+    @Feature({"Android-AppBase"})
+    public void testFileComparator() throws IOException {
+        CrashFileManager crashFileManager = new CrashFileManager(mCacheDir);
+        File[] expectedFiles = new File[] {mTmpFile3, mTmpFile2, mTmpFile1};
+        File[] originalFiles = new File[] {mTmpFile1, mTmpFile2, mTmpFile3};
+        Arrays.sort(originalFiles, crashFileManager.sFileComparator);
+        assertNotNull(originalFiles);
+        MoreAsserts.assertEquals("File comparator failed to prioritize last modified file",
+                expectedFiles, originalFiles);
     }
 
     @SmallTest
     @Feature({"Android-AppBase"})
     public void testGetAllMinidumpFilesSorted() {
         CrashFileManager crashFileManager = new CrashFileManager(mCacheDir);
-        File[] expectedFiles = new File[] {mDmpFile1, mDmpFile2};
+        File[] expectedFiles = new File[] {mDmpFile2, mDmpFile1};
         File[] actualFiles = crashFileManager.getAllMinidumpFilesSorted();
         assertNotNull(actualFiles);
-        assertEquals(expectedFiles.length, actualFiles.length);
-        for (int i = 0; i < expectedFiles.length; i++) {
-            // Test to see if files are properly ordered.
-            assertEquals(expectedFiles[i], actualFiles[i]);
-        }
+        MoreAsserts.assertEquals("Failed to sort minidumps by modification time", expectedFiles,
+                actualFiles);
     }
 
     @SmallTest
@@ -105,6 +118,7 @@
     @SmallTest
     @Feature({"Android-AppBase"})
     public void testDeleteFile() {
+        assertTrue(mTmpFile1.exists());
         assertTrue(CrashFileManager.deleteFile(mTmpFile1));
         assertFalse(mTmpFile1.exists());
     }
@@ -116,11 +130,8 @@
         File[] expectedFiles = new File[] { mDmpFile1, mDmpFile2 };
         File[] actualFiles = crashFileManager.getAllMinidumpFiles();
         assertNotNull(actualFiles);
-        assertEquals(expectedFiles.length, actualFiles.length);
-        for (int i = 0; i < expectedFiles.length; i++) {
-            // Test to see if files are properly ordered.
-            assertEquals(expectedFiles[i], actualFiles[i]);
-        }
+        MoreAsserts.assertEquals("Failed to get the correct minidump files in directory",
+                expectedFiles, actualFiles);
     }
 
     @SmallTest
@@ -130,11 +141,8 @@
         File[] expectedFiles = new File[] { mUpFile1, mUpFile2 };
         File[] actualFiles = crashFileManager.getAllUploadedFiles();
         assertNotNull(actualFiles);
-        assertEquals(expectedFiles.length, actualFiles.length);
-        for (int i = 0; i < expectedFiles.length; i++) {
-            // Test to see if files are properly ordered.
-            assertEquals(expectedFiles[i], actualFiles[i]);
-        }
+        MoreAsserts.assertEquals("Failed to get the correct uploaded files in directory",
+                expectedFiles, actualFiles);
     }
 
     @SmallTest
@@ -165,7 +173,7 @@
     @SmallTest
     @Feature({"Android-AppBase"})
     public void testCleanAllMiniDumps() {
-        assertEquals(6, mCrashDir.listFiles().length);
+        assertEquals(7, mCrashDir.listFiles().length);
 
         CrashFileManager crashFileManager = new CrashFileManager(mCacheDir);
         crashFileManager.cleanAllMiniDumps();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index 62e5d5f..debc6cb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -6,6 +6,7 @@
 
 import android.app.DownloadManager;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.test.InstrumentationTestCase;
@@ -101,7 +102,7 @@
         }
 
         @Override
-        public void notifyDownloadSuccessful(DownloadInfo downloadInfo) {
+        public void notifyDownloadSuccessful(DownloadInfo downloadInfo, Intent intent) {
             assertCorrectExpectedCall(MethodID.DOWNLOAD_SUCCESSFUL, downloadInfo);
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java
index b1aabdd9..0f1ed39 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java
@@ -68,6 +68,11 @@
         }
 
         @Override
+        public int getThemeColor() {
+            return 0;
+        }
+
+        @Override
         public View getView() {
             return null;
         }
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index a5c37e7..f1ad048 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -279,6 +279,37 @@
   ]
 }
 
+# GYP version: chrome/settings_chromium_strings.gyp:settings_chromium_strings
+#              (generate_settings_chromium_strings action)
+grit("settings_chromium_strings") {
+  source = "settings_chromium_strings.grd"
+  output_dir = "$root_gen_dir/chrome"
+  use_qualified_include = true
+  outputs = [
+    "grit/settings_chromium_strings.h",
+
+    # These strings are not being treated as strings because they are
+    # not translated (English only), this should change in late 2015.
+    "settings_chromium_strings.pak",
+  ]
+}
+
+# GYP version:
+#     chrome/settings_google_chrome_strings.gyp:settings_google_chrome_strings
+#              (generate_settings_google_chrome_strings action)
+grit("settings_google_chrome_strings") {
+  source = "settings_google_chrome_strings.grd"
+  output_dir = "$root_gen_dir/chrome"
+  use_qualified_include = true
+  outputs = [
+    "grit/settings_google_chrome_strings.h",
+
+    # These strings are not being treated as strings because they are
+    # not translated (English only), this should change in late 2015.
+    "settings_google_chrome_strings.pak",
+  ]
+}
+
 source_set("test_support") {
   testonly = true
   visibility = [ "//chrome/test:test_support" ]
diff --git a/chrome/app/chrome_watcher_client_unittest_win.cc b/chrome/app/chrome_watcher_client_unittest_win.cc
index 20d1d32..e3549e9 100644
--- a/chrome/app/chrome_watcher_client_unittest_win.cc
+++ b/chrome/app/chrome_watcher_client_unittest_win.cc
@@ -6,6 +6,7 @@
 
 #include <windows.h>
 #include <string>
+
 #include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 3b66d29c..8fbedbd 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -133,9 +133,6 @@
   </translations>
   <release seq="1" allow_pseudo="false">
     <messages fallback_to_english="true">
-      <!-- Settings specific strings -->
-      <part file="settings_chromium_strings.grdp" />
-
       <message name="IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT" desc="Message explaining to the user what will happen if they disconnect the managed profile.">
         Disconnecting <ph name="USERNAME">$1<ex>someone@example.com</ex></ph> will clear your history, bookmarks, settings, and other Chromium data stored on this device. Data stored in your Google Account will not be cleared and can be managed on <ph name="GOOGLE_DASHBOARD_LINK">&lt;a target="_blank" href="$2"&gt;</ph>Google Dashboard<ph name="END_GOOGLE_DASHBOARD_LINK">&lt;/a&gt;</ph>.
       </message>
diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc
index 4a156a7..f169bfd 100644
--- a/chrome/app/client_util.cc
+++ b/chrome/app/client_util.cc
@@ -6,6 +6,7 @@
 #include <shlwapi.h>
 
 #include "base/base_paths.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/environment.h"
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 1ad02c31d..77d6aea 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -135,9 +135,6 @@
   </translations>
   <release seq="1" allow_pseudo="false">
     <messages fallback_to_english="true">
-      <!-- Settings specific strings -->
-      <part file="settings_google_chrome_strings.grdp" />
-
       <message name="IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT" desc="Message explaining to the user what will happen if they disconnect the managed profile.">
         Disconnecting <ph name="USERNAME">$1<ex>someone@example.com</ex></ph> will clear your history, bookmarks, settings, and other Chrome data stored on this device. Data stored in your Google Account will not be cleared and can be managed on <ph name="GOOGLE_DASHBOARD_LINK">&lt;a target="_blank" href="$2"&gt;</ph>Google Dashboard<ph name="END_GOOGLE_DASHBOARD_LINK">&lt;/a&gt;</ph>.
       </message>
diff --git a/chrome/app/settings_chromium_strings.grd b/chrome/app/settings_chromium_strings.grd
new file mode 100644
index 0000000..df8a8f5
--- /dev/null
+++ b/chrome/app/settings_chromium_strings.grd
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+This file contains the Material Design settings strings.
+While under development, it is set to not be translated,
+in translation_expectations.pyl.
+Once the Material Design settings is further along, these
+strings will begin to be translated.
+-->
+<grit base_dir="." latest_public_release="0" current_release="1"
+      source_lang_id="en" enc_check="möl">
+  <outputs>
+    <output filename="grit/settings_chromium_strings.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="settings_chromium_strings.pak" type="data_package"/>
+  </outputs>
+
+  <release seq="1" allow_pseudo="false">
+    <messages fallback_to_english="true">
+
+      <!-- Settings specific strings -->
+      <part file="settings_chromium_strings.grdp" />
+
+    </messages>
+  </release>
+</grit>
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index beb31be..7c58c0a 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -1,6 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Settings-specific Chromium strings (included from chromium_strings.grd). -->
 <grit-part>
+  <!-- Default Browser Page -->
+  <if expr="not chromeos">
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT" desc="The text displayed when Chrome is not the default browser">
+      The default browser is currently Chromium.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_NOT_DEFAULT" desc="The text displayed when Chrome is not the default browser">
+      Chromium is not currently your default browser.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT" desc="Default browser checkbox label">
+      Make Chromium the default browser
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_UNKNOWN" desc="The text displayed when Chrome cannot determine or set the default browser">
+      Chromium cannot determine or set the default browser.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY" desc="The text displayed when Chrome is installed in side-by-side mode, which does not support setting as the default browser.">
+      This is a secondary installation of Chromium, and cannot be made your default browser.
+    </message>
+  </if>
+
   <!-- Privacy Page -->
   <message name="IDS_SETTINGS_IMPROVE_BROWSING_EXPERIENCE" desc="The text in the options panel that describes how we use web services to improve browsing experience.">
     Chromium may use <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>web services<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> to improve your browsing experience. You may optionally disable these services at any time.
diff --git a/chrome/app/settings_google_chrome_strings.grd b/chrome/app/settings_google_chrome_strings.grd
new file mode 100644
index 0000000..68c71e2
--- /dev/null
+++ b/chrome/app/settings_google_chrome_strings.grd
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+This file contains the Material Design settings strings.
+While under development, it is set to not be translated,
+in translation_expectations.pyl.
+Once the Material Design settings is further along, these
+strings will begin to be translated.
+-->
+<grit base_dir="." latest_public_release="0" current_release="1"
+      source_lang_id="en" enc_check="möl">
+  <outputs>
+    <output filename="grit/settings_google_chrome_strings.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="settings_google_chrome_strings.pak" type="data_package"/>
+  </outputs>
+
+  <release seq="1" allow_pseudo="false">
+    <messages fallback_to_english="true">
+
+      <!-- Settings specific strings -->
+      <part file="settings_google_chrome_strings.grdp" />
+
+    </messages>
+  </release>
+</grit>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 772f056f..29c00e6 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -1,6 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Settings-specific Google Chrome strings (included from google_chrome_strings.grd). -->
 <grit-part>
+  <!-- Default Browser Page -->
+  <if expr="not chromeos">
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT" desc="The text displayed when Chrome is not the default browser">
+      The default browser is currently Google Chrome.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_NOT_DEFAULT" desc="The text displayed when Chrome is not the default browser">
+      Google Chrome is not currently your default browser.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT" desc="Default browser checkbox label">
+      Make Google Chrome the default browser
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_UNKNOWN" desc="The text displayed when Chrome cannot determine or set the default browser">
+      Google Chrome cannot determine or set the default browser.
+    </message>
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY" desc="The text displayed when Chrome is installed in side-by-side mode, which does not support setting as the default browser.">
+      This is a secondary installation of Google Chrome, and cannot be made your default browser.
+    </message>
+  </if>
+
   <!-- Privacy Page -->
   <message name="IDS_SETTINGS_IMPROVE_BROWSING_EXPERIENCE" desc="The text in the options panel that describes how we use web services to improve browsing experience.">
     Google Chrome may use <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>web services<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> to improve your browsing experience. You may optionally disable these services at any time.
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index be08601..582e662 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -104,6 +104,13 @@
     Basic
   </message>
 
+  <!-- Default Browser Page -->
+  <if expr="not chromeos">
+    <message name="IDS_SETTINGS_DEFAULT_BROWSER" desc="Name of the Default Browser page, which allows users to set which browser will open .html files within the OS.">
+      Default browser
+    </message>
+  </if>
+
   <!-- Certificate Manager Page -->
   <message name="IDS_SETTINGS_CERTIFICATE_MANAGER" desc="Name of the certificate manager page which allows users to modify SSL certificate settings.">
     Certificate manager
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index bc10356..17e91d6 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -323,19 +323,19 @@
       <if expr="not is_android and not is_ios">
         <!-- TODO(estade): replace with vector image when one's available. -->
         <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_INCOGNITO" file="common/incognito.png" />
-        <if expr="is_macosx">
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR" file="legacy/avatar_menu_auth_error.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR" file="legacy/avatar_button_auth_error.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_CAMERA" file="legacy/edit_camera.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_HOVER" file="legacy/edit_button_hover.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_PRESSED" file="legacy/edit_button_pressed.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_LEGACY_SUPERVISED" file="legacy/icon_legacy_supervised.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_AVATAR" file="legacy/avatar_menu_profile.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_CHILD" file="legacy/avatar_menu_child.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_DISCONNECT" file="legacy/avatar_menu_disconnect.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_LEGACY_SUPERVISED" file="legacy/avatar_menu_legacy_supervised.png" />
-          <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_LOCK" file="legacy/avatar_menu_lock.png" />
-        </if>
+      </if>
+      <if expr="is_macosx">
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR" file="legacy/avatar_menu_auth_error.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR" file="legacy/avatar_button_auth_error.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_CAMERA" file="legacy/edit_camera.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_HOVER" file="legacy/edit_button_hover.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_EDIT_PRESSED" file="legacy/edit_button_pressed.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_LEGACY_SUPERVISED" file="legacy/icon_legacy_supervised.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_AVATAR" file="legacy/avatar_menu_profile.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_CHILD" file="legacy/avatar_menu_child.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_DISCONNECT" file="legacy/avatar_menu_disconnect.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_LEGACY_SUPERVISED" file="legacy/avatar_menu_legacy_supervised.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_PROFILES_MENU_LOCK" file="legacy/avatar_menu_lock.png" />
       </if>
       <if expr="not is_android and not is_ios and not chromeos">
         <!-- User Manager tutorial -->
@@ -387,17 +387,6 @@
         <structure type="chrome_scaled_image" name="IDR_INFOBAR_AUTOLOGIN" file="common/infobar_autologin.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_INFOBAR_COOKIE" file="common/infobar_cookie.png" />
-      <if expr="is_android">
-        <!-- The actual image here is not used; this line only exists to
-             reserve a valid ID, so the Android code can map it to the
-             appropriate Android resource.  So we just refer to a random
-             image. -->
-        <structure type="chrome_scaled_image" name="IDR_INFOBAR_DESKTOP_NOTIFICATIONS" file="chromium/product_logo_32.png" />
-        <structure type="chrome_scaled_image" name="IDR_INFOBAR_FROZEN_TAB" file="chromium/product_logo_32.png" />
-        <structure type="chrome_scaled_image" name="IDR_INFOBAR_FULLSCREEN" file="chromium/product_logo_32.png" />
-        <structure type="chrome_scaled_image" name="IDR_INFOBAR_GEOLOCATION" file="chromium/product_logo_32.png" />
-        <structure type="chrome_scaled_image" name="IDR_INFOBAR_PROTECTED_MEDIA_IDENTIFIER" file="chromium/product_logo_32.png" />
-      </if>
       <if expr="is_macosx">
         <structure type="chrome_scaled_image" name="IDR_INFOBAR_DESKTOP_NOTIFICATIONS" file="legacy/infobar_desktop_notifications.png" />
         <structure type="chrome_scaled_image" name="IDR_INFOBAR_GEOLOCATION" file="legacy/infobar_geolocation.png" />
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 867e6d8d..783d064b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1080,6 +1080,12 @@
 
   if (is_android) {
     sources -= [
+      "download/test_download_shelf.cc",
+      "download/test_download_shelf.h",
+      "profile_resetter/profile_resetter_test_base.cc",
+      "profile_resetter/profile_resetter_test_base.h",
+      "sessions/session_restore_test_helper.cc",
+      "sessions/session_restore_test_helper.h",
       "sessions/session_service_test_helper.cc",
       "sessions/session_service_test_helper.h",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d21c7d4..63237e4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -26,6 +26,7 @@
 #include "chrome/grit/google_chrome_strings.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/enhanced_bookmarks/enhanced_bookmark_switches.h"
 #include "components/flags_ui/flags_storage.h"
@@ -33,9 +34,11 @@
 #include "components/nacl/common/nacl_switches.h"
 #include "components/offline_pages/offline_page_switches.h"
 #include "components/omnibox/browser/omnibox_switches.h"
+#include "components/password_manager/core/common/password_manager_switches.h"
 #include "components/plugins/common/plugins_switches.h"
 #include "components/proximity_auth/switches.h"
 #include "components/search/search_switches.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "components/sync_driver/sync_driver_switches.h"
 #include "components/tracing/tracing_switches.h"
 #include "components/version_info/version_info.h"
diff --git a/chrome/browser/android/android_theme_resources.h b/chrome/browser/android/android_theme_resources.h
new file mode 100644
index 0000000..34599b05
--- /dev/null
+++ b/chrome/browser/android/android_theme_resources.h
@@ -0,0 +1,23 @@
+// 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 CHROME_BROWSER_ANDROID_ANDROID_THEME_RESOURCES_H_
+#define CHROME_BROWSER_ANDROID_ANDROID_THEME_RESOURCES_H_
+
+// LINK_RESOURCE_ID will use an ID defined by grit, so no-op.
+#define LINK_RESOURCE_ID(c_id, java_id)
+// For DECLARE_RESOURCE_ID, make an entry in an enum.
+#define DECLARE_RESOURCE_ID(c_id, java_id) c_id,
+
+enum {
+  // Not used; just provides a starting value for the enum. These must
+  // not conflict with IDR_* values, which top out at 2^16 - 1.
+  ANDROID_RESOURCE_ID_NONE = 1 << 16,
+#include "chrome/browser/android/resource_id.h"
+};
+
+#undef LINK_RESOURCE_ID
+#undef DECLARE_RESOURCE_ID
+
+#endif  // CHROME_BROWSER_ANDROID_ANDROID_THEME_RESOURCES_H_
diff --git a/chrome/browser/android/fullscreen/fullscreen_infobar_delegate.cc b/chrome/browser/android/fullscreen/fullscreen_infobar_delegate.cc
index a202c776..cf96e89 100644
--- a/chrome/browser/android/fullscreen/fullscreen_infobar_delegate.cc
+++ b/chrome/browser/android/fullscreen/fullscreen_infobar_delegate.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_string.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_service.h"
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -15,7 +16,6 @@
 #include "components/infobars/core/infobar.h"
 #include "components/url_formatter/elide_url.h"
 #include "grit/components_strings.h"
-#include "grit/theme_resources.h"
 #include "jni/FullscreenInfoBarDelegate_jni.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -60,7 +60,7 @@
 }
 
 int FullscreenInfoBarDelegate::GetIconId() const {
-  return IDR_INFOBAR_FULLSCREEN;
+  return IDR_ANDROID_INFOBAR_FULLSCREEN;
 }
 
 base::string16 FullscreenInfoBarDelegate::GetMessageText() const {
diff --git a/chrome/browser/android/hung_renderer_infobar_delegate.cc b/chrome/browser/android/hung_renderer_infobar_delegate.cc
index 40473a9..7b35b1c9 100644
--- a/chrome/browser/android/hung_renderer_infobar_delegate.cc
+++ b/chrome/browser/android/hung_renderer_infobar_delegate.cc
@@ -6,12 +6,12 @@
 
 #include "base/callback.h"
 #include "base/metrics/histogram.h"
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/infobar.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/result_codes.h"
-#include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 // static
@@ -48,7 +48,7 @@
 }
 
 int HungRendererInfoBarDelegate::GetIconId() const {
-  return IDR_INFOBAR_FROZEN_TAB;
+  return IDR_ANDROID_INFOBAR_FROZEN_TAB;
 }
 
 base::string16 HungRendererInfoBarDelegate::GetMessageText() const {
diff --git a/chrome/browser/android/resource_id.h b/chrome/browser/android/resource_id.h
index 68c5e466..e6dfd66 100644
--- a/chrome/browser/android/resource_id.h
+++ b/chrome/browser/android/resource_id.h
@@ -2,69 +2,71 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ANDROID_RESOURCE_ID_H_
-#define CHROME_BROWSER_ANDROID_RESOURCE_ID_H_
-
 // This file maps Chromium resource IDs to Android resource IDs.
-#ifndef DEFINE_RESOURCE_ID
-#error "DEFINE_RESOURCE_ID should be defined before including this file"
+
+// LINK_RESOURCE_ID is used for IDs that come from a .grd file.
+#ifndef LINK_RESOURCE_ID
+#error "LINK_RESOURCE_ID should be defined before including this file"
+#endif
+// DECLARE_RESOURCE_ID is used for IDs that don't have .grd entries, and
+// are only declared in this file.
+#ifndef DECLARE_RESOURCE_ID
+#error "DECLARE_RESOURCE_ID should be defined before including this file"
 #endif
 
 // Create a mapping that identifies when a resource isn't being passed in.
-DEFINE_RESOURCE_ID(0, 0)
+LINK_RESOURCE_ID(0, 0)
 
 // InfoBar resources.
-DEFINE_RESOURCE_ID(IDR_INFOBAR_AUTOFILL_CC, R.drawable.infobar_autofill_cc)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_DESKTOP_NOTIFICATIONS,\
-                   R.drawable.infobar_desktop_notifications)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_GEOLOCATION, R.drawable.infobar_geolocation)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_MEDIA_STREAM_CAMERA, R.drawable.infobar_camera)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_MEDIA_STREAM_MIC, R.drawable.infobar_microphone)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_MIDI, R.drawable.infobar_midi)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_MULTIPLE_DOWNLOADS,\
-                   R.drawable.infobar_multiple_downloads)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_PROTECTED_MEDIA_IDENTIFIER,
-                   R.drawable.infobar_protected_media_identifier)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_SAVE_PASSWORD,\
-                   R.drawable.infobar_savepassword)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_WARNING, R.drawable.infobar_warning)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_TRANSLATE, R.drawable.infobar_translate)
-DEFINE_RESOURCE_ID(IDR_BLOCKED_POPUPS, R.drawable.infobar_blocked_popups)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_FROZEN_TAB, R.drawable.infobar_restore)
-DEFINE_RESOURCE_ID(IDR_INFOBAR_FULLSCREEN, R.drawable.infobar_fullscreen)
+LINK_RESOURCE_ID(IDR_INFOBAR_AUTOFILL_CC, R.drawable.infobar_autofill_cc)
+LINK_RESOURCE_ID(IDR_INFOBAR_MEDIA_STREAM_CAMERA, R.drawable.infobar_camera)
+LINK_RESOURCE_ID(IDR_INFOBAR_MEDIA_STREAM_MIC, R.drawable.infobar_microphone)
+LINK_RESOURCE_ID(IDR_INFOBAR_MIDI, R.drawable.infobar_midi)
+LINK_RESOURCE_ID(IDR_INFOBAR_MULTIPLE_DOWNLOADS,
+                 R.drawable.infobar_multiple_downloads)
+LINK_RESOURCE_ID(IDR_INFOBAR_SAVE_PASSWORD, R.drawable.infobar_savepassword)
+LINK_RESOURCE_ID(IDR_INFOBAR_WARNING, R.drawable.infobar_warning)
+LINK_RESOURCE_ID(IDR_INFOBAR_TRANSLATE, R.drawable.infobar_translate)
+LINK_RESOURCE_ID(IDR_BLOCKED_POPUPS, R.drawable.infobar_blocked_popups)
+
+// Android only infobars.
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_PROTECTED_MEDIA_IDENTIFIER,
+                    R.drawable.infobar_protected_media_identifier)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_NOTIFICATIONS,
+                    R.drawable.infobar_desktop_notifications)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_GEOLOCATION,
+                    R.drawable.infobar_geolocation)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_FROZEN_TAB, R.drawable.infobar_restore)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_FULLSCREEN,
+                    R.drawable.infobar_fullscreen)
 
 // WebsiteSettingsUI images, used in ConnectionInfoPopup
 // Good:
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_GOOD, R.drawable.pageinfo_good)
+LINK_RESOURCE_ID(IDR_PAGEINFO_GOOD, R.drawable.pageinfo_good)
 // Warnings:
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_WARNING_MINOR,\
-                   R.drawable.pageinfo_warning)
+LINK_RESOURCE_ID(IDR_PAGEINFO_WARNING_MINOR, R.drawable.pageinfo_warning)
 // Bad:
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_BAD, R.drawable.pageinfo_bad)
+LINK_RESOURCE_ID(IDR_PAGEINFO_BAD, R.drawable.pageinfo_bad)
 // Should never occur, use warning just in case:
 // Enterprise managed: ChromeOS only.
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_ENTERPRISE_MANAGED,\
-                   R.drawable.pageinfo_warning)
+LINK_RESOURCE_ID(IDR_PAGEINFO_ENTERPRISE_MANAGED, R.drawable.pageinfo_warning)
 // Info: Only shown on chrome:// urls, which don't show the connection info
 // popup.
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_INFO, R.drawable.pageinfo_warning)
+LINK_RESOURCE_ID(IDR_PAGEINFO_INFO, R.drawable.pageinfo_warning)
 // Major warning: Used on insecure pages, which don't show the connection info
 // popup.
-DEFINE_RESOURCE_ID(IDR_PAGEINFO_WARNING_MAJOR,\
-                   R.drawable.pageinfo_warning)
+LINK_RESOURCE_ID(IDR_PAGEINFO_WARNING_MAJOR, R.drawable.pageinfo_warning)
 
 // Autofill popup and keyboard accessory images.
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_AMEX, R.drawable.amex_card)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_DISCOVER, R.drawable.discover_card)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_GENERIC, R.drawable.generic_card)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_MASTERCARD, R.drawable.mc_card)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_VISA, R.drawable.visa_card)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_SCAN_NEW, android.R.drawable.ic_menu_camera)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_CC_SCAN_NEW_KEYBOARD_ACCESSORY,
-                   org.chromium.chrome.R.drawable.ic_photo_camera)
-DEFINE_RESOURCE_ID(IDR_CREDIT_CARD_CVC_HINT, R.drawable.cvc_icon)
-DEFINE_RESOURCE_ID(IDR_CREDIT_CARD_CVC_HINT_AMEX, R.drawable.cvc_icon_amex)
-DEFINE_RESOURCE_ID(IDR_AUTOFILL_SETTINGS,
-                   org.chromium.chrome.R.drawable.ic_settings)
-
-#endif  // CHROME_BROWSER_ANDROID_RESOURCE_ID_H_
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_AMEX, R.drawable.amex_card)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_DISCOVER, R.drawable.discover_card)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_GENERIC, R.drawable.generic_card)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_MASTERCARD, R.drawable.mc_card)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_VISA, R.drawable.visa_card)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_SCAN_NEW, android.R.drawable.ic_menu_camera)
+LINK_RESOURCE_ID(IDR_AUTOFILL_CC_SCAN_NEW_KEYBOARD_ACCESSORY,
+                 org.chromium.chrome.R.drawable.ic_photo_camera)
+LINK_RESOURCE_ID(IDR_CREDIT_CARD_CVC_HINT, R.drawable.cvc_icon)
+LINK_RESOURCE_ID(IDR_CREDIT_CARD_CVC_HINT_AMEX, R.drawable.cvc_icon_amex)
+LINK_RESOURCE_ID(IDR_AUTOFILL_SETTINGS,
+                 org.chromium.chrome.R.drawable.ic_settings)
diff --git a/chrome/browser/android/resource_mapper.cc b/chrome/browser/android/resource_mapper.cc
index 545caf71..ed4e66e 100644
--- a/chrome/browser/android/resource_mapper.cc
+++ b/chrome/browser/android/resource_mapper.cc
@@ -8,6 +8,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "chrome/browser/android/android_theme_resources.h"
 #include "grit/components_scaled_resources.h"
 #include "grit/theme_resources.h"
 
@@ -39,7 +40,9 @@
   DCHECK(g_id_map.Get().empty());
   int next_id = 0;
 
-#define DEFINE_RESOURCE_ID(c_id,java_id) g_id_map.Get()[c_id] = next_id++;
+#define LINK_RESOURCE_ID(c_id, java_id) g_id_map.Get()[c_id] = next_id++;
+#define DECLARE_RESOURCE_ID(c_id, java_id) g_id_map.Get()[c_id] = next_id++;
 #include "chrome/browser/android/resource_id.h"
-#undef DEFINE_RESOURCE_ID
+#undef LINK_RESOURCE_ID
+#undef DECLARE_RESOURCE_ID
 }
diff --git a/chrome/browser/android/tab/thumbnail_tab_helper_android.cc b/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
index 17c0cae..c93d41c 100644
--- a/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
+++ b/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
@@ -5,23 +5,108 @@
 #include "chrome/browser/android/tab/thumbnail_tab_helper_android.h"
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
 #include "chrome/browser/thumbnails/thumbnail_service.h"
 #include "chrome/browser/thumbnails/thumbnail_service_factory.h"
 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
-#include "components/history/core/browser/top_sites.h"
+#include "chrome/browser/thumbnails/thumbnailing_algorithm.h"
+#include "chrome/browser/thumbnails/thumbnailing_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/ThumbnailTabHelper_jni.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/color_utils.h"
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 
+using thumbnails::ThumbnailingAlgorithm;
+using thumbnails::ThumbnailingContext;
+using thumbnails::ThumbnailService;
+
+namespace {
+
+const int kScrollbarWidthDp = 6;
+
+void UpdateThumbnail(const ThumbnailingContext& context,
+                     const SkBitmap& thumbnail) {
+  gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail);
+  context.service->SetPageThumbnail(context, image);
+}
+
+void ProcessCapturedBitmap(
+    const base::android::ScopedJavaGlobalRef<jobject>& jthumbnail_tab_helper,
+    scoped_refptr<ThumbnailingContext> context,
+    scoped_refptr<ThumbnailingAlgorithm> algorithm,
+    const SkBitmap& bitmap,
+    content::ReadbackResponse response) {
+  if (response != content::READBACK_SUCCESS)
+    return;
+
+  // On success, we must be on the UI thread (on failure because of shutdown we
+  // are not on the UI thread).
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (!Java_ThumbnailTabHelper_shouldSaveCapturedThumbnail(
+          env, jthumbnail_tab_helper.obj())) {
+    return;
+  }
+
+  algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
+}
+
+void CaptureThumbnailInternal(
+    const base::android::ScopedJavaGlobalRef<jobject>& jthumbnail_tab_helper,
+    content::WebContents* web_contents,
+    scoped_refptr<ThumbnailingContext> context,
+    scoped_refptr<ThumbnailingAlgorithm> algorithm,
+    const gfx::Size& thumbnail_size) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::RenderWidgetHost* render_widget_host =
+      web_contents->GetRenderViewHost();
+  content::RenderWidgetHostView* view = render_widget_host->GetView();
+  if (!view)
+    return;
+  if (!view->IsSurfaceAvailableForCopy())
+    return;
+
+  gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
+  // Clip the pixels that will commonly hold a scrollbar, which looks bad in
+  // thumbnails.
+  copy_rect.Inset(0, 0, kScrollbarWidthDp, 0);
+  if (copy_rect.IsEmpty())
+    return;
+
+  ui::ScaleFactor scale_factor =
+      ui::GetSupportedScaleFactor(
+          ui::GetScaleFactorForNativeView(view->GetNativeView()));
+  context->clip_result = algorithm->GetCanvasCopyInfo(
+      copy_rect.size(),
+      scale_factor,
+      &copy_rect,
+      &context->requested_copy_size);
+
+  // Workaround for a bug where CopyFromBackingStore() accepts different input
+  // units on Android (DIP) vs on other platforms (pixels).
+  // TODO(newt): remove this line once https://crbug.com/540497 is fixed.
+  context->requested_copy_size = thumbnail_size;
+
+  render_widget_host->CopyFromBackingStore(
+      copy_rect, context->requested_copy_size,
+      base::Bind(&ProcessCapturedBitmap, jthumbnail_tab_helper, context,
+                 algorithm),
+      kN32_SkColorType);
+}
+
+}  // namespace
+
 // static
 bool RegisterThumbnailTabHelperAndroid(JNIEnv* env) {
   return RegisterNativesImpl(env);
@@ -44,57 +129,33 @@
     thumbnail_tab_helper->set_enabled(false);
 }
 
-static jboolean ShouldUpdateThumbnail(JNIEnv* env,
-                                      const JavaParamRef<jclass>& clazz,
-                                      const JavaParamRef<jobject>& jprofile,
-                                      const JavaParamRef<jstring>& jurl) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
-
-  GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
-  scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
-      ThumbnailServiceFactory::GetForProfile(profile);
-  return (thumbnail_service.get() != NULL &&
-          thumbnail_service->ShouldAcquirePageThumbnail(url));
-}
-
-static void UpdateThumbnail(JNIEnv* env,
-                            const JavaParamRef<jclass>& clazz,
-                            const JavaParamRef<jobject>& jweb_contents,
-                            const JavaParamRef<jobject>& bitmap,
-                            jboolean jat_top) {
+static void CaptureThumbnail(JNIEnv* env,
+                             const JavaParamRef<jclass>& clazz,
+                             const JavaParamRef<jobject>& jthumbnail_tab_helper,
+                             const JavaParamRef<jobject>& jweb_contents,
+                             jint thumbnail_width_dp,
+                             jint thumbnail_height_dp) {
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
   DCHECK(web_contents);
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-  Profile* profile = Profile::FromBrowserContext(
-      web_contents->GetBrowserContext());
-
-  gfx::JavaBitmap bitmap_lock(bitmap);
-  SkBitmap sk_bitmap;
-  gfx::Size size = bitmap_lock.size();
-  SkColorType color_type = kN32_SkColorType;
-  sk_bitmap.setInfo(
-      SkImageInfo::Make(size.width(), size.height(),
-                        color_type, kPremul_SkAlphaType), 0);
-  sk_bitmap.setPixels(bitmap_lock.pixels());
-
-  // TODO(nileshagrawal): Refactor this.
-  // We were using some non-public methods from ThumbnailTabHelper. We need to
-  // either add android specific logic to ThumbnailTabHelper or create our own
-  // helper which is driven by the java app (will need to pull out some logic
-  // from ThumbnailTabHelper to a common class).
-  scoped_refptr<history::TopSites> ts = TopSitesFactory::GetForProfile(profile);
-  if (!ts)
+  scoped_refptr<ThumbnailService> thumbnail_service =
+      ThumbnailServiceFactory::GetForProfile(profile);
+  if (thumbnail_service.get() == nullptr ||
+      !thumbnail_service->ShouldAcquirePageThumbnail(
+          web_contents->GetLastCommittedURL())) {
     return;
+  }
 
-  // Compute the thumbnail score.
-  ThumbnailScore score;
-  score.at_top = jat_top;
-  score.boring_score = color_utils::CalculateBoringScore(sk_bitmap);
-  score.good_clipping = true;
-  score.load_completed = !web_contents->IsLoading();
+  const gfx::Size thumbnail_size(thumbnail_width_dp, thumbnail_height_dp);
+  scoped_refptr<ThumbnailingAlgorithm> algorithm(
+      new thumbnails::SimpleThumbnailCrop(thumbnail_size));
 
-  gfx::Image image = gfx::Image::CreateFrom1xBitmap(sk_bitmap);
-  const GURL& url = web_contents->GetURL();
-  ts->SetPageThumbnail(url, image, score);
+  scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
+      web_contents, thumbnail_service.get(), false /*load_interrupted*/));
+  CaptureThumbnailInternal(
+      base::android::ScopedJavaGlobalRef<jobject>(env, jthumbnail_tab_helper),
+      web_contents, context, algorithm, thumbnail_size);
 }
diff --git a/chrome/browser/autofill/autofill_server_browsertest.cc b/chrome/browser/autofill/autofill_server_browsertest.cc
index 163dd2d3..eb4299b 100644
--- a/chrome/browser/autofill/autofill_server_browsertest.cc
+++ b/chrome/browser/autofill/autofill_server_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -12,7 +13,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/autofill/core/browser/autofill_profile.h"
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index 825fa8e4..1bca5ec 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -36,7 +37,6 @@
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 3632038..a43d66b1 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/at_exit.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc
index c8dcda4d..4fd6d9d 100644
--- a/chrome/browser/chrome_browser_main_android.cc
+++ b/chrome/browser/chrome_browser_main_android.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chrome_browser_main_android.h"
 
 #include "base/android/build_info.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -14,7 +15,6 @@
 #include "chrome/browser/android/seccomp_support_detector.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/content/browser/crash_dump_manager_android.h"
 #include "components/enhanced_bookmarks/persistent_image_store.h"
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 5a7e6fc..5c90b54 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -9,6 +9,7 @@
 
 #include <algorithm>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/files/file_enumerator.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 71adb835..c607c2c9 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -99,6 +100,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chromeos/chromeos_constants.h"
+#include "components/autofill/core/common/autofill_switches.h"
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index a24c6f4..1b6481f 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "ash/ash_switches.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
@@ -74,6 +75,7 @@
     ::switches::kDisableAcceleratedVideoDecode,
     ::switches::kDisableBlinkFeatures,
     ::switches::kDisableCastStreamingHWEncoding,
+    ::switches::kDisableCompositorAnimationTimelines,
     ::switches::kDisableDistanceFieldText,
     ::switches::kDisableGpu,
     ::switches::kDisableGpuMemoryBufferVideoFrames,
@@ -95,7 +97,6 @@
     ::switches::kDisableTouchDragDrop,
     ::switches::kDisableZeroCopy,
     ::switches::kEnableBlinkFeatures,
-    ::switches::kEnableCompositorAnimationTimelines,
     ::switches::kDisableDisplayList2dCanvas,
     ::switches::kEnableDisplayList2dCanvas,
     ::switches::kForceDisplayList2dCanvas,
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc
index cd9f794..57e44b2 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc
@@ -59,11 +59,9 @@
 const base::FilePath::CharType kSwReporterExeName[] =
     FILE_PATH_LITERAL("software_reporter_tool.exe");
 
-// Where to fetch the reporter's list of found uws in the registry.
+// SRT registry keys and value names.
 const wchar_t kCleanerSuffixRegistryKey[] = L"Cleaner";
-const wchar_t kEndTimeValueName[] = L"EndTime";
 const wchar_t kExitCodeValueName[] = L"ExitCode";
-const wchar_t kStartTimeValueName[] = L"StartTime";
 const wchar_t kUploadResultsValueName[] = L"UploadResults";
 const wchar_t kVersionValueName[] = L"Version";
 
@@ -201,7 +199,7 @@
       HKEY_CURRENT_USER, cleaner_key_name.c_str(), KEY_ALL_ACCESS);
   // Cleaner is assumed to have run if we have a start time.
   if (cleaner_key.Valid()) {
-    if (cleaner_key.HasValue(kStartTimeValueName)) {
+    if (cleaner_key.HasValue(safe_browsing::kStartTimeValueName)) {
       // Get version number.
       if (cleaner_key.HasValue(kVersionValueName)) {
         DWORD version;
@@ -213,14 +211,16 @@
       // Get start & end time. If we don't have an end time, we can assume the
       // cleaner has not completed.
       int64 start_time_value;
-      cleaner_key.ReadInt64(kStartTimeValueName, &start_time_value);
+      cleaner_key.ReadInt64(safe_browsing::kStartTimeValueName,
+                            &start_time_value);
 
-      bool completed = cleaner_key.HasValue(kEndTimeValueName);
+      bool completed = cleaner_key.HasValue(safe_browsing::kEndTimeValueName);
       SRTHasCompleted(completed ? SRT_COMPLETED_YES : SRT_COMPLETED_NOT_YET);
       if (completed) {
         int64 end_time_value;
-        cleaner_key.ReadInt64(kEndTimeValueName, &end_time_value);
-        cleaner_key.DeleteValue(kEndTimeValueName);
+        cleaner_key.ReadInt64(safe_browsing::kEndTimeValueName,
+                              &end_time_value);
+        cleaner_key.DeleteValue(safe_browsing::kEndTimeValueName);
         base::TimeDelta run_time(
             base::Time::FromInternalValue(end_time_value) -
             base::Time::FromInternalValue(start_time_value));
@@ -235,7 +235,7 @@
                                     exit_code);
         cleaner_key.DeleteValue(kExitCodeValueName);
       }
-      cleaner_key.DeleteValue(kStartTimeValueName);
+      cleaner_key.DeleteValue(safe_browsing::kStartTimeValueName);
 
       if (exit_code == safe_browsing::kSwReporterPostRebootCleanupNeeded ||
           exit_code ==
@@ -256,9 +256,9 @@
         ReportUploadsWithUma(upload_results);
       }
     } else {
-      if (cleaner_key.HasValue(kEndTimeValueName)) {
+      if (cleaner_key.HasValue(safe_browsing::kEndTimeValueName)) {
         SRTHasCompleted(SRT_COMPLETED_LATER);
-        cleaner_key.DeleteValue(kEndTimeValueName);
+        cleaner_key.DeleteValue(safe_browsing::kEndTimeValueName);
       }
     }
   }
diff --git a/chrome/browser/crash_recovery_browsertest.cc b/chrome/browser/crash_recovery_browsertest.cc
index 82ee5478..36ce51f 100644
--- a/chrome/browser/crash_recovery_browsertest.cc
+++ b/chrome/browser/crash_recovery_browsertest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/engagement/site_engagement_helper.cc b/chrome/browser/engagement/site_engagement_helper.cc
index 7f1bfe5..96c6db05 100644
--- a/chrome/browser/engagement/site_engagement_helper.cc
+++ b/chrome/browser/engagement/site_engagement_helper.cc
@@ -15,7 +15,9 @@
 
 namespace {
 
-double g_seconds_between_user_input_check = 10;
+int g_seconds_between_user_input_check = 10;
+int g_seconds_tracking_delay_after_navigation = 10;
+int g_seconds_tracking_delay_after_show = 5;
 
 }  // anonymous namespace
 
@@ -24,7 +26,9 @@
 SiteEngagementHelper::InputTracker::InputTracker(SiteEngagementHelper* helper)
     : helper_(helper),
       pause_timer_(new base::Timer(true, false)),
-      callbacks_added_(false) {
+      host_(nullptr),
+      is_active_(false),
+      is_tracking_(false) {
   key_press_event_callback_ =
       base::Bind(&SiteEngagementHelper::InputTracker::HandleKeyPressEvent,
                  base::Unretained(this));
@@ -33,7 +37,7 @@
                  base::Unretained(this));
 }
 
-SiteEngagementHelper::InputTracker::~InputTracker() { }
+SiteEngagementHelper::InputTracker::~InputTracker() {}
 
 // Record that there was some user input, and defer handling of the input event.
 // web_contents() will return nullptr if the observed contents have been
@@ -45,7 +49,7 @@
   // Only respond to raw key down to avoid multiple triggering on a single input
   // (e.g. keypress is a key down then key up).
   if (event.type == blink::WebInputEvent::RawKeyDown) {
-    PauseTracking(helper_->web_contents()->GetRenderViewHost());
+    Pause();
     helper_->RecordUserInput(SiteEngagementMetrics::ENGAGEMENT_KEYPRESS);
   }
   return false;
@@ -59,62 +63,91 @@
   if ((event.button != blink::WebMouseEvent::ButtonNone &&
        event.type == blink::WebInputEvent::MouseDown) ||
       event.type == blink::WebInputEvent::MouseWheel) {
-    PauseTracking(helper_->web_contents()->GetRenderViewHost());
+    Pause();
     helper_->RecordUserInput(SiteEngagementMetrics::ENGAGEMENT_MOUSE);
   }
   return false;
 }
 
-void SiteEngagementHelper::InputTracker::StartTracking(
-    content::RenderViewHost* host) {
-  if (!callbacks_added_) {
-    host->AddKeyPressEventCallback(key_press_event_callback_);
-    host->AddMouseEventCallback(mouse_event_callback_);
-    callbacks_added_ = true;
-  }
+void SiteEngagementHelper::InputTracker::Start(content::RenderViewHost* host,
+                                               base::TimeDelta initial_delay) {
+  DCHECK(!is_active_);
+  DCHECK(host);
+  host_ = host;
+  StartTimer(initial_delay);
+  is_active_ = true;
 }
 
-void SiteEngagementHelper::InputTracker::PauseTracking(
-    content::RenderViewHost* host) {
-  StopTracking(host);
+void SiteEngagementHelper::InputTracker::Pause() {
+  RemoveCallbacks();
+  StartTimer(base::TimeDelta::FromSeconds(g_seconds_between_user_input_check));
+}
+
+void SiteEngagementHelper::InputTracker::SwitchRenderViewHost(
+    content::RenderViewHost* old_host,
+    content::RenderViewHost* new_host) {
+  DCHECK(is_tracking_);
+  DCHECK(new_host);
+
+  bool was_tracking = is_tracking_;
+  if (old_host) {
+    DCHECK_EQ(host_, old_host);
+    RemoveCallbacks();
+  }
+
+  host_ = new_host;
+
+  if (was_tracking)
+    AddCallbacks();
+}
+
+void SiteEngagementHelper::InputTracker::Stop() {
+  pause_timer_->Stop();
+  RemoveCallbacks();
+  host_ = nullptr;
+  is_active_ = false;
+}
+
+void SiteEngagementHelper::InputTracker::SetPauseTimerForTesting(
+    scoped_ptr<base::Timer> timer) {
+  pause_timer_ = timer.Pass();
+}
+
+void SiteEngagementHelper::InputTracker::StartTimer(base::TimeDelta delay) {
   pause_timer_->Start(
-      FROM_HERE,
-      base::TimeDelta::FromSeconds(g_seconds_between_user_input_check),
-      base::Bind(&SiteEngagementHelper::InputTracker::ResumeTracking,
+      FROM_HERE, delay,
+      base::Bind(&SiteEngagementHelper::InputTracker::AddCallbacks,
                  base::Unretained(this)));
 }
 
-void SiteEngagementHelper::InputTracker::ResumeTracking() {
+void SiteEngagementHelper::InputTracker::AddCallbacks() {
   content::WebContents* contents = helper_->web_contents();
-  if (contents)
-    StartTracking(contents->GetRenderViewHost());
+  if (!contents)
+    return;
+
+  host_->AddKeyPressEventCallback(key_press_event_callback_);
+  host_->AddMouseEventCallback(mouse_event_callback_);
+  is_tracking_ = true;
 }
 
-void SiteEngagementHelper::InputTracker::StopTracking(
-    content::RenderViewHost* host) {
-  pause_timer_->Stop();
-  if (callbacks_added_) {
-    host->RemoveKeyPressEventCallback(key_press_event_callback_);
-    host->RemoveMouseEventCallback(mouse_event_callback_);
-    callbacks_added_ = false;
+void SiteEngagementHelper::InputTracker::RemoveCallbacks() {
+  if (is_tracking_) {
+    host_->RemoveKeyPressEventCallback(key_press_event_callback_);
+    host_->RemoveMouseEventCallback(mouse_event_callback_);
+    is_tracking_ = false;
   }
 }
 
-void SiteEngagementHelper::InputTracker::SetTimerForTesting(
-    scoped_ptr<base::Timer> timer) {
-  pause_timer_ = timer.Pass();
-}
-
 SiteEngagementHelper::~SiteEngagementHelper() {
   content::WebContents* contents = web_contents();
   if (contents)
-    input_tracker_.StopTracking(contents->GetRenderViewHost());
+    input_tracker_.Stop();
 }
 
 SiteEngagementHelper::SiteEngagementHelper(content::WebContents* contents)
     : content::WebContentsObserver(contents),
       input_tracker_(this),
-      record_engagement_(false) { }
+      record_engagement_(false) {}
 
 void SiteEngagementHelper::RecordUserInput(
     SiteEngagementMetrics::EngagementType type) {
@@ -132,31 +165,28 @@
   }
 }
 
-bool SiteEngagementHelper::ShouldRecordEngagement() {
-  return record_engagement_;
-}
-
 void SiteEngagementHelper::DidNavigateMainFrame(
     const content::LoadCommittedDetails& details,
     const content::FrameNavigateParams& params) {
-  content::WebContents* contents = web_contents();
-  input_tracker_.StopTracking(contents->GetRenderViewHost());
+  input_tracker_.Stop();
 
-  // Ignore all schemes except HTTP and HTTPS.
   record_engagement_ = params.url.SchemeIsHTTPOrHTTPS();
 
-  if (!ShouldRecordEngagement())
+  // Ignore all schemes except HTTP and HTTPS.
+  if (!record_engagement_)
     return;
 
   Profile* profile =
-      Profile::FromBrowserContext(contents->GetBrowserContext());
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   SiteEngagementService* service =
       SiteEngagementServiceFactory::GetForProfile(profile);
 
   if (service)
     service->HandleNavigation(params.url, params.transition);
 
-  input_tracker_.StartTracking(contents->GetRenderViewHost());
+  input_tracker_.Start(
+      web_contents()->GetRenderViewHost(),
+      base::TimeDelta::FromSeconds(g_seconds_tracking_delay_after_navigation));
 }
 
 void SiteEngagementHelper::RenderViewHostChanged(
@@ -164,29 +194,36 @@
     content::RenderViewHost* new_host) {
   // On changing the render view host, we need to re-register the callbacks
   // listening for user input.
-  if (ShouldRecordEngagement()) {
-    if (old_host)
-      input_tracker_.StopTracking(old_host);
-    input_tracker_.StartTracking(new_host);
+  if (input_tracker_.is_tracking()) {
+    input_tracker_.SwitchRenderViewHost(old_host, new_host);
   }
 }
 
 void SiteEngagementHelper::WasShown() {
   // Ensure that the input callbacks are registered when we come into view.
-  if (ShouldRecordEngagement())
-    input_tracker_.StartTracking(web_contents()->GetRenderViewHost());
+  if (record_engagement_) {
+    input_tracker_.Start(
+        web_contents()->GetRenderViewHost(),
+        base::TimeDelta::FromSeconds(g_seconds_tracking_delay_after_show));
+  }
 }
 
 void SiteEngagementHelper::WasHidden() {
   // Ensure that the input callbacks are not registered when hidden.
-  if (ShouldRecordEngagement()) {
-    content::WebContents* contents = web_contents();
-    if (contents)
-      input_tracker_.StopTracking(contents->GetRenderViewHost());
-  }
+  input_tracker_.Stop();
 }
 
 // static
-void SiteEngagementHelper::SetSecondsBetweenUserInputCheck(double seconds) {
+void SiteEngagementHelper::SetSecondsBetweenUserInputCheck(int seconds) {
   g_seconds_between_user_input_check = seconds;
 }
+
+// static
+void SiteEngagementHelper::SetSecondsTrackingDelayAfterNavigation(int seconds) {
+  g_seconds_tracking_delay_after_navigation = seconds;
+}
+
+// static
+void SiteEngagementHelper::SetSecondsTrackingDelayAfterShow(int seconds) {
+  g_seconds_tracking_delay_after_show = seconds;
+}
diff --git a/chrome/browser/engagement/site_engagement_helper.h b/chrome/browser/engagement/site_engagement_helper.h
index bd45650..3077afb5 100644
--- a/chrome/browser/engagement/site_engagement_helper.h
+++ b/chrome/browser/engagement/site_engagement_helper.h
@@ -26,7 +26,18 @@
  public:
   ~SiteEngagementHelper() override;
 
-  static void SetSecondsBetweenUserInputCheck(double seconds);
+  static void SetSecondsBetweenUserInputCheck(int seconds);
+  static void SetSecondsTrackingDelayAfterNavigation(int seconds);
+  static void SetSecondsTrackingDelayAfterShow(int seconds);
+
+  // content::WebContentsObserver overrides.
+  void DidNavigateMainFrame(
+      const content::LoadCommittedDetails& details,
+      const content::FrameNavigateParams& params) override;
+  void RenderViewHostChanged(content::RenderViewHost* old_host,
+                             content::RenderViewHost* new_host) override;
+  void WasShown() override;
+  void WasHidden() override;
 
  private:
   // Class to encapsulate the user input listening.
@@ -51,30 +62,45 @@
     // Callback to handle mouse events from the RenderViewHost.
     bool HandleMouseEvent(const blink::WebMouseEvent& event);
 
-    // Register callbacks to listen for user input.
-    void StartTracking(content::RenderViewHost* host);
+    // Begin tracking input after |initial_delay|.
+    void Start(content::RenderViewHost* host, base::TimeDelta initial_delay);
 
-    // Pause listening for user input, restarting listening after
-    // g_seconds_between_user_input_check seconds.
-    void PauseTracking(content::RenderViewHost* host);
+    // Pause listening for user input, restarting listening after a delay.
+    void Pause();
 
-    // Restart listening for user input.
-    void ResumeTracking();
+    // Switches the InputTracker to another RenderViewHost, respecting the pause
+    // timer state.
+    void SwitchRenderViewHost(content::RenderViewHost* old_host,
+                              content::RenderViewHost* new_host);
 
     // Stop listening for user input.
-    void StopTracking(content::RenderViewHost* host);
+    void Stop();
+
+    // Returns whether the InputTracker has been started for a RenderViewHost.
+    bool is_active() const { return is_active_; }
+
+    // Returns whether input tracking callbacks have been added to
+    // RenderViewHost.
+    bool is_tracking() const { return is_tracking_; }
 
     // Set the timer object for testing purposes.
-    void SetTimerForTesting(scoped_ptr<base::Timer> timer);
-
-    bool callbacks_added() { return callbacks_added_; }
+    void SetPauseTimerForTesting(scoped_ptr<base::Timer> timer);
 
    private:
+    // Starts the timer for adding callbacks to the RenderViewHost.
+    void StartTimer(base::TimeDelta delay);
+
+    // Adds/removes tracking callbacks to the RenderViewHost.
+    void AddCallbacks();
+    void RemoveCallbacks();
+
     SiteEngagementHelper* helper_;
     scoped_ptr<base::Timer> pause_timer_;
+    content::RenderViewHost* host_;
     content::RenderWidgetHost::KeyPressEventCallback key_press_event_callback_;
     content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
-    bool callbacks_added_;
+    bool is_active_;
+    bool is_tracking_;
   };
 
   explicit SiteEngagementHelper(content::WebContents* web_contents);
@@ -85,19 +111,6 @@
   // current contents location.
   void RecordUserInput(SiteEngagementMetrics::EngagementType type);
 
-  bool ShouldRecordEngagement();
-
-  // content::WebContentsObserver overrides.
-  void DidNavigateMainFrame(
-      const content::LoadCommittedDetails& details,
-      const content::FrameNavigateParams& params) override;
-
-  void RenderViewHostChanged(content::RenderViewHost* old_host,
-                             content::RenderViewHost* new_host) override;
-
-  void WasShown() override;
-  void WasHidden() override;
-
   InputTracker input_tracker_;
   bool record_engagement_;
 
diff --git a/chrome/browser/engagement/site_engagement_service_browsertest.cc b/chrome/browser/engagement/site_engagement_service_browsertest.cc
index bec367a..2da4efa2 100644
--- a/chrome/browser/engagement/site_engagement_service_browsertest.cc
+++ b/chrome/browser/engagement/site_engagement_service_browsertest.cc
@@ -49,14 +49,14 @@
     helper->input_tracker_.HandleMouseEvent(event);
   }
 
-  // Set a timer object for test purposes.
-  void SetHelperTimer(SiteEngagementHelper* helper,
-                      scoped_ptr<base::Timer> timer) {
-    helper->input_tracker_.SetTimerForTesting(timer.Pass());
+  // Set a pause timer on the input tracker for test purposes.
+  void SetInputTrackerPauseTimer(SiteEngagementHelper* helper,
+                                 scoped_ptr<base::Timer> timer) {
+    helper->input_tracker_.SetPauseTimerForTesting(timer.Pass());
   }
 
-  bool CallbacksAdded(SiteEngagementHelper* helper) {
-    return helper->input_tracker_.callbacks_added();
+  bool IsTracking(SiteEngagementHelper* helper) {
+    return helper->input_tracker_.is_tracking();
   }
 };
 
@@ -237,10 +237,9 @@
   content::WebContents* web_contents =
     browser()->tab_strip_model()->GetActiveWebContents();
 
-  scoped_ptr<base::MockTimer> mock_timer(new base::MockTimer(true, false));
-  base::MockTimer* timer = mock_timer.get();
+  base::MockTimer* input_tracker_timer = new base::MockTimer(true, false);
   scoped_ptr<SiteEngagementHelper> helper(CreateHelper(web_contents));
-  SetHelperTimer(helper.get(), mock_timer.Pass());
+  SetInputTrackerPauseTimer(helper.get(), make_scoped_ptr(input_tracker_timer));
 
   SiteEngagementService* service =
     SiteEngagementServiceFactory::GetForProfile(browser()->profile());
@@ -250,43 +249,198 @@
   EXPECT_DOUBLE_EQ(0.5, service->GetScore(url1));
   EXPECT_EQ(0, service->GetScore(url2));
 
-  // Timer should not be running after navigation. It should start after input.
-  EXPECT_FALSE(timer->IsRunning());
-  EXPECT_TRUE(CallbacksAdded(helper.get()));
+  // Timer should be running for navigation delay.
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+  input_tracker_timer->Fire();
+
+  // Timer should start running again after input.
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
   HandleKeyPress(helper.get(), ui::VKEY_RETURN);
-  EXPECT_TRUE(timer->IsRunning());
-  EXPECT_FALSE(CallbacksAdded(helper.get()));
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
 
   EXPECT_DOUBLE_EQ(0.55, service->GetScore(url1));
   EXPECT_EQ(0, service->GetScore(url2));
-  timer->Fire();
 
-  EXPECT_FALSE(timer->IsRunning());
-  EXPECT_TRUE(CallbacksAdded(helper.get()));
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Timer should start running again after input.
   HandleMouseEvent(helper.get(), blink::WebMouseEvent::ButtonNone,
                    blink::WebInputEvent::MouseWheel);
-  EXPECT_TRUE(timer->IsRunning());
-  EXPECT_FALSE(CallbacksAdded(helper.get()));
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
 
   EXPECT_DOUBLE_EQ(0.6, service->GetScore(url1));
   EXPECT_EQ(0, service->GetScore(url2));
-  timer->Fire();
 
-  EXPECT_FALSE(timer->IsRunning());
-  EXPECT_TRUE(CallbacksAdded(helper.get()));
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Timer should be running for navigation delay.
   ui_test_utils::NavigateToURL(browser(), url2);
-  EXPECT_FALSE(timer->IsRunning());
-  EXPECT_TRUE(CallbacksAdded(helper.get()));
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
 
   EXPECT_DOUBLE_EQ(0.6, service->GetScore(url1));
   EXPECT_DOUBLE_EQ(0.5, service->GetScore(url2));
 
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
   HandleMouseEvent(helper.get(), blink::WebMouseEvent::ButtonRight,
                    blink::WebInputEvent::MouseDown);
-  EXPECT_TRUE(timer->IsRunning());
-  EXPECT_FALSE(CallbacksAdded(helper.get()));
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
 
   EXPECT_DOUBLE_EQ(0.6, service->GetScore(url1));
   EXPECT_DOUBLE_EQ(0.55, service->GetScore(url2));
   EXPECT_DOUBLE_EQ(1.15, service->GetTotalEngagementPoints());
 }
+
+// Ensure that navigation does not trigger input tracking until after a delay.
+IN_PROC_BROWSER_TEST_F(SiteEngagementServiceBrowserTest, ShowAndHide) {
+  GURL url1("https://www.google.com/");
+  GURL url2("http://www.google.com/");
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  base::MockTimer* input_tracker_timer = new base::MockTimer(true, false);
+  scoped_ptr<SiteEngagementHelper> helper(CreateHelper(web_contents));
+  SetInputTrackerPauseTimer(helper.get(), make_scoped_ptr(input_tracker_timer));
+
+  ui_test_utils::NavigateToURL(browser(), url1);
+  input_tracker_timer->Fire();
+
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Hiding the tab should stop input tracking.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2, NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  // Showing the tab should start tracking again after another delay.
+  browser()->tab_strip_model()->ActivateTabAt(0, true);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // New background tabs should not affect the current tab's input tracking.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2, NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Ensure behavior holds when tab is hidden before the initial delay timer
+  // fires.
+  ui_test_utils::NavigateToURL(browser(), url2);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2, NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  // Showing the tab should start tracking again after another delay.
+  browser()->tab_strip_model()->ActivateTabAt(0, true);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  input_tracker_timer->Fire();
+  EXPECT_TRUE(IsTracking(helper.get()));
+}
+
+// Ensure tracking behavior is correct for multiple navigations in a single tab.
+IN_PROC_BROWSER_TEST_F(SiteEngagementServiceBrowserTest, SingleTabNavigation) {
+  GURL url1("https://www.google.com/");
+  GURL url2("https://www.example.com/");
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  base::MockTimer* input_tracker_timer = new base::MockTimer(true, false);
+  scoped_ptr<SiteEngagementHelper> helper(CreateHelper(web_contents));
+  SetInputTrackerPauseTimer(helper.get(), make_scoped_ptr(input_tracker_timer));
+
+  // Navigation should start the initial delay timer.
+  ui_test_utils::NavigateToURL(browser(), url1);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  // Navigating before the timer fires should simply reset the timer.
+  ui_test_utils::NavigateToURL(browser(), url2);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  // When the timer fires, callbacks are added.
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Navigation should start the initial delay timer again.
+  ui_test_utils::NavigateToURL(browser(), url1);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+}
+
+// Ensure tracking behavior is correct for multiple navigations in a single tab.
+IN_PROC_BROWSER_TEST_F(SiteEngagementServiceBrowserTest, SwitchRenderViewHost) {
+  GURL url1("https://www.google.com/");
+  GURL url2("https://www.example.com/");
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  base::MockTimer* input_tracker_timer = new base::MockTimer(true, false);
+  scoped_ptr<SiteEngagementHelper> helper(CreateHelper(web_contents));
+  SetInputTrackerPauseTimer(helper.get(), make_scoped_ptr(input_tracker_timer));
+
+  // Navigation starts the initial delay timer.
+  ui_test_utils::NavigateToURL(browser(), url1);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
+
+  // The timer should still be running after the RenderViewHost is changed.
+  helper->RenderViewHostChanged(rvh, rvh);
+  EXPECT_TRUE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  // Firing the timer should add the callbacks.
+  input_tracker_timer->Fire();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // The callbacks should be on readded another RVH change since the timer has
+  // already fired.
+  helper->RenderViewHostChanged(rvh, rvh);
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Ensure nothing bad happens with a destroyed RVH.
+  helper->RenderViewHostChanged(nullptr, rvh);
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_TRUE(IsTracking(helper.get()));
+
+  // Ensure nothing happens when RVH change happens for a hidden tab.
+  helper->WasHidden();
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+
+  helper->RenderViewHostChanged(nullptr, rvh);
+  EXPECT_FALSE(input_tracker_timer->IsRunning());
+  EXPECT_FALSE(IsTracking(helper.get()));
+}
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 8d84b3d..a626f928 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -46,6 +46,7 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/signin/core/common/signin_pref_names.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
index 367d7a0..1929671 100644
--- a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
+++ b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/api/instance_id/instance_id_api.h"
@@ -11,7 +12,6 @@
 #include "chrome/browser/services/gcm/fake_gcm_profile_service.h"
 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
 #include "components/version_info/version_info.h"
diff --git a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
index 52e2126..a178f0d3 100644
--- a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
+++ b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
@@ -45,11 +45,13 @@
   auto policy = content::ChildProcessSecurityPolicy::GetInstance();
 
   // Components of chrome that are implemented as extensions or platform apps
-  // are allowed to use chrome://resources/ URLs.
+  // are allowed to use chrome://resources/ and chrome://theme/ URLs.
   if ((extension->is_extension() || extension->is_platform_app()) &&
       Manifest::IsComponentLocation(extension->location())) {
     policy->GrantOrigin(process_id,
                         url::Origin(GURL(content::kChromeUIResourcesURL)));
+    policy->GrantOrigin(process_id,
+                        url::Origin(GURL(chrome::kChromeUIThemeURL)));
   }
 
   // Extensions, legacy packaged apps, and component platform apps are allowed
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index ad5f1ab..7918358 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/chrome/browser/extensions/install_verifier.cc b/chrome/browser/extensions/install_verifier.cc
index 0739647..b5bdbd7 100644
--- a/chrome/browser/extensions/install_verifier.cc
+++ b/chrome/browser/extensions/install_verifier.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <string>
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
diff --git a/chrome/browser/first_run/first_run_browsertest.cc b/chrome/browser/first_run/first_run_browsertest.cc
index 25897c1..716a1a9a 100644
--- a/chrome/browser/first_run/first_run_browsertest.cc
+++ b/chrome/browser/first_run/first_run_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <string>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
diff --git a/chrome/browser/geolocation/geolocation_infobar_delegate.cc b/chrome/browser/geolocation/geolocation_infobar_delegate.cc
index b6959ca5..b64ae70 100644
--- a/chrome/browser/geolocation/geolocation_infobar_delegate.cc
+++ b/chrome/browser/geolocation/geolocation_infobar_delegate.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/geolocation/geolocation_infobar_delegate.h"
 
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/infobar.h"
 #include "components/url_formatter/elide_url.h"
 #include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 // static
@@ -38,7 +38,7 @@
 }
 
 int GeolocationInfoBarDelegate::GetIconId() const {
-  return IDR_INFOBAR_GEOLOCATION;
+  return IDR_ANDROID_INFOBAR_GEOLOCATION;
 }
 
 base::string16 GeolocationInfoBarDelegate::GetMessageText() const {
diff --git a/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
similarity index 90%
rename from chrome/browser/media/encrypted_media_istypesupported_browsertest.cc
rename to chrome/browser/media/encrypted_media_supported_types_browsertest.cc
index f8a7461..78658bd3 100644
--- a/chrome/browser/media/encrypted_media_istypesupported_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
@@ -93,10 +93,9 @@
 
 };  // namespace
 
-// TODO(jrummell): Rename these tests and this file. http://crbug.com/367158.
-class EncryptedMediaIsTypeSupportedTest : public InProcessBrowserTest {
+class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
  protected:
-  EncryptedMediaIsTypeSupportedTest() : is_pepper_cdm_registered_(false) {
+  EncryptedMediaSupportedTypesTest() : is_pepper_cdm_registered_(false) {
     audio_webm_codecs_.push_back("opus");
     audio_webm_codecs_.push_back("vorbis");
 
@@ -257,17 +256,17 @@
 };
 
 // For ClearKey, nothing additional is required.
-class EncryptedMediaIsTypeSupportedClearKeyTest
-    : public EncryptedMediaIsTypeSupportedTest {
+class EncryptedMediaSupportedTypesClearKeyTest
+    : public EncryptedMediaSupportedTypesTest {
 };
 
 // For ExternalClearKey tests, ensure that the ClearKey adapter is loaded.
-class EncryptedMediaIsTypeSupportedExternalClearKeyTest
-    : public EncryptedMediaIsTypeSupportedTest {
+class EncryptedMediaSupportedTypesExternalClearKeyTest
+    : public EncryptedMediaSupportedTypesTest {
 #if defined(ENABLE_PEPPER_CDMS)
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    EncryptedMediaIsTypeSupportedTest::SetUpCommandLine(command_line);
+    EncryptedMediaSupportedTypesTest::SetUpCommandLine(command_line);
 
     // Platform-specific filename relative to the chrome executable.
     const char adapter_file_name[] =
@@ -288,17 +287,17 @@
 // TODO(sandersd): Register the Widevine CDM if it is a component. A component
 // CDM registered using RegisterPepperCdm() declares support for audio codecs,
 // but not the other codecs we expect. http://crbug.com/356833.
-class EncryptedMediaIsTypeSupportedWidevineTest
-    : public EncryptedMediaIsTypeSupportedTest {
+class EncryptedMediaSupportedTypesWidevineTest
+    : public EncryptedMediaSupportedTypesTest {
 };
 
 #if defined(ENABLE_PEPPER_CDMS)
 // Registers ClearKey CDM with the wrong path (filename).
-class EncryptedMediaIsTypeSupportedClearKeyCDMRegisteredWithWrongPathTest
-    : public EncryptedMediaIsTypeSupportedTest {
+class EncryptedMediaSupportedTypesClearKeyCDMRegisteredWithWrongPathTest
+    : public EncryptedMediaSupportedTypesTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    EncryptedMediaIsTypeSupportedTest::SetUpCommandLine(command_line);
+    EncryptedMediaSupportedTypesTest::SetUpCommandLine(command_line);
     RegisterPepperCdm(command_line,
                       "clearkeycdmadapterwrongname.dll",
                       "application/x-ppapi-clearkey-cdm",
@@ -307,11 +306,11 @@
 };
 
 // Registers Widevine CDM with the wrong path (filename).
-class EncryptedMediaIsTypeSupportedWidevineCDMRegisteredWithWrongPathTest
-    : public EncryptedMediaIsTypeSupportedTest {
+class EncryptedMediaSupportedTypesWidevineCDMRegisteredWithWrongPathTest
+    : public EncryptedMediaSupportedTypesTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    EncryptedMediaIsTypeSupportedTest::SetUpCommandLine(command_line);
+    EncryptedMediaSupportedTypesTest::SetUpCommandLine(command_line);
     RegisterPepperCdm(command_line,
                       "widevinecdmadapterwrongname.dll",
                       "application/x-ppapi-widevine-cdm",
@@ -320,7 +319,7 @@
 };
 #endif  // defined(ENABLE_PEPPER_CDMS)
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest, Basic) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Basic) {
   EXPECT_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kClearKey));
   EXPECT_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
@@ -331,7 +330,7 @@
       kAudioMP4MimeType, no_codecs(), kClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest,
                        InvalidKeySystems) {
   // Case sensitive.
   EXPECT_UNKNOWN_KEYSYSTEM(IsSupportedKeySystemWithMediaMimeType(
@@ -364,7 +363,7 @@
       kVideoWebMMimeType, no_codecs(), "org.w3.clearkey.foo"));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest, Video_WebM) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Video_WebM) {
   // Valid video types.
   EXPECT_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, video_webm_codecs(), kClearKey));
@@ -382,7 +381,7 @@
       kVideoWebMMimeType, video_mp4_codecs(), kClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest, Audio_WebM) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Audio_WebM) {
   // Valid audio types.
   EXPECT_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kAudioWebMMimeType, audio_webm_codecs(), kClearKey));
@@ -400,7 +399,7 @@
       kAudioWebMMimeType, video_mp4_codecs(), kClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest, Video_MP4) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Video_MP4) {
   // Valid video types.
   EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       kVideoMP4MimeType, video_mp4_codecs(), kClearKey));
@@ -418,7 +417,7 @@
       kVideoMP4MimeType, video_webm_codecs(), kClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedClearKeyTest, Audio_MP4) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Audio_MP4) {
   // Valid audio types.
   EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       kAudioMP4MimeType, audio_mp4_codecs(), kClearKey));
@@ -441,7 +440,7 @@
 //
 
 // When defined(ENABLE_PEPPER_CDMS), this also tests the Pepper CDM check.
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        Basic) {
   EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kExternalClearKey));
@@ -453,7 +452,7 @@
       kAudioMP4MimeType, no_codecs(), kExternalClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        InvalidKeySystems) {
   // Case sensitive.
   EXPECT_UNKNOWN_KEYSYSTEM(IsSupportedKeySystemWithMediaMimeType(
@@ -482,7 +481,7 @@
       kVideoWebMMimeType, no_codecs(), "org.chromium.externalclearkey.foo"));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        Video_WebM) {
   // Valid video types.
   EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
@@ -501,7 +500,7 @@
       kVideoWebMMimeType, video_mp4_codecs(), kExternalClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        Audio_WebM) {
   // Valid audio types.
   EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
@@ -520,7 +519,7 @@
       kAudioWebMMimeType, video_mp4_codecs(), kExternalClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        Video_MP4) {
   // Valid video types.
   EXPECT_ECK_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
@@ -539,7 +538,7 @@
       kVideoMP4MimeType, video_webm_codecs(), kExternalClearKey));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedExternalClearKeyTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
                        Audio_MP4) {
   // Valid audio types.
   EXPECT_ECK_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
@@ -562,8 +561,7 @@
 // Widevine
 //
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest,
-                       Basic) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesWidevineTest, Basic) {
   EXPECT_WV_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kWidevine));
   EXPECT_WV_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
@@ -574,7 +572,7 @@
       kAudioMP4MimeType, no_codecs(), kWidevine));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, Video_WebM) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesWidevineTest, Video_WebM) {
   // Valid video types.
   EXPECT_WV_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, video_webm_codecs(), kWidevine));
@@ -592,7 +590,7 @@
       kVideoWebMMimeType, video_mp4_codecs(), kWidevine));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, Audio_WebM) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesWidevineTest, Audio_WebM) {
   // Valid audio types.
   EXPECT_WV_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kAudioWebMMimeType, audio_webm_codecs(), kWidevine));
@@ -610,7 +608,7 @@
       kAudioWebMMimeType, video_mp4_codecs(), kWidevine));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, Video_MP4) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesWidevineTest, Video_MP4) {
   // Valid video types.
   EXPECT_WV_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       kVideoMP4MimeType, video_mp4_codecs(), kWidevine));
@@ -628,7 +626,7 @@
       kVideoMP4MimeType, video_webm_codecs(), kWidevine));
 }
 
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedWidevineTest, Audio_MP4) {
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesWidevineTest, Audio_MP4) {
   // Valid audio types.
   EXPECT_WV_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       kAudioMP4MimeType, audio_mp4_codecs(), kWidevine));
@@ -650,7 +648,7 @@
 // Since this test fixture does not register the CDMs on the command line, the
 // check for the CDMs in chrome_key_systems.cc should fail, and they should not
 // be registered with KeySystems.
-IN_PROC_BROWSER_TEST_F(EncryptedMediaIsTypeSupportedTest,
+IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesTest,
                        PepperCDMsNotRegistered) {
   EXPECT_UNKNOWN_KEYSYSTEM(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kExternalClearKey));
@@ -671,7 +669,7 @@
 // check for the CDMs in chrome_key_systems.cc should fail, and they should not
 // be registered with KeySystems.
 IN_PROC_BROWSER_TEST_F(
-    EncryptedMediaIsTypeSupportedClearKeyCDMRegisteredWithWrongPathTest,
+    EncryptedMediaSupportedTypesClearKeyCDMRegisteredWithWrongPathTest,
     PepperCDMsRegisteredButAdapterNotPresent) {
   EXPECT_UNKNOWN_KEYSYSTEM(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kExternalClearKey));
@@ -685,7 +683,7 @@
 // component, in which case it is registered internally.
 #if !defined(WIDEVINE_CDM_AVAILABLE) || defined(WIDEVINE_CDM_IS_COMPONENT)
 IN_PROC_BROWSER_TEST_F(
-    EncryptedMediaIsTypeSupportedWidevineCDMRegisteredWithWrongPathTest,
+    EncryptedMediaSupportedTypesWidevineCDMRegisteredWithWrongPathTest,
     PepperCDMsRegisteredButAdapterNotPresent) {
   EXPECT_UNKNOWN_KEYSYSTEM(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kWidevine));
@@ -694,7 +692,8 @@
   EXPECT_SUCCESS(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebMMimeType, no_codecs(), kClearKey));
 }
-#endif  // !defined(WIDEVINE_CDM_AVAILABLE) || defined(WIDEVINE_CDM_IS_COMPONENT)
+#endif  // !defined(WIDEVINE_CDM_AVAILABLE) ||
+        // defined(WIDEVINE_CDM_IS_COMPONENT)
 #endif  // defined(ENABLE_PEPPER_CDMS)
 
 }  // namespace chrome
diff --git a/chrome/browser/media/protected_media_identifier_infobar_delegate.cc b/chrome/browser/media/protected_media_identifier_infobar_delegate.cc
index 0932b99..a182244 100644
--- a/chrome/browser/media/protected_media_identifier_infobar_delegate.cc
+++ b/chrome/browser/media/protected_media_identifier_infobar_delegate.cc
@@ -4,13 +4,13 @@
 
 #include "chrome/browser/media/protected_media_identifier_infobar_delegate.h"
 
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/infobar.h"
 #include "components/url_formatter/elide_url.h"
 #include "grit/components_strings.h"
-#include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 // static
@@ -43,7 +43,7 @@
 }
 
 int ProtectedMediaIdentifierInfoBarDelegate::GetIconId() const {
-  return IDR_INFOBAR_PROTECTED_MEDIA_IDENTIFIER;
+  return IDR_ANDROID_INFOBAR_PROTECTED_MEDIA_IDENTIFIER;
 }
 
 base::string16 ProtectedMediaIdentifierInfoBarDelegate::GetMessageText() const {
diff --git a/chrome/browser/media/router/issues_observer.cc b/chrome/browser/media/router/issues_observer.cc
index 0d496d8..1590b0cd 100644
--- a/chrome/browser/media/router/issues_observer.cc
+++ b/chrome/browser/media/router/issues_observer.cc
@@ -11,10 +11,16 @@
 
 IssuesObserver::IssuesObserver(MediaRouter* router) : router_(router) {
   DCHECK(router_);
-  router_->RegisterIssuesObserver(this);
 }
 
 IssuesObserver::~IssuesObserver() {
+}
+
+void IssuesObserver::RegisterObserver() {
+  router_->RegisterIssuesObserver(this);
+}
+
+void IssuesObserver::UnregisterObserver() {
   router_->UnregisterIssuesObserver(this);
 }
 
diff --git a/chrome/browser/media/router/issues_observer.h b/chrome/browser/media/router/issues_observer.h
index d8b4258..5b51e63 100644
--- a/chrome/browser/media/router/issues_observer.h
+++ b/chrome/browser/media/router/issues_observer.h
@@ -18,6 +18,9 @@
   explicit IssuesObserver(MediaRouter* router);
   virtual ~IssuesObserver();
 
+  void RegisterObserver();
+  void UnregisterObserver();
+
   // Called when there is an updated Media Router Issue.
   // If |issue| is nullptr, then there is currently no issue.
   virtual void OnIssueUpdated(const Issue* issue) {}
diff --git a/chrome/browser/media/router/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
index 64be2663..32fd0d5 100644
--- a/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
@@ -283,6 +283,9 @@
 TEST_F(MediaRouterMojoImplTest, HandleIssue) {
   MockIssuesObserver issue_observer1(router());
   MockIssuesObserver issue_observer2(router());
+  issue_observer1.RegisterObserver();
+  issue_observer2.RegisterObserver();
+
   interfaces::IssuePtr mojo_issue1 = CreateMojoIssue("title 1");
   const Issue& expected_issue1 = mojo_issue1.To<Issue>();
 
@@ -322,6 +325,9 @@
               OnIssueUpdated(Pointee(EqualsIssue(expected_issue2))));
   media_router_proxy_->OnIssue(mojo_issue2.Pass());
   ProcessEventLoop();
+
+  issue_observer1.UnregisterObserver();
+  issue_observer2.UnregisterObserver();
 }
 
 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
diff --git a/chrome/browser/memory/tab_discard_state.cc b/chrome/browser/memory/tab_discard_state.cc
index 5cf2df3..449c545c 100644
--- a/chrome/browser/memory/tab_discard_state.cc
+++ b/chrome/browser/memory/tab_discard_state.cc
@@ -61,10 +61,16 @@
     static int reload_count = 0;
     UMA_HISTOGRAM_CUSTOM_COUNTS("TabManager.Discarding.ReloadCount",
                                 ++reload_count, 1, 1000, 50);
+    auto delta = base::TimeTicks::Now() - discard_state->last_discard_time_;
+    // Capped to one day for now, will adjust if necessary.
+    UMA_HISTOGRAM_CUSTOM_TIMES("TabManager.Discarding.DiscardToReloadTime",
+                               delta, base::TimeDelta::FromSeconds(1),
+                               base::TimeDelta::FromDays(1), 100);
   } else if (!discard_state->is_discarded_ && state) {
     static int discard_count = 0;
     UMA_HISTOGRAM_CUSTOM_COUNTS("TabManager.Discarding.DiscardCount",
                                 ++discard_count, 1, 1000, 50);
+    discard_state->last_discard_time_ = base::TimeTicks::Now();
   }
 
   discard_state->is_discarded_ = state;
diff --git a/chrome/browser/memory/tab_discard_state.h b/chrome/browser/memory/tab_discard_state.h
index 95770be..12dd9976 100644
--- a/chrome/browser/memory/tab_discard_state.h
+++ b/chrome/browser/memory/tab_discard_state.h
@@ -74,6 +74,9 @@
   // Last time the tab started or stopped playing audio (we record the
   // transition time).
   base::TimeTicks last_audio_change_time_;
+
+  // The last time the tab was discarded.
+  base::TimeTicks last_discard_time_;
 };
 
 }  // namespace memory
diff --git a/chrome/browser/notifications/notification_permission_infobar_delegate.cc b/chrome/browser/notifications/notification_permission_infobar_delegate.cc
index 532b3934..105e48d 100644
--- a/chrome/browser/notifications/notification_permission_infobar_delegate.cc
+++ b/chrome/browser/notifications/notification_permission_infobar_delegate.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/notifications/notification_permission_infobar_delegate.h"
 
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/infobar.h"
 #include "components/url_formatter/elide_url.h"
-#include "grit/theme_resources.h"
 #include "net/base/escape.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -39,7 +39,7 @@
     {}
 
 int NotificationPermissionInfobarDelegate::GetIconId() const {
-  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
+  return IDR_ANDROID_INFOBAR_NOTIFICATIONS;
 }
 
 base::string16 NotificationPermissionInfobarDelegate::GetMessageText() const {
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index cc5d2ae..f30b74b 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -387,6 +387,8 @@
 
   notification.set_buttons(buttons);
 
+  notification.set_is_web_notification(true);
+
   // On desktop, notifications with require_interaction==true stay on-screen
   // rather than minimizing to the notification center after a timeout.
   // On mobile, this is ignored (notifications are minimized at all times).
diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc
index e544ed6c..761b489 100644
--- a/chrome/browser/permissions/permission_context_base.cc
+++ b/chrome/browser/permissions/permission_context_base.cc
@@ -147,7 +147,10 @@
 #if !defined(OS_ANDROID)
   PermissionBubbleManager* bubble_manager =
       PermissionBubbleManager::FromWebContents(web_contents);
-  DCHECK(bubble_manager);
+  // TODO(felt): sometimes |bubble_manager| is null. This check is meant to
+  // prevent crashes. See crbug.com/457091.
+  if (!bubble_manager)
+    return;
   scoped_ptr<PermissionBubbleRequest> request_ptr(
       new PermissionBubbleRequestImpl(
           requesting_origin, user_gesture, permission_type_,
diff --git a/chrome/browser/permissions/permission_manager.cc b/chrome/browser/permissions/permission_manager.cc
index 26ed924..9ccf74d6 100644
--- a/chrome/browser/permissions/permission_manager.cc
+++ b/chrome/browser/permissions/permission_manager.cc
@@ -302,12 +302,9 @@
 
 bool PermissionManager::IsPermissionBubbleManagerMissing(
     content::WebContents* web_contents) {
-#if defined(OS_ANDROID)
-  // Can't be missing if it isn't needed to begin with.
+  // TODO(felt): Remove this method entirely. Leaving it to make a minimal
+  // last-minute merge to 46. See crbug.com/457091 and crbug.com/534631.
   return false;
-#else
-  return PermissionBubbleManager::FromWebContents(web_contents) == nullptr;
-#endif
 }
 
 void PermissionManager::OnContentSettingChanged(
diff --git a/chrome/browser/prefetch/prefetch_browsertest.cc b/chrome/browser/prefetch/prefetch_browsertest.cc
index 8d2408b2..91b6aaf3 100644
--- a/chrome/browser/prefetch/prefetch_browsertest.cc
+++ b/chrome/browser/prefetch/prefetch_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/utf_string_conversions.h"
@@ -9,7 +10,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc
index 9fc1c87f..687bd27 100644
--- a/chrome/browser/prefs/command_line_pref_store.cc
+++ b/chrome/browser/prefs/command_line_pref_store.cc
@@ -15,6 +15,7 @@
 #include "base/values.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "ui/base/ui_base_switches.h"
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 9885761..4de2f78 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/profile_chooser_constants.h"
 #include "chrome/browser/ui/user_manager.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
@@ -33,6 +32,7 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/user_metrics.h"
 
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js
index d6c6389..d0a64e3 100644
--- a/chrome/browser/resources/md_downloads/crisper.js
+++ b/chrome/browser/resources/md_downloads/crisper.js
@@ -10844,12 +10844,12 @@
     },
 
     /** @private */
-    onCancelClick_: function() {
+    onCancelTap_: function() {
       downloads.ActionService.getInstance().cancel(this.data.id);
     },
 
     /** @private */
-    onDiscardDangerous_: function() {
+    onDiscardDangerousTap_: function() {
       downloads.ActionService.getInstance().discardDangerous(this.data.id);
     },
 
@@ -10866,38 +10866,38 @@
      * @param {Event} e
      * @private
      */
-    onFileLinkClick_: function(e) {
+    onFileLinkTap_: function(e) {
       e.preventDefault();
       downloads.ActionService.getInstance().openFile(this.data.id);
     },
 
     /** @private */
-    onPauseClick_: function() {
+    onPauseTap_: function() {
       downloads.ActionService.getInstance().pause(this.data.id);
     },
 
     /** @private */
-    onRemoveClick_: function() {
+    onRemoveTap_: function() {
       downloads.ActionService.getInstance().remove(this.data.id);
     },
 
     /** @private */
-    onResumeClick_: function() {
+    onResumeTap_: function() {
       downloads.ActionService.getInstance().resume(this.data.id);
     },
 
     /** @private */
-    onRetryClick_: function() {
+    onRetryTap_: function() {
       downloads.ActionService.getInstance().download(this.data.url);
     },
 
     /** @private */
-    onSaveDangerous_: function() {
+    onSaveDangerousTap_: function() {
       downloads.ActionService.getInstance().saveDangerous(this.data.id);
     },
 
     /** @private */
-    onShowClick_: function() {
+    onShowTap_: function() {
       downloads.ActionService.getInstance().show(this.data.id);
     },
   });
@@ -15378,7 +15378,7 @@
     },
 
     /** @private */
-    onClearAllClick_: function() {
+    onClearAllTap_: function() {
       assert(this.canClearAll());
       downloads.ActionService.getInstance().clearAll();
     },
@@ -15395,7 +15395,7 @@
     },
 
     /** @private */
-    onOpenDownloadsFolderClick_: function() {
+    onOpenDownloadsFolderTap_: function() {
       downloads.ActionService.getInstance().openDownloadsFolder();
     },
 
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index 1d6dfa3..c090672 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -26,7 +26,7 @@
         <div id="title-area"><!--
           Can't have any line breaks.
           --><a is="action-link" id="file-link" href="[[data.url]]"
-              on-click="onFileLinkClick_"
+              on-tap="onFileLinkTap_"
               hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><!--
           Before #name.
           --><span id="name"
@@ -46,22 +46,22 @@
 
         <div id="safe" class="controls" hidden="[[isDangerous_]]">
           <a is="action-link" id="show" i18n-content="controlShowInFolder"
-              on-click="onShowClick_" hidden="[[!completelyOnDisk_]]"></a>
+              on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a>
           <template is="dom-if" if="[[data.retry]]">
             <paper-button id="retry"
-                on-click="onRetryClick_">[[i18n_.retry]]</paper-button>
+                on-tap="onRetryTap_">[[i18n_.retry]]</paper-button>
           </template>
           <template is="dom-if" if="[[isInProgress_]]">
             <paper-button id="pause"
-                on-click="onPauseClick_">[[i18n_.pause]]</paper-button>
+                on-tap="onPauseTap_">[[i18n_.pause]]</paper-button>
           </template>
           <template is="dom-if" if="[[data.resume]]">
             <paper-button id="resume"
-                on-click="onResumeClick_">[[i18n_.resume]]</paper-button>
+                on-tap="onResumeTap_">[[i18n_.resume]]</paper-button>
           </template>
           <template is="dom-if" if="[[showCancel_]]">
             <paper-button id="cancel"
-                on-click="onCancelClick_">[[i18n_.cancel]]</paper-button>
+                on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button>
           </template>
           <span id="controlled-by"><!-- Text populated dynamically. --></span>
         </div>
@@ -70,17 +70,17 @@
           <div id="dangerous" class="controls">
             <!-- Dangerous file types (e.g. .exe, .jar). -->
             <template is="dom-if" if="[[!isMalware_]]">
-              <paper-button id="discard" on-click="onDiscardDangerous_"
+              <paper-button id="discard" on-tap="onDiscardDangerousTap_"
                   class="discard">[[i18n_.discard]]</paper-button>
-              <paper-button id="save" on-click="onSaveDangerous_"
+              <paper-button id="save" on-tap="onSaveDangerousTap_"
                   class="keep">[[i18n_.save]]</paper-button>
             </template>
 
             <!-- Things that safe browsing has determined to be dangerous. -->
             <template is="dom-if" if="[[isMalware_]]">
-              <paper-button id="danger-remove" on-click="onDiscardDangerous_"
+              <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_"
                   class="discard">[[i18n_.remove]]</paper-button>
-              <paper-button id="restore" on-click="onSaveDangerous_"
+              <paper-button id="restore" on-tap="onSaveDangerousTap_"
                   class="keep">[[i18n_.restore]</paper-button>
             </template>
           </div>
@@ -91,7 +91,7 @@
         <paper-icon-button id="remove" icon="clear"
             i18n-values="title:controlRemoveFromList"
             style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]"
-            on-click="onRemoveClick_"></paper-icon-button>
+            on-tap="onRemoveTap_"></paper-icon-button>
       </div>
 
       <div id="incognito" i18n-values="title:inIncognito"
diff --git a/chrome/browser/resources/md_downloads/item.js b/chrome/browser/resources/md_downloads/item.js
index a750e61..7b8d0079 100644
--- a/chrome/browser/resources/md_downloads/item.js
+++ b/chrome/browser/resources/md_downloads/item.js
@@ -263,12 +263,12 @@
     },
 
     /** @private */
-    onCancelClick_: function() {
+    onCancelTap_: function() {
       downloads.ActionService.getInstance().cancel(this.data.id);
     },
 
     /** @private */
-    onDiscardDangerous_: function() {
+    onDiscardDangerousTap_: function() {
       downloads.ActionService.getInstance().discardDangerous(this.data.id);
     },
 
@@ -285,38 +285,38 @@
      * @param {Event} e
      * @private
      */
-    onFileLinkClick_: function(e) {
+    onFileLinkTap_: function(e) {
       e.preventDefault();
       downloads.ActionService.getInstance().openFile(this.data.id);
     },
 
     /** @private */
-    onPauseClick_: function() {
+    onPauseTap_: function() {
       downloads.ActionService.getInstance().pause(this.data.id);
     },
 
     /** @private */
-    onRemoveClick_: function() {
+    onRemoveTap_: function() {
       downloads.ActionService.getInstance().remove(this.data.id);
     },
 
     /** @private */
-    onResumeClick_: function() {
+    onResumeTap_: function() {
       downloads.ActionService.getInstance().resume(this.data.id);
     },
 
     /** @private */
-    onRetryClick_: function() {
+    onRetryTap_: function() {
       downloads.ActionService.getInstance().download(this.data.url);
     },
 
     /** @private */
-    onSaveDangerous_: function() {
+    onSaveDangerousTap_: function() {
       downloads.ActionService.getInstance().saveDangerous(this.data.id);
     },
 
     /** @private */
-    onShowClick_: function() {
+    onShowTap_: function() {
       downloads.ActionService.getInstance().show(this.data.id);
     },
   });
diff --git a/chrome/browser/resources/md_downloads/toolbar.html b/chrome/browser/resources/md_downloads/toolbar.html
index 5161a21..53234f8 100644
--- a/chrome/browser/resources/md_downloads/toolbar.html
+++ b/chrome/browser/resources/md_downloads/toolbar.html
@@ -17,9 +17,9 @@
     </div>
     <div id="actions">
       <paper-button class="clear-all" i18n-content="clearAll"
-          on-click="onClearAllClick_"></paper-button>
+          on-tap="onClearAllTap_"></paper-button>
       <paper-button i18n-content="openDownloadsFolder"
-          on-click="onOpenDownloadsFolderClick_"></paper-button>
+          on-tap="onOpenDownloadsFolderTap_"></paper-button>
     </div>
     <div id="search">
       <cr-search-field id="search-input"
@@ -29,9 +29,9 @@
              class="dropdown-trigger"></paper-icon-button>
         <paper-menu class="dropdown-content">
           <paper-item class="clear-all" i18n-content="clearAll"
-              on-click="onClearAllClick_"></paper-item>
+              on-tap="onClearAllTap_"></paper-item>
           <paper-item i18n-content="openDownloadsFolder"
-              on-click="onOpenDownloadsFolderClick_"></paper-item>
+              on-tap="onOpenDownloadsFolderTap_"></paper-item>
         </paper-menu>
       </paper-menu-button>
     </div>
diff --git a/chrome/browser/resources/md_downloads/toolbar.js b/chrome/browser/resources/md_downloads/toolbar.js
index f9437c02..ff9173e 100644
--- a/chrome/browser/resources/md_downloads/toolbar.js
+++ b/chrome/browser/resources/md_downloads/toolbar.js
@@ -40,7 +40,7 @@
     },
 
     /** @private */
-    onClearAllClick_: function() {
+    onClearAllTap_: function() {
       assert(this.canClearAll());
       downloads.ActionService.getInstance().clearAll();
     },
@@ -57,7 +57,7 @@
     },
 
     /** @private */
-    onOpenDownloadsFolderClick_: function() {
+    onOpenDownloadsFolderTap_: function() {
       downloads.ActionService.getInstance().openDownloadsFolder();
     },
 
diff --git a/chrome/browser/resources/md_downloads/vulcanize_readme.md b/chrome/browser/resources/md_downloads/vulcanize_readme.md
index 858d6f06..1a20f9c 100644
--- a/chrome/browser/resources/md_downloads/vulcanize_readme.md
+++ b/chrome/browser/resources/md_downloads/vulcanize_readme.md
@@ -7,6 +7,7 @@
 ## Required software
 
 Vulcanization currently requires:
+
 - node.js: v0.10.25 (can be found with `node --version`)
 - npm: 1.3.10 (can be found with `npm --version`)
 - vulcanize: 1.12.3 (can be found with `vulcanize --version`)
diff --git a/chrome/browser/resources/md_downloads/vulcanized.html b/chrome/browser/resources/md_downloads/vulcanized.html
index fe3dd30..37e4737 100644
--- a/chrome/browser/resources/md_downloads/vulcanized.html
+++ b/chrome/browser/resources/md_downloads/vulcanized.html
@@ -2130,7 +2130,7 @@
       </div>
 
       <div id="details">
-        <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-click="onFileLinkClick_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span>
+        <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-tap="onFileLinkTap_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span>
           <span id="tag">[[computeTag_(data.state, data.last_reason_text, data.file_externally_removed)]]</span>
         </div>
 
@@ -2143,18 +2143,18 @@
         </template>
 
         <div id="safe" class="controls" hidden="[[isDangerous_]]">
-          <a is="action-link" id="show" i18n-content="controlShowInFolder" on-click="onShowClick_" hidden="[[!completelyOnDisk_]]"></a>
+          <a is="action-link" id="show" i18n-content="controlShowInFolder" on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a>
           <template is="dom-if" if="[[data.retry]]">
-            <paper-button id="retry" on-click="onRetryClick_">[[i18n_.retry]]</paper-button>
+            <paper-button id="retry" on-tap="onRetryTap_">[[i18n_.retry]]</paper-button>
           </template>
           <template is="dom-if" if="[[isInProgress_]]">
-            <paper-button id="pause" on-click="onPauseClick_">[[i18n_.pause]]</paper-button>
+            <paper-button id="pause" on-tap="onPauseTap_">[[i18n_.pause]]</paper-button>
           </template>
           <template is="dom-if" if="[[data.resume]]">
-            <paper-button id="resume" on-click="onResumeClick_">[[i18n_.resume]]</paper-button>
+            <paper-button id="resume" on-tap="onResumeTap_">[[i18n_.resume]]</paper-button>
           </template>
           <template is="dom-if" if="[[showCancel_]]">
-            <paper-button id="cancel" on-click="onCancelClick_">[[i18n_.cancel]]</paper-button>
+            <paper-button id="cancel" on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button>
           </template>
           <span id="controlled-by"></span>
         </div>
@@ -2163,21 +2163,21 @@
           <div id="dangerous" class="controls">
             
             <template is="dom-if" if="[[!isMalware_]]">
-              <paper-button id="discard" on-click="onDiscardDangerous_" class="discard">[[i18n_.discard]]</paper-button>
-              <paper-button id="save" on-click="onSaveDangerous_" class="keep">[[i18n_.save]]</paper-button>
+              <paper-button id="discard" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.discard]]</paper-button>
+              <paper-button id="save" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.save]]</paper-button>
             </template>
 
             
             <template is="dom-if" if="[[isMalware_]]">
-              <paper-button id="danger-remove" on-click="onDiscardDangerous_" class="discard">[[i18n_.remove]]</paper-button>
-              <paper-button id="restore" on-click="onSaveDangerous_" class="keep">[[i18n_.restore]</paper-button>
+              <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.remove]]</paper-button>
+              <paper-button id="restore" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.restore]</paper-button>
             </template>
           </div>
         </template>
       </div>
 
       <div id="remove-wrapper" class="icon-wrapper">
-        <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-click="onRemoveClick_"></paper-icon-button>
+        <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-tap="onRemoveTap_"></paper-icon-button>
       </div>
 
       <div id="incognito" i18n-values="title:inIncognito" hidden="[[!data.otr]]"></div>
@@ -3254,16 +3254,16 @@
       <h1 i18n-content="title"></h1>
     </div>
     <div id="actions">
-      <paper-button class="clear-all" i18n-content="clearAll" on-click="onClearAllClick_"></paper-button>
-      <paper-button i18n-content="openDownloadsFolder" on-click="onOpenDownloadsFolderClick_"></paper-button>
+      <paper-button class="clear-all" i18n-content="clearAll" on-tap="onClearAllTap_"></paper-button>
+      <paper-button i18n-content="openDownloadsFolder" on-tap="onOpenDownloadsFolderTap_"></paper-button>
     </div>
     <div id="search">
       <cr-search-field id="search-input" i18n-values="label:search;clear-label:clearSearch"></cr-search-field>
       <paper-menu-button id="more" horizontal-align="[[overflowAlign_]]">
         <paper-icon-button icon="more-vert" i18n-values="title:moreActions" class="dropdown-trigger"></paper-icon-button>
         <paper-menu class="dropdown-content">
-          <paper-item class="clear-all" i18n-content="clearAll" on-click="onClearAllClick_"></paper-item>
-          <paper-item i18n-content="openDownloadsFolder" on-click="onOpenDownloadsFolderClick_"></paper-item>
+          <paper-item class="clear-all" i18n-content="clearAll" on-tap="onClearAllTap_"></paper-item>
+          <paper-item i18n-content="openDownloadsFolder" on-tap="onOpenDownloadsFolderTap_"></paper-item>
         </paper-menu>
       </paper-menu-button>
     </div>
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
index f65ae61e..f110a3c6 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
@@ -39,7 +39,7 @@
     <paper-menu id="cast-mode-list"
         hidden$="[[computeCastModeHidden_(currentView_)]]">
       <template is="dom-repeat" id="defaultCastModeList"
-          items="[[computeDefaultCastModeList_(castModeList)]]">
+          items="[[computeDefaultCastModeList_(castModeList_)]]">
         <paper-item on-click="onCastModeClick_">
           <iron-icon class="cast-mode-icon"
               icon="[[computeCastModeIcon_(item)]]">
@@ -48,11 +48,11 @@
         </paper-item>
       </template>
       <div id="share-screen-text"
-          hidden$="[[computeShareScreenSubheadingHidden_(castModeList)]]">
+          hidden$="[[computeShareScreenSubheadingHidden_(castModeList_)]]">
         <span>[[shareYourScreenSubheadingText_]]</span>
       </div>
       <template is="dom-repeat" id="nonDefaultCastModeList"
-          items="[[computeNonDefaultCastModeList_(castModeList)]]">
+          items="[[computeNonDefaultCastModeList_(castModeList_)]]">
         <paper-item on-click="onCastModeClick_">
           <iron-icon class="cast-mode-icon"
               icon="[[computeCastModeIcon_(item)]]">
@@ -70,7 +70,7 @@
     <div id="sink-list-view"
         hidden$="[[computeSinkListViewHidden_(currentView_, issue)]]">
       <div id="device-missing"
-           hidden$="[[!computeSinkListHidden_(sinkList)]]">
+           hidden$="[[!computeSinkListHidden_(sinksToShow_)]]">
         <paper-spinner id="searching-devices-spinner" active
             hidden$="[[computeSpinnerHidden_(justOpened_)]]">
         </paper-spinner>
@@ -81,7 +81,7 @@
       </div>
       <paper-menu id="sink-list"
           hidden$="[[computeSinkListHidden_(currentView_, issue)]]">
-        <template is="dom-repeat" id="sinkList" items="[[sinkList]]">
+        <template is="dom-repeat" id="sinkList" items="[[sinksToShow_]]">
           <paper-item class="sink" on-click="onSinkClick_">
             <div class="sink-content">
               <div>
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
index 0a98d75..3e2b959 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
@@ -9,15 +9,6 @@
 
   properties: {
     /**
-     * The list of CastModes to show.
-     * @type {!Array<!media_router.CastMode>}
-     */
-    castModeList: {
-      type: Array,
-      value: [],
-    },
-
-    /**
      * The possible states of media-router-container. Used to determine which
      * components of media-router-container to show.
      * This is a property of media-router-container because it is used in
@@ -38,6 +29,26 @@
     },
 
     /**
+     * The list of available sinks.
+     * @type {!Array<!media_router.Sink>}
+     */
+    allSinks: {
+      type: Array,
+      value: [],
+      observer: 'reindexSinksAndRebuildSinksToShow_',
+    },
+
+    /**
+     * The list of CastModes to show.
+     * @private {!Array<!media_router.CastMode>}
+     */
+    castModeList_: {
+      type: Array,
+      value: [],
+      observer: 'checkCurrentCastMode_',
+    },
+
+    /**
      * The ID of the Sink currently being launched.
      * @private {string}
      */
@@ -158,14 +169,11 @@
     },
 
     /**
-     * The value of the selected cast mode in |castModeList|, or -1 if the
-     * user has not explicitly selected a cast mode.
+     * The value of the selected cast mode in |castModeList_|.
      * @private {number}
      */
     selectedCastModeValue_: {
       type: Number,
-      value: -1,
-      observer: 'showSinkList_',
     },
 
     /**
@@ -178,16 +186,6 @@
     },
 
     /**
-     * The list of available sinks.
-     * @type {!Array<!media_router.Sink>}
-     */
-    sinkList: {
-      type: Array,
-      value: [],
-      observer: 'rebuildSinkMap_',
-    },
-
-    /**
      * Maps media_router.Sink.id to corresponding media_router.Sink.
      * @private {!Object<!string, !media_router.Sink>}
      */
@@ -198,12 +196,21 @@
 
     /**
      * Maps media_router.Sink.id to corresponding media_router.Route.
-     * @private {!Object<!string, ?media_router.Route>}
+     * @private {!Object<!string, !media_router.Route>}
      */
     sinkToRouteMap_: {
       type: Object,
       value: {},
     },
+
+    /**
+     * Sinks to show for the currently selected cast mode.
+     * @private {!Array<!media_router.Sink>}
+     */
+    sinksToShow_: {
+      type: Array,
+      value: [],
+    },
   },
 
   ready: function() {
@@ -219,6 +226,18 @@
   },
 
   /**
+   * Checks that the currently selected cast mode is still in the
+   * updated list of available cast modes. If not, then update the selected
+   * cast mode to the first available cast mode on the list.
+   */
+  checkCurrentCastMode_: function() {
+    if (this.castModeList_.length > 0 &&
+        !this.findCastModeByType_(this.selectedCastModeValue_)) {
+      this.setSelectedCastMode_(this.castModeList_[0]);
+    }
+  },
+
+  /**
    * @param {CONTAINER_VIEW_} view The current view.
    * @return {string} The current arrow-drop-* icon to use.
    * @private
@@ -242,7 +261,7 @@
    *     icon for.
    * @return {string} The Polymer <iron-icon> icon to use. The format is
    *     <iconset>:<icon>, where <iconset> is the set ID and <icon> is the name
-   *     of the icon. <iconset>: may be ommitted if <icon> is from the default
+   *     of the icon. <iconset>: may be omitted if <icon> is from the default
    *     set.
    * @private
    */
@@ -425,8 +444,8 @@
    * @return {boolean} Whether or not to hide the sink list.
    * @private
    */
-  computeSinkListHidden_: function(sinkList) {
-    return sinkList.length == 0;
+  computeSinkListHidden_: function(sinksToShow) {
+    return sinksToShow.length == 0;
   },
 
   /**
@@ -450,6 +469,37 @@
   },
 
   /**
+   * Helper function to locate the CastMode object with the given type in
+   * castModeList.
+   *
+   * @param {number} castModeType Type of cast mode to look for.
+   * @return {?media_router.CastMode} CastMode object with the given type in
+   *     castModeList, or undefined if not found.
+   */
+  findCastModeByType_: function(castModeType) {
+    return this.castModeList_.find(function(element, index, array) {
+      return element.type == castModeType;
+    });
+  },
+
+  /**
+   * Sets the list of available cast modes and the initial cast mode.
+   *
+   * @param {!Array<!media_router.CastMode>} availableCastModes The list
+   *     of available cast modes.
+   * @param {number} initialCastModeType The initial cast mode when dialog is
+   *     opened.
+   */
+  initializeCastModes: function(availableCastModes, initialCastModeType) {
+    this.castModeList_ = availableCastModes;
+    var castMode = this.findCastModeByType_(initialCastModeType);
+    if (!castMode)
+      return;
+
+    this.setSelectedCastMode_(castMode);
+  },
+
+  /**
    * Updates |currentView_| if the dialog had just opened and there's
    * only one local route.
    *
@@ -478,9 +528,8 @@
     if (!clickedMode)
       return;
 
-    this.headerText = clickedMode.description;
-    this.headerTextTooltip = clickedMode.host;
-    this.selectedCastModeValue_ = clickedMode.type;
+    this.setSelectedCastMode_(clickedMode);
+    this.showSinkList_();
   },
 
   /**
@@ -577,19 +626,53 @@
 
     this.sinkToRouteMap_ = tempSinkToRouteMap;
     this.maybeShowRouteDetailsOnOpen_(localRoute);
+    this.rebuildSinksToShow_();
   },
 
   /**
-   * Called when |sinkList| is updated. Rebuilds |sinkMap_|.
+   * Rebuilds the list of sinks to be shown for the current cast mode.
+   * A sink should be shown if it is compatible with the current cast mode, or
+   * if the sink is associated with a route.
+   */
+  rebuildSinksToShow_: function() {
+    var sinksToShow = [];
+    this.allSinks.forEach(function(element, index, array) {
+      if (element.castModes.indexOf(this.selectedCastModeValue_) != -1 ||
+          this.sinkToRouteMap_[element.id]) {
+        sinksToShow.push(element);
+      }
+    }, this);
+    this.sinksToShow_ = sinksToShow;
+  },
+
+  /**
+   * Called when |allSinks| is updated.
    *
    * @private
    */
-  rebuildSinkMap_: function() {
+  reindexSinksAndRebuildSinksToShow_: function() {
     this.sinkMap_ = {};
 
-    this.sinkList.forEach(function(sink) {
+    this.allSinks.forEach(function(sink) {
       this.sinkMap_[sink.id] = sink;
     }, this);
+
+    this.rebuildSinksToShow_();
+  },
+
+  /**
+   * Updates the selected cast mode, and updates the header text fields
+   * according to the cast mode.
+   *
+   * @param {!media_router.CastMode} castMode
+   */
+  setSelectedCastMode_: function(castMode) {
+    if (castMode.type != this.selectedCastModeValue_) {
+      this.headerText = castMode.description;
+      this.headerTextTooltip = castMode.host;
+      this.selectedCastModeValue_ = castMode.type;
+      this.rebuildSinksToShow_();
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/media_router/media_router_ui_interface.js b/chrome/browser/resources/media_router/media_router_ui_interface.js
index 78008e10..7406159 100644
--- a/chrome/browser/resources/media_router/media_router_ui_interface.js
+++ b/chrome/browser/resources/media_router/media_router_ui_interface.js
@@ -50,27 +50,25 @@
   /**
    * Populates the WebUI with data obtained from Media Router.
    *
-   * @param {headerText: string,
-   *         headerTextTooltip: string,
-   *         deviceMissingUrl: string,
+   * @param {deviceMissingUrl: string,
    *         sinks: !Array<!media_router.Sink>,
    *         routes: !Array<!media_router.Route>,
-   *         castModes: !Array<!media_router.CastMode>} data
+   *         castModes: !Array<!media_router.CastMode>,
+   *         initialCastModeType: number} data
    * Parameters in data:
-   *   headerText - text to be displayed in the header of the WebUI.
-   *   headerTextTooltip - tooltip to be displayed for the header of the WebUI.
    *   deviceMissingUrl - url to be opened on "Device missing?" clicked.
    *   sinks - list of sinks to be displayed.
    *   routes - list of routes that are associated with the sinks.
    *   castModes - list of available cast modes.
+   *   initialCastModeType - cast mode to show initially. Expected to be
+   *       included in |castModes|.
    */
   function setInitialData(data) {
-    container.headerText = data['headerText'];
-    container.headerTextTooltip = data['headerTextTooltip'];
     container.deviceMissingUrl = data['deviceMissingUrl'];
-    container.sinkList = data['sinks'];
+    container.allSinks = data['sinks'];
     container.routeList = data['routes'];
-    container.castModeList = data['castModes'];
+    container.initializeCastModes(data['castModes'],
+        data['initialCastModeType']);
   }
 
   /**
@@ -98,7 +96,7 @@
    * @param {!Array<!media_router.Sink>} sinkList
    */
   function setSinkList(sinkList) {
-    container.sinkList = sinkList;
+    container.allSinks = sinkList;
   }
 
   return {
@@ -158,7 +156,7 @@
    *
    * @param {string} sinkId The sink ID.
    * @param {number} selectedCastMode The value of the cast mode the user
-   *   selected, or -1 if the user has not explicitly selected a mode.
+   *   selected.
    */
   function requestRoute(sinkId, selectedCastMode) {
     chrome.send('requestRoute',
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index d7ae920..5afc4c7 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -2,7 +2,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html">
 
-<dom-module id="cr-settings-a11y-page">
+<dom-module id="settings-a11y-page">
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="a11y_page.css">
   <template>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.js b/chrome/browser/resources/settings/a11y_page/a11y_page.js
index f7286ad..8fb7448 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.js
@@ -4,21 +4,21 @@
 
 /**
  * @fileoverview
- * 'cr-settings-a11y-page' is the settings page containing accessibility
+ * 'settings-a11y-page' is the settings page containing accessibility
  * settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page>
+ *      <settings-a11y-page prefs="{{prefs}}"></settings-a11y-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-a11y-page
+ * @element settings-a11y-page
  */
 Polymer({
-  is: 'cr-settings-a11y-page',
+  is: 'settings-a11y-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.html b/chrome/browser/resources/settings/advanced_page/advanced_page.html
index ae69661b..30eb95d 100644
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.html
+++ b/chrome/browser/resources/settings/advanced_page/advanced_page.html
@@ -10,40 +10,40 @@
 <link rel="import" href="chrome://md-settings/date_time_page/date_time_page.html">
 </if>
 
-<dom-module id="cr-settings-advanced-page">
+<dom-module id="settings-advanced-page">
   <link rel="import" type="css" href="advanced_page.css">
   <template>
 <if expr="chromeos">
     <settings-section i18n-values="page-title:dateTimePageTitle"
         current-route="[[currentRoute]]" section="dateTime">
-      <cr-settings-date-time-page prefs="{{prefs}}">
-      </cr-settings-date-time-page>
+      <settings-date-time-page prefs="{{prefs}}">
+      </settings-date-time-page>
     </settings-section>
 </if>
     <settings-section i18n-values="page-title:siteSettingsLocation"
         current-route="[[currentRoute]]" section="location">
-      <cr-settings-location-page prefs="{{prefs}}">
-      </cr-settings-location-page>
+      <settings-location-page prefs="{{prefs}}">
+      </settings-location-page>
     </settings-section>
 
     <settings-section i18n-values="page-title:privacyPageTitle"
         current-route="[[currentRoute]]" section="privacy">
-      <cr-settings-privacy-page prefs="{{prefs}}"
+      <settings-privacy-page prefs="{{prefs}}"
         current-route="{{currentRoute}}">
-      </cr-settings-privacy-page>
+      </settings-privacy-page>
     </settings-section>
 
     <settings-section i18n-values="page-title:languagesPageTitle"
         current-route="[[currentRoute]]" section="languages">
-      <cr-settings-languages-page prefs="{{prefs}}"
+      <settings-languages-page prefs="{{prefs}}"
           current-route="{{currentRoute}}">
-      </cr-settings-languages-page>
+      </settings-languages-page>
     </settings-section>
 
     <settings-section i18n-values="page-title:downloadsPageTitle"
         current-route="[[currentRoute]]" section="downloads">
-      <cr-settings-downloads-page prefs="{{prefs}}">
-      </cr-settings-downloads-page>
+      <settings-downloads-page prefs="{{prefs}}">
+      </settings-downloads-page>
     </settings-section>
 
 <if expr="chromeos">
@@ -51,7 +51,7 @@
          on desktop. -->
     <settings-section i18n-values="page-title:a11yPageTitle"
         current-route="[[currentRoute]]" section="a11y">
-      <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page>
+      <settings-a11y-page prefs="{{prefs}}"></settings-a11y-page>
     </settings-section>
 </if>
   </template>
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.js b/chrome/browser/resources/settings/advanced_page/advanced_page.js
index 0943c0dd..92be225 100644
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.js
+++ b/chrome/browser/resources/settings/advanced_page/advanced_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-advanced-page' is the settings page containing the advanced
+ * 'settings-advanced-page' is the settings page containing the advanced
  * settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-advanced-page prefs="{{prefs}}">
- *      </cr-settings-advanced-page>
+ *      <settings-advanced-page prefs="{{prefs}}">
+ *      </settings-advanced-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-advanced-page
+ * @element settings-advanced-page
  */
 Polymer({
-  is: 'cr-settings-advanced-page',
+  is: 'settings-advanced-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index 144db018..769d362 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://md-settings/controls/settings_input.html">
 <link rel="import" href="chrome://md-settings/controls/settings_radio_group.html">
 
-<dom-module id="cr-settings-appearance-page">
+<dom-module id="settings-appearance-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css"
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chrome/browser/resources/settings/appearance_page/appearance_page.js
index d12a1acc..e3a3bff 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.js
@@ -3,22 +3,22 @@
 // found in the LICENSE file.
 
 /**
- * 'cr-settings-appearance-page' is the settings page containing appearance
+ * 'settings-appearance-page' is the settings page containing appearance
  * settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-appearance-page prefs="{{prefs}}">
- *      </cr-settings-appearance-page>
+ *      <settings-appearance-page prefs="{{prefs}}">
+ *      </settings-appearance-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-appearance-page
+ * @element settings-appearance-page
  */
 Polymer({
-  is: 'cr-settings-appearance-page',
+  is: 'settings-appearance-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 843a322b..cf63ca38 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -13,7 +13,7 @@
 <link rel="import" href="chrome://md-settings/signin_page/signin_page.html">
 </if>
 
-<dom-module id="cr-settings-basic-page">
+<dom-module id="settings-basic-page">
   <link rel="import" type="css" href="basic_page.css">
   <template>
 <if expr="not chromeos">
@@ -26,33 +26,33 @@
 <if expr="chromeos">
     <settings-section i18n-values="page-title:internetPageTitle"
         current-route="[[currentRoute]]" section="internet">
-      <cr-settings-internet-page current-route="{{currentRoute}}">
-      </cr-settings-internet-page>
+      <settings-internet-page current-route="{{currentRoute}}">
+      </settings-internet-page>
     </settings-section>
 </if>
     <settings-section i18n-values="page-title:appearancePageTitle"
         current-route="[[currentRoute]]" section="appearance">
-      <cr-settings-appearance-page prefs="{{prefs}}">
-      </cr-settings-appearance-page>
+      <settings-appearance-page prefs="{{prefs}}">
+      </settings-appearance-page>
     </settings-section>
 
     <settings-section i18n-values="page-title:onStartup"
         current-route="[[currentRoute]]" section="on-startup">
-      <cr-settings-on-startup-page
+      <settings-on-startup-page
           prefs="{{prefs}}" current-route="{{currentRoute}}">
-      </cr-settings-on-startup-page>
+      </settings-on-startup-page>
     </settings-section>
 
     <settings-section i18n-values="page-title:searchPageTitle"
         current-route="[[currentRoute]]" section="search">
-      <cr-settings-search-page current-route="{{currentRoute}}">
-      </cr-settings-search-page>
+      <settings-search-page current-route="{{currentRoute}}">
+      </settings-search-page>
     </settings-section>
 
 <if expr="chromeos">
     <settings-section i18n-values="page-title:usersPageTitle"
         current-route="[[currentRoute]]" section="users">
-      <cr-settings-users-page prefs="{{prefs}}"></cr-settings-users-page>
+      <settings-users-page prefs="{{prefs}}"></settings-users-page>
     </settings-section>
 </if>
   </template>
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index 024794ac..c23e61b 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -4,20 +4,20 @@
 
 /**
  * @fileoverview
- * 'cr-settings-basic-page' is the settings page containing the basic settings.
+ * 'settings-basic-page' is the settings page containing the basic settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-basic-page prefs="{{prefs}}"></cr-settings-basic-page>
+ *      <settings-basic-page prefs="{{prefs}}"></settings-basic-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-basic-page
+ * @element settings-basic-page
  */
 Polymer({
-  is: 'cr-settings-basic-page',
+  is: 'settings-basic-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
index 3c120ef..70a5b6ea 100644
--- a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
+++ b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
 
-<dom-module id="cr-settings-certificate-manager-page">
+<dom-module id="settings-certificate-manager-page">
   <link rel="import" type="css" href="certificate_manager_page.css">
   <template>
     <paper-tabs selected="{{selected}}">
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.js b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.js
index 253e283..8c9f3134 100644
--- a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.js
+++ b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-certificate-manager-page' is the settings page containing SSL
+ * 'settings-certificate-manager-page' is the settings page containing SSL
  * certificate settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-certificate-manager-page prefs="{{prefs}}">
- *      </cr-settings-certificate-manager-page>
+ *      <settings-certificate-manager-page prefs="{{prefs}}">
+ *      </settings-certificate-manager-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-certificate-manager-page
+ * @element settings-certificate-manager-page
  */
 Polymer({
-  is: 'cr-settings-certificate-manager-page',
+  is: 'settings-certificate-manager-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html
index e4c0488..03d9915 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-dropdown-menu/paper-dropdown-menu.html">
 <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html">
 
-<dom-module id="cr-settings-clear-browsing-data-page">
+<dom-module id="settings-clear-browsing-data-page">
   <link rel="import" type="css" href="clear_browsing_data_page.css">
   <template>
     <div>
diff --git a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.js b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.js
index df6ae06..7e76486 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.js
+++ b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-clear-browsing-data-page' provides options to delete browsing
+ * 'settings-clear-browsing-data-page' provides options to delete browsing
  * data that has been cached by chromium.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-clear-browsing-data-page prefs="{{prefs}}">
- *      </cr-settings-clear-browsing-data-page>
+ *      <settings-clear-browsing-data-page prefs="{{prefs}}">
+ *      </settings-clear-browsing-data-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-privacy-page
+ * @element settings-privacy-page
  */
 Polymer({
-  is: 'cr-settings-clear-browsing-data-page',
+  is: 'settings-clear-browsing-data-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/controls/settings_checkbox.html b/chrome/browser/resources/settings/controls/settings_checkbox.html
index 133f839..25a3cae 100644
--- a/chrome/browser/resources/settings/controls/settings_checkbox.html
+++ b/chrome/browser/resources/settings/controls/settings_checkbox.html
@@ -9,7 +9,7 @@
   <link rel="import" type="css" href="settings_checkbox.css">
   <template>
     <cr-events id="events"></cr-events>
-    <cr-settings-pref-tracker pref="[[pref]]"></cr-settings-pref-tracker>
+    <settings-pref-tracker pref="[[pref]]"></settings-pref-tracker>
 
     <div id="outerDiv" class="layout horizontal center">
       <paper-checkbox id="checkbox" checked="{{checked}}"
diff --git a/chrome/browser/resources/settings/controls/settings_input.html b/chrome/browser/resources/settings/controls/settings_input.html
index 055d7986..3db53397 100644
--- a/chrome/browser/resources/settings/controls/settings_input.html
+++ b/chrome/browser/resources/settings/controls/settings_input.html
@@ -8,7 +8,7 @@
   <link rel="import" type="css" href="settings_input.css">
   <template>
     <cr-events id="events"></cr-events>
-    <cr-settings-pref-tracker pref="[[pref]]"></cr-settings-pref-tracker>
+    <settings-pref-tracker pref="[[pref]]"></settings-pref-tracker>
 
     <div id="outerDiv" class="layout horizontal center">
       <paper-input id="input" auto-validate value="{{value}}"
diff --git a/chrome/browser/resources/settings/date_time_page/date_time_page.html b/chrome/browser/resources/settings/date_time_page/date_time_page.html
index fa80295..6363c3f32 100644
--- a/chrome/browser/resources/settings/date_time_page/date_time_page.html
+++ b/chrome/browser/resources/settings/date_time_page/date_time_page.html
@@ -2,7 +2,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html">
 <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html">
 
-<dom-module id="cr-settings-date-time-page">
+<dom-module id="settings-date-time-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="date_time_page.css">
diff --git a/chrome/browser/resources/settings/date_time_page/date_time_page.js b/chrome/browser/resources/settings/date_time_page/date_time_page.js
index 73e7a10..cd0b5b7d 100644
--- a/chrome/browser/resources/settings/date_time_page/date_time_page.js
+++ b/chrome/browser/resources/settings/date_time_page/date_time_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-date-time-page' is the settings page containing date-time
+ * 'settings-date-time-page' is the settings page containing date-time
  * settings.
  *
  * Example:
  *
  *    <core-animated-pages>
- *      <cr-settings-date-time-page prefs="{{prefs}}">
- *      </cr-settings-date-time-page>
+ *      <settings-date-time-page prefs="{{prefs}}">
+ *      </settings-date-time-page>
  *      ... other pages ...
  *    </core-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-date-time-page
+ * @element settings-date-time-page
  */
 Polymer({
-  is: 'cr-settings-date-time-page',
+  is: 'settings-date-time-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/date_time_page/demo.html b/chrome/browser/resources/settings/date_time_page/demo.html
index 919675d9..e9129b3 100644
--- a/chrome/browser/resources/settings/date_time_page/demo.html
+++ b/chrome/browser/resources/settings/date_time_page/demo.html
@@ -6,7 +6,7 @@
   <script src="demo.js"></script>
 </head>
 <body unresolved>
-  <cr-settings-prefs></cr-settings-prefs>
-  <cr-settings-date-time-page></cr-settings-date-time-page>
+  <settings-prefs></settings-prefs>
+  <settings-date-time-page></settings-date-time-page>
 </body>
 </html>
diff --git a/chrome/browser/resources/settings/date_time_page/demo.js b/chrome/browser/resources/settings/date_time_page/demo.js
index 612cabe..b95aa392 100644
--- a/chrome/browser/resources/settings/date_time_page/demo.js
+++ b/chrome/browser/resources/settings/date_time_page/demo.js
@@ -4,6 +4,6 @@
 
 // Wire up the prefs to the date/time page.
 window.addEventListener('polymer-ready', function() {
-  var page = document.querySelector('cr-settings-date-time-page');
-  page.prefs = document.querySelector('cr-settings-prefs');
+  var page = document.querySelector('settings-date-time-page');
+  page.prefs = document.querySelector('settings-prefs');
 });
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html
index fad2c97..45b1ccd7 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.html
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html">
 <link rel="import" href="chrome://md-settings/controls/settings_input.html">
 
-<dom-module id="cr-settings-downloads-page">
+<dom-module id="settings-downloads-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="downloads_page.css">
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.js b/chrome/browser/resources/settings/downloads_page/downloads_page.js
index 252f04c..a12d76d3 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.js
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-downloads-page' is the settings page containing downloads
+ * 'settings-downloads-page' is the settings page containing downloads
  * settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-downloads-page prefs="{{prefs}}">
- *      </cr-settings-downloads-page>
+ *      <settings-downloads-page prefs="{{prefs}}">
+ *      </settings-downloads-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-downloads-page
+ * @element settings-downloads-page
  */
 Polymer({
-  is: 'cr-settings-downloads-page',
+  is: 'settings-downloads-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
index 9cd13af..9304312e 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -15,7 +15,7 @@
 <link rel="import" href="network_proxy.html">
 <link rel="import" href="network_siminfo.html">
 
-<dom-module id="cr-settings-internet-detail-page">
+<dom-module id="settings-internet-detail-page">
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="internet_detail_page.css">
   <template>
@@ -77,20 +77,20 @@
           </paper-button>
           <span class="flex"></span>
           <paper-button hidden$="[[!showViewAccount_(networkProperties)]]"
-              on-tap="onViewAccountClicked_">
+              on-tap="onViewAccountTap_">
             View Account
           </paper-button>
           <paper-button hidden$="[[!showActivate_(networkProperties)]]"
-              on-tap="onActivateClicked_">
+              on-tap="onActivateTap_">
             Activate
           </paper-button>
           <paper-button hidden$="[[!showConnect_(networkProperties)]]"
               disabled="[[!enableConnect_(networkProperties)]]"
-              on-tap="onConnectClicked_">
+              on-tap="onConnectTap_">
             Connect
           </paper-button>
           <paper-button hidden$="[[!showDisconnect_(networkProperties)]]"
-              on-tap="onDisconnectClicked_">
+              on-tap="onDisconnectTap_">
             Disconnect
           </paper-button>
         </div>
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 8fa56e8..5c272b7 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -4,11 +4,11 @@
 
 /**
  * @fileoverview
- * 'cr-settings-internet-detail' is the settings subpage containing details
+ * 'settings-internet-detail' is the settings subpage containing details
  * for a network.
  *
  * @group Chrome Settings Elements
- * @element cr-settings-internet-detail
+ * @element settings-internet-detail
  */
 (function() {
 'use strict';
@@ -16,7 +16,7 @@
 /** @const */ var CARRIER_VERIZON = 'Verizon Wireless';
 
 Polymer({
-  is: 'cr-settings-internet-detail-page',
+  is: 'settings-internet-detail-page',
 
   properties: {
     /**
@@ -244,8 +244,7 @@
    * @private
    */
   isConnectedState_: function(properties) {
-    return !!properties && properties.ConnectionState ==
-        CrOnc.ConnectionState.CONNECTED;
+    return properties.ConnectionState == CrOnc.ConnectionState.CONNECTED;
   },
 
   /**
@@ -254,7 +253,7 @@
    * @private
    */
   showConnect_: function(properties) {
-    return !!properties && properties.Type != CrOnc.Type.ETHERNET &&
+    return properties.Type != CrOnc.Type.ETHERNET &&
            properties.ConnectionState == CrOnc.ConnectionState.NOT_CONNECTED;
   },
 
@@ -327,39 +326,39 @@
    * @private
    */
   showDisconnect_: function(properties) {
-    return !!properties && properties.Type != CrOnc.Type.ETHERNET &&
+    return properties.Type != CrOnc.Type.ETHERNET &&
            properties.ConnectionState != CrOnc.ConnectionState.NOT_CONNECTED;
   },
 
   /**
-   * Callback when the Connect button is clicked.
+   * Callback when the Connect button is tapped.
    * @private
    */
-  onConnectClicked_: function() {
+  onConnectTap_: function() {
     chrome.networkingPrivate.startConnect(this.guid);
   },
 
   /**
-   * Callback when the Disconnect button is clicked.
+   * Callback when the Disconnect button is tapped.
    * @private
    */
-  onDisconnectClicked_: function() {
+  onDisconnectTap_: function() {
     chrome.networkingPrivate.startDisconnect(this.guid);
   },
 
   /**
-   * Callback when the Activate button is clicked.
+   * Callback when the Activate button is tapped.
    * @private
    */
-  onActivateClicked_: function() {
+  onActivateTap_: function() {
     chrome.networkingPrivate.startActivate(this.guid);
   },
 
   /**
-   * Callback when the View Account button is clicked.
+   * Callback when the View Account button is tapped.
    * @private
    */
-  onViewAccountClicked_: function() {
+  onViewAccountTap_: function() {
     // startActivate() will show the account page for activated networks.
     chrome.networkingPrivate.startActivate(this.guid);
   },
@@ -487,8 +486,7 @@
    * @private
    */
   showShared_: function(properties) {
-    return !!properties && (properties.Source == 'Device' ||
-                            properties.Source == 'DevicePolicy');
+    return properties.Source == 'Device' || properties.Source == 'DevicePolicy';
   },
 
   /**
@@ -497,7 +495,7 @@
    * @private
    */
   showAutoConnect_: function(properties) {
-    return !!properties && properties.Type != CrOnc.Type.ETHERNET &&
+    return properties.Type != CrOnc.Type.ETHERNET &&
            properties.Source != CrOnc.Source.NONE;
   },
 
@@ -509,7 +507,7 @@
   showPreferNetwork_: function(properties) {
     // TODO(stevenjb): Resolve whether or not we want to allow "preferred" for
     // properties.Type == CrOnc.Type.ETHERNET.
-    return !!properties && properties.Source != CrOnc.Source.NONE;
+    return properties.Source != CrOnc.Source.NONE;
   },
 
   /**
@@ -637,7 +635,7 @@
    * @private
    */
   hasNetworkSection_: function(properties) {
-    return !!properties && properties.Type != CrOnc.Type.VPN;
+    return properties.Type != CrOnc.Type.VPN;
   },
 
   /**
@@ -647,7 +645,7 @@
    * @private
    */
   isType_: function(properties, type) {
-    return !!properties && properties.Type == type;
+    return properties.Type == type;
   },
 
   /**
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
index ed58dca..b6ace42 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://resources/cr_elements/v1_0/network/cr_network_list.html">
 
-<dom-module id="cr-settings-internet-known-networks-page">
+<dom-module id="settings-internet-known-networks-page">
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="internet_known_networks_page.css">
   <template>
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
index 46eddc5..508abed 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
@@ -4,14 +4,14 @@
 
 /**
  * @fileoverview
- * 'cr-settings-internet-known-networks' is the settings subpage listing the
+ * 'settings-internet-known-networks' is the settings subpage listing the
  * known networks for a type (currently always WiFi).
  *
  * @group Chrome Settings Elements
- * @element cr-settings-internet-known-networks
+ * @element settings-internet-known-networks
  */
 Polymer({
-  is: 'cr-settings-internet-known-networks-page',
+  is: 'settings-internet-known-networks-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.html b/chrome/browser/resources/settings/internet_page/internet_page.html
index 1a54d51..4c48d8a 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -7,7 +7,7 @@
 <link rel="import" href="internet_known_networks_page.html">
 <link rel="import" href="network_summary.html">
 
-<dom-module id="cr-settings-internet-page">
+<dom-module id="settings-internet-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <template>
@@ -21,18 +21,18 @@
       <neon-animatable id="network-detail">
         <settings-subheader i18n-values="page-title:internetDetailPageTitle">
         </settings-subheader>
-        <cr-settings-internet-detail-page guid="[[detailGuid]]"
+        <settings-internet-detail-page guid="[[detailGuid]]"
             on-close="onBackTap_">
-        </cr-settings-internet-detail-page>
+        </settings-internet-detail-page>
       </neon-animatable>
       <neon-animatable id="known-networks">
         <settings-subheader
             i18n-values="page-title:internetKnownNetworksPageTitle">
         </settings-subheader>
-        <cr-settings-internet-known-networks-page
+        <settings-internet-known-networks-page
             network-type="[[knownNetworksType]]"
             on-show-detail="onShowDetail_">
-        </cr-settings-internet-known-networks-page>
+        </settings-internet-known-networks-page>
       </neon-animatable>
     </settings-animated-pages>
   </template>
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index dd66f4be..1581df4 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-internet-page' is the settings page containing internet
+ * 'settings-internet-page' is the settings page containing internet
  * settings.
  *
  * Example:
  *
  *    <core-animated-pages>
- *      <cr-settings-internet-page prefs='{{prefs}}'>
- *      </cr-settings-internet-page>
+ *      <settings-internet-page prefs='{{prefs}}'>
+ *      </settings-internet-page>
  *      ... other pages ...
  *    </core-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-internet-page
+ * @element settings-internet-page
  */
 Polymer({
-  is: 'cr-settings-internet-page',
+  is: 'settings-internet-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/internet_page/network_apnlist.html b/chrome/browser/resources/settings/internet_page/network_apnlist.html
index 2d53bbe..48295be3 100644
--- a/chrome/browser/resources/settings/internet_page/network_apnlist.html
+++ b/chrome/browser/resources/settings/internet_page/network_apnlist.html
@@ -25,7 +25,7 @@
             edit-field-types="[[otherApnEditTypes_]]"
             on-property-change="onOtherApnChange_">
         </network-property-list>
-        <paper-button on-tap="onSaveOther_">Save</paper-button>
+        <paper-button on-tap="onSaveOtherTap_">Save</paper-button>
       </div>
     </div>
   </template>
diff --git a/chrome/browser/resources/settings/internet_page/network_apnlist.js b/chrome/browser/resources/settings/internet_page/network_apnlist.js
index e95ff7df..67bf2e2 100644
--- a/chrome/browser/resources/settings/internet_page/network_apnlist.js
+++ b/chrome/browser/resources/settings/internet_page/network_apnlist.js
@@ -206,15 +206,15 @@
    */
   onOtherApnChange_: function(event) {
     this.set('otherApn.' + event.detail.field, event.detail.value);
-    // Don't send a change event for 'Other' until the 'Save' button is clicked.
+    // Don't send a change event for 'Other' until the 'Save' button is tapped.
   },
 
   /**
-   * Event triggered when the Other APN 'Save' button is clicked.
+   * Event triggered when the Other APN 'Save' button is tapped.
    * @param {Event} event
    * @private
    */
-  onSaveOther_: function(event) {
+  onSaveOtherTap_: function(event) {
     this.sendApnChange_(this.selectedApn);
   },
 
diff --git a/chrome/browser/resources/settings/internet_page/network_property_list.css b/chrome/browser/resources/settings/internet_page/network_property_list.css
index 4bd06dc..edad310 100644
--- a/chrome/browser/resources/settings/internet_page/network_property_list.css
+++ b/chrome/browser/resources/settings/internet_page/network_property_list.css
@@ -3,8 +3,6 @@
  * found in the LICENSE file. */
 
 span {
-  @apply(--paper-font-subhead);
-  @apply(--paper-input-container-input);
   margin: 0 5px 5px 0;
 }
 
diff --git a/chrome/browser/resources/settings/internet_page/network_property_list.html b/chrome/browser/resources/settings/internet_page/network_property_list.html
index 79db6d9..bc89ad75 100644
--- a/chrome/browser/resources/settings/internet_page/network_property_list.html
+++ b/chrome/browser/resources/settings/internet_page/network_property_list.html
@@ -1,7 +1,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://resources/cr_elements/v1_0/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/paper-styles.html">
 
 <dom-module name="network-property-list">
   <link rel="import" type="css" href="network_property_list.css">
diff --git a/chrome/browser/resources/settings/internet_page/network_property_list.js b/chrome/browser/resources/settings/internet_page/network_property_list.js
index 87b64216..51faf09 100644
--- a/chrome/browser/resources/settings/internet_page/network_property_list.js
+++ b/chrome/browser/resources/settings/internet_page/network_property_list.js
@@ -17,6 +17,7 @@
   properties: {
     /**
      * The dictionary containing the properties to display.
+     * @type {!Object|undefined}
      */
     propertyDict: {
       type: Object
@@ -80,18 +81,18 @@
   },
 
   /**
-   * @param {!Object|undefined} propertyDict
+   * @param {!Object} propertyDict
    * @param {string} key The property key.
    * @return {boolean} Whether or not the property exists in |propertyDict|.
    * @private
    */
   hasPropertyValue_: function(propertyDict, key) {
-    var value = (propertyDict && this.get(key, propertyDict)) || undefined;
-    return (value !== undefined && value !== '');
+    var value = this.get(key, propertyDict);
+    return value !== undefined && value !== '';
   },
 
   /**
-   * @param {!Object|undefined} propertyDict
+   * @param {!Object} propertyDict
    * @param {!Object} editFieldTypes The editFieldTypes object.
    * @param {string} key The property key.
    * @return {boolean} Whether or not to show the property. Editable properties
@@ -105,7 +106,7 @@
   },
 
   /**
-   * @param {!Object|undefined} propertyDict
+   * @param {!Object} propertyDict
    * @param {!Object} editFieldTypes The editFieldTypes object.
    * @param {string} key The property key.
    * @return {boolean} True if |key| exists in |propertiesDict| and is not
@@ -120,7 +121,7 @@
   },
 
   /**
-   * @param {!Object|undefined} propertyDict
+   * @param {!Object} propertyDict
    * @param {!Object} editFieldTypes The editFieldTypes object.
    * @param {string} key The property key.
    * @param {string} type The field type.
@@ -133,14 +134,12 @@
   },
 
   /**
-   * @param {!Object|undefined} propertyDict
+   * @param {!Object} propertyDict
    * @param {string} key The property key.
    * @return {string} The text to display for the property value.
    * @private
    */
   getPropertyValue_: function(propertyDict, key) {
-    if (!propertyDict)
-      return '';
     var value = this.get(key, propertyDict);
     if (value === undefined)
       return '';
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy.css b/chrome/browser/resources/settings/internet_page/network_proxy.css
index 9098be4..0b3794a 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy.css
+++ b/chrome/browser/resources/settings/internet_page/network_proxy.css
@@ -14,8 +14,6 @@
 }
 
 span {
-  @apply(--paper-font-subhead);
-  @apply(--paper-input-container-input);
   margin: 5px;
 }
 
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy.html b/chrome/browser/resources/settings/internet_page/network_proxy.html
index 61ae5fd..826a535 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy.html
+++ b/chrome/browser/resources/settings/internet_page/network_proxy.html
@@ -2,7 +2,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/paper-styles.html">
 <link rel="import" href="chrome://resources/cr_elements/v1_0/network/cr_onc_types.html">
 <link rel="import" href="network_property_list.html">
 <link rel="import" href="network_proxy_input.html">
@@ -71,7 +70,7 @@
         <div class="layout horizontal baseline">
           <paper-input id="proxyExclusion" class="flex" no-label-float>
           </paper-input>
-          <paper-button on-tap="onAddProxyExclusion_">
+          <paper-button on-tap="onAddProxyExclusionTap_">
             Add Exception
           </paper-button>
         </div>
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy.js b/chrome/browser/resources/settings/internet_page/network_proxy.js
index c7c02e6..940b9ba 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy.js
+++ b/chrome/browser/resources/settings/internet_page/network_proxy.js
@@ -220,7 +220,7 @@
    * @param {Event} event The add proxy exclusion event.
    * @private
    */
-  onAddProxyExclusion_: function(event) {
+  onAddProxyExclusionTap_: function(event) {
     var value = this.$.proxyExclusion.value;
     if (!value)
       return;
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.html b/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.html
index 07b9273..35dad0e 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.html
+++ b/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/paper-styles.html">
 
 <dom-module id="network-proxy-exclusions">
   <link rel="import" type="css" href="network_proxy_exclusions.css">
@@ -9,7 +8,7 @@
       <template is="dom-repeat" items="[[exclusions]]">
         <div class="layout horizontal">
           <span class="flex">[[item]]</span>
-          <iron-icon icon="clear" on-click="removeItem_"></iron-icon>
+          <iron-icon icon="clear" on-tap="onRemoveTap_"></iron-icon>
         </div>
       </template>
     </div>
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.js b/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.js
index b765e1d..2d408b7 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.js
+++ b/chrome/browser/resources/settings/internet_page/network_proxy_exclusions.js
@@ -29,7 +29,7 @@
    * @param {!{model: !{index: number}}} event
    * @private
    */
-  removeItem_: function(event) {
+  onRemoveTap_: function(event) {
     var index = event.model.index;
     this.splice('exclusions', index, 1);
     this.fire('proxy-change');
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy_input.css b/chrome/browser/resources/settings/internet_page/network_proxy_input.css
index 16302de..518cedf 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy_input.css
+++ b/chrome/browser/resources/settings/internet_page/network_proxy_input.css
@@ -7,8 +7,6 @@
 }
 
 span {
-  @apply(--paper-font-subhead);
-  @apply(--paper-input-container-input);
   margin-right: 5px;
 }
 
diff --git a/chrome/browser/resources/settings/internet_page/network_proxy_input.html b/chrome/browser/resources/settings/internet_page/network_proxy_input.html
index 6eeef81a..21f196c 100644
--- a/chrome/browser/resources/settings/internet_page/network_proxy_input.html
+++ b/chrome/browser/resources/settings/internet_page/network_proxy_input.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/paper-styles.html">
 
 <dom-module id="network-proxy-input">
   <link rel="import" type="css" href="network_proxy_input.css">
diff --git a/chrome/browser/resources/settings/internet_page/network_siminfo.html b/chrome/browser/resources/settings/internet_page/network_siminfo.html
index 03450ad..300a61f2 100644
--- a/chrome/browser/resources/settings/internet_page/network_siminfo.html
+++ b/chrome/browser/resources/settings/internet_page/network_siminfo.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/notification-icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html">
@@ -12,19 +13,19 @@
   <template>
     <div id="outerDiv" class="layout vertical">
       <div class="layout horizontal center"
-          hidden$="[[state.Cellular.SIMPresent]]">
+          hidden$="[[networkProperties.Cellular.SIMPresent]]">
         <!-- SIM missing UI -->
-        <iron-icon icon="sim-card-alert"></iron-icon>
+        <iron-icon icon="notification:sim-card-alert"></iron-icon>
         <span>Missing SIM card</span>
       </div>
       <div class="layout vertical"
-          hidden$="[[!state.Cellular.SIMPresent]]">
+          hidden$="[[!networkProperties.Cellular.SIMPresent]]">
         <div id="lockedDiv" class="layout horizontal center"
             hidden$="[[!isSimLocked_(networkProperties)]]">
           <!-- SIM locked -->
           <iron-icon icon="lock"></iron-icon>
           <span>SIM card is locked.</span>
-          <paper-button on-tap="unlockPin_">Unlock</paper-button>
+          <paper-button on-tap="onUnlockPinTap_">Unlock</paper-button>
         </div>
         <div class="layout vertical"
             hidden$="[[isSimLocked_(networkProperties)]]">
@@ -37,7 +38,7 @@
           <div class="layout horizontal center"
                hidden$="[[!networkProperties.Cellular.SIMLockStatus.LockEnabled]]">
             <!-- SIM lock enabled -->
-            <paper-button on-tap="onChangePin_">Change PIN</paper-button>
+            <paper-button on-tap="onChangePinTap_">Change PIN</paper-button>
           </div>
         </div>
       </div>
diff --git a/chrome/browser/resources/settings/internet_page/network_siminfo.js b/chrome/browser/resources/settings/internet_page/network_siminfo.js
index d93268c..06b463a4 100644
--- a/chrome/browser/resources/settings/internet_page/network_siminfo.js
+++ b/chrome/browser/resources/settings/internet_page/network_siminfo.js
@@ -150,7 +150,7 @@
    * @param {Event} event
    * @private
    */
-  onChangePin_: function(event) {
+  onChangePinTap_: function(event) {
     if (!this.networkProperties || !this.networkProperties.Cellular)
       return;
     this.error = ErrorType.NONE;
@@ -203,7 +203,7 @@
    * @param {Event} event
    * @private
    */
-  unlockPin_: function(event) {
+  onUnlockPinTap_: function(event) {
     this.error = ErrorType.NONE;
     this.$.unlockPinDialog.open();
   },
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chrome/browser/resources/settings/internet_page/network_summary_item.html
index 9ab31df..73dede1 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.html
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -11,7 +11,7 @@
   <template>
     <div class="layout vertical" hidden$="[[isHidden]]">
       <div id="details" class="layout horizontal center"
-          on-click="onDetailsClicked_">
+          on-tap="onDetailsTap_">
         <cr-network-list-item id="detailsItem"
             network-state="[[networkState]]">
         </cr-network-list-item>
@@ -26,7 +26,7 @@
           <paper-toggle-button id="deviceEnabledButton"
               checked="[[deviceIsEnabled_(deviceState)]]"
               class$="[[getDeviceEnabledButtonClass_(deviceState)]]"
-              on-tap="onDeviceEnabledToggled_">
+              on-tap="onDeviceEnabledTap_">
           </paper-toggle-button>
         </div>
       </div>
@@ -40,7 +40,7 @@
         <div class="layout horizontal">
           <paper-button
               hidden$="[[!showKnownNetworks_(networkState, expanded)]]"
-              on-tap="onKnownNetworksClicked_">
+              on-tap="onKnownNetworksTap_">
             Known networks
           </paper-button>
         </div>
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 67e7a1b0..ccb10ea 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -168,11 +168,11 @@
   },
 
   /**
-   * Event triggered when the details div is clicked.
+   * Event triggered when the details div is tapped.
    * @param {Event} event The enable button event.
    * @private
    */
-  onDetailsClicked_: function(event) {
+  onDetailsTap_: function(event) {
     if ((event.target.id == 'expandListButton') ||
         (this.deviceState && !this.deviceIsEnabled_(this.deviceState))) {
       // Already handled or disabled, do nothing.
@@ -188,10 +188,10 @@
   },
 
   /**
-   * Event triggered when the known networks button is clicked.
+   * Event triggered when the known networks button is tapped.
    * @private
    */
-  onKnownNetworksClicked_: function() {
+  onKnownNetworksTap_: function() {
     this.fire('show-known-networks', {type: CrOnc.Type.WI_FI});
   },
 
@@ -210,12 +210,12 @@
    * @param {!Object} event The enable button event.
    * @private
    */
-  onDeviceEnabledToggled_: function(event) {
+  onDeviceEnabledTap_: function(event) {
     var deviceIsEnabled = this.deviceIsEnabled_(this.deviceState);
     var type = this.deviceState ? this.deviceState.Type : '';
     this.fire('device-enabled-toggled',
               {enabled: !deviceIsEnabled, type: type});
-    // Make sure this does not propagate to onDetailsClicked_.
+    // Make sure this does not propagate to onDetailsTap_.
     event.stopPropagation();
   },
 
diff --git a/chrome/browser/resources/settings/languages_page/language_detail_page.html b/chrome/browser/resources/settings/languages_page/language_detail_page.html
index 51b1767..e3ea064 100644
--- a/chrome/browser/resources/settings/languages_page/language_detail_page.html
+++ b/chrome/browser/resources/settings/languages_page/language_detail_page.html
@@ -14,8 +14,8 @@
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="language_detail_page.css">
   <template>
-    <cr-settings-languages id="languages" languages="{{languages}}">
-    </cr-settings-languages>
+    <settings-languages id="languages" languages="{{languages}}">
+    </settings-languages>
 <if expr="chromeos or is_win">
     <div id="languageSettings">
       <label hidden$="[[!detail.language.supportsUI]]">
diff --git a/chrome/browser/resources/settings/languages_page/language_detail_page.js b/chrome/browser/resources/settings/languages_page/language_detail_page.js
index aadcc60..b30d2a9 100644
--- a/chrome/browser/resources/settings/languages_page/language_detail_page.js
+++ b/chrome/browser/resources/settings/languages_page/language_detail_page.js
@@ -23,7 +23,7 @@
 
     /**
      * Read-only reference to the languages model provided by the
-     * 'cr-settings-languages' instance.
+     * 'settings-languages' instance.
      * @type {LanguagesModel|undefined}
      */
     languages: Object,
diff --git a/chrome/browser/resources/settings/languages_page/languages.html b/chrome/browser/resources/settings/languages_page/languages.html
index d426d5e..d44724c 100644
--- a/chrome/browser/resources/settings/languages_page/languages.html
+++ b/chrome/browser/resources/settings/languages_page/languages.html
@@ -3,9 +3,9 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://md-settings/prefs/prefs.html">
 
-<dom-module id="cr-settings-languages-singleton">
+<dom-module id="settings-languages-singleton">
   <template>
-    <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs>
+    <settings-prefs prefs="{{prefs}}"></settings-prefs>
   </template>
   <script src="languages.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/languages_page/languages.js b/chrome/browser/resources/settings/languages_page/languages.js
index c65c0e74..3314136 100644
--- a/chrome/browser/resources/settings/languages_page/languages.js
+++ b/chrome/browser/resources/settings/languages_page/languages.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview 'cr-settings-languages' provides convenient access to
+ * @fileoverview 'settings-languages' provides convenient access to
  * Chrome's language and input method settings.
  *
  * Instances of this element have a 'languages' property, which reflects the
@@ -15,13 +15,13 @@
  * changes made internally to 'languages' propagate to your host element:
  *
  *     <template>
- *       <cr-settings-languages languages="{{languages}}">
- *       </cr-settings-languages>
+ *       <settings-languages languages="{{languages}}">
+ *       </settings-languages>
  *       <div>[[languages.someProperty]]</div>
  *     </template>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-languages
+ * @element settings-languages
  */
 
 /** @typedef {{spellCheckEnabled: boolean, translateEnabled: boolean}} */
@@ -71,7 +71,7 @@
  * language model to the host of this element as the 'languages' property.
  */
 Polymer({
-  is: 'cr-settings-languages',
+  is: 'settings-languages',
 
   properties: {
     /**
@@ -80,7 +80,7 @@
      */
     singleton_: {
       type: Object,
-      value: document.createElement('cr-settings-languages-singleton'),
+      value: document.createElement('settings-languages-singleton'),
     },
 
     /**
@@ -183,14 +183,14 @@
     'settings.language.preferred_languages' : 'intl.accept_languages';
 
 /**
- * Singleton element created when cr-settings-languages is registered.
+ * Singleton element created when settings-languages is registered.
  * Generates the languages model on start-up, and updates it whenever Chrome's
  * pref store and other settings change. These updates propagate to each
- * <cr-settings-language> instance so that their 'languages' property updates
+ * <settings-language> instance so that their 'languages' property updates
  * like any other Polymer property.
  */
 Polymer({
-  is: 'cr-settings-languages-singleton',
+  is: 'settings-languages-singleton',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index bc456e45..1c80e96 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -14,13 +14,13 @@
 <link rel="import" href="languages.html">
 <link rel="import" href="manage_languages_page.html">
 
-<dom-module id="cr-settings-languages-page">
+<dom-module id="settings-languages-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="languages_page.css">
   <template>
-    <cr-settings-languages id="languages" languages="{{languages}}">
-    </cr-settings-languages>
+    <settings-languages id="languages" languages="{{languages}}">
+    </settings-languages>
     <settings-animated-pages id="pages" current-route="{{currentRoute}}"
         section="languages">
       <neon-animatable id="main">
@@ -80,8 +80,8 @@
       <neon-animatable id="manage-languages">
         <settings-subheader i18n-values="page-title:manageLanguagesPageTitle">
         </settings-subheader>
-        <cr-settings-manage-languages-page id="manageLanguagesPage"
-            prefs="{{prefs}}"></cr-settings-manage-languages-page>
+        <settings-manage-languages-page id="manageLanguagesPage"
+            prefs="{{prefs}}"></settings-manage-languages-page>
       </neon-animatable>
       <neon-animatable id="language-detail">
         <settings-subheader id="language-detail-subheader"
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.js b/chrome/browser/resources/settings/languages_page/languages_page.js
index 427c9153..9186884 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -3,17 +3,17 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview 'cr-settings-languages-page' is the settings page
+ * @fileoverview 'settings-languages-page' is the settings page
  * for language and input method settings.
  *
  * @group Chrome Settings Elements
- * @element cr-settings-languages-page
+ * @element settings-languages-page
  */
 (function() {
 'use strict';
 
 Polymer({
-  is: 'cr-settings-languages-page',
+  is: 'settings-languages-page',
 
   properties: {
     /**
@@ -34,7 +34,7 @@
 
     /**
      * Read-only reference to the languages model provided by the
-     * 'cr-settings-languages' instance.
+     * 'settings-languages' instance.
      * @type {LanguagesModel|undefined}
      */
     languages: {
diff --git a/chrome/browser/resources/settings/languages_page/manage_languages_page.html b/chrome/browser/resources/settings/languages_page/manage_languages_page.html
index 8fa80eb..04a60a6 100644
--- a/chrome/browser/resources/settings/languages_page/manage_languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/manage_languages_page.html
@@ -9,13 +9,13 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="languages.html">
 
-<dom-module id="cr-settings-manage-languages-page">
+<dom-module id="settings-manage-languages-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="manage_languages_page.css">
   <template>
-    <cr-settings-languages id="languages" languages="{{languages}}">
-    </cr-settings-languages>
+    <settings-languages id="languages" languages="{{languages}}">
+    </settings-languages>
     <div class="content">
       <h2 i18n-content="enabledLanguages"></h2>
       <div class="item-list">
diff --git a/chrome/browser/resources/settings/languages_page/manage_languages_page.js b/chrome/browser/resources/settings/languages_page/manage_languages_page.js
index 1fe8ba4..9dda84c1 100644
--- a/chrome/browser/resources/settings/languages_page/manage_languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/manage_languages_page.js
@@ -3,14 +3,14 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview 'cr-settings-manage-languages-page' is a sub-page for enabling
+ * @fileoverview 'settings-manage-languages-page' is a sub-page for enabling
  * and disabling languages.
  *
  * @group Chrome Settings Elements
- * @element cr-settings-manage-languages-page
+ * @element settings-manage-languages-page
  */
 Polymer({
-  is: 'cr-settings-manage-languages-page',
+  is: 'settings-manage-languages-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/location_page/location_page.html b/chrome/browser/resources/settings/location_page/location_page.html
index 4c4e0ca..6adde9e7 100644
--- a/chrome/browser/resources/settings/location_page/location_page.html
+++ b/chrome/browser/resources/settings/location_page/location_page.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-submenu.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
 
-<dom-module id="cr-settings-location-page">
+<dom-module id="settings-location-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="location_page.css">
diff --git a/chrome/browser/resources/settings/location_page/location_page.js b/chrome/browser/resources/settings/location_page/location_page.js
index 1d8201b..368bfa3e 100644
--- a/chrome/browser/resources/settings/location_page/location_page.js
+++ b/chrome/browser/resources/settings/location_page/location_page.js
@@ -4,19 +4,19 @@
 
 /**
  * @fileoverview
- * 'cr-settings-location-page' is the settings page for location access.
+ * 'settings-location-page' is the settings page for location access.
  *
  * Example:
  *
- *      <cr-settings-location-page prefs="{{prefs}}">
- *      </cr-settings-location-page>
+ *      <settings-location-page prefs="{{prefs}}">
+ *      </settings-location-page>
  *      ... other pages ...
  *
  * @group Chrome Settings Elements
- * @element cr-settings-location-page
+ * @element settings-location-page
  */
 Polymer({
-  is: 'cr-settings-location-page',
+  is: 'settings-location-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
index c99975a..7df7904 100644
--- a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
+++ b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html">
 
-<dom-module id="cr-settings-on-startup-page">
+<dom-module id="settings-on-startup-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="on_startup_shared.css">
@@ -36,8 +36,8 @@
       <neon-animatable id="startup-urls">
         <settings-subheader i18n-values="page-title:onStartupSetPages">
         </settings-subheader>
-        <cr-settings-startup-urls-page prefs="{{prefs}}">
-        </cr-settings-startup-urls-page>
+        <settings-startup-urls-page prefs="{{prefs}}">
+        </settings-startup-urls-page>
       </neon-animatable>
     </settings-animated-pages>
   </template>
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_page.js b/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
index 95e516d..d1645b93 100644
--- a/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
+++ b/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
@@ -4,21 +4,21 @@
 
 /**
  * @fileoverview
- * 'cr-settings-on-startup-page' is a settings page.
+ * 'settings-on-startup-page' is a settings page.
  *
  * Example:
  *
  *    <neon-animated-pages>
- *      <cr-settings-on-startup-page prefs="{{prefs}}">
- *      </cr-settings-on-startup-page>
+ *      <settings-on-startup-page prefs="{{prefs}}">
+ *      </settings-on-startup-page>
  *      ... other pages ...
  *    </neon-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-on-startup-page
+ * @element settings-on-startup-page
  */
 Polymer({
-  is: 'cr-settings-on-startup-page',
+  is: 'settings-on-startup-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
index dca01c7e..e34f683 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -2,7 +2,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-icon-item.html">
 
-<dom-module id="cr-settings-startup-urls-page">
+<dom-module id="settings-startup-urls-page">
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="on_startup_shared.css">
   <template>
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.js b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.js
index fd5e903..a8d1873 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.js
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.js
@@ -3,22 +3,22 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview 'cr-settings-startup-urls-page' is the settings page
+ * @fileoverview 'settings-startup-urls-page' is the settings page
  * containing the urls that will be opened when chrome is started.
  *
  * Example:
  *
  *    <neon-animated-pages>
- *      <cr-settings-startup-urls-page prefs="{{prefs}}">
- *      </cr-settings-startup-urls-page>
+ *      <settings-startup-urls-page prefs="{{prefs}}">
+ *      </settings-startup-urls-page>
  *      ... other pages ...
  *    </neon-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-startup-urls-page
+ * @element settings-startup-urls-page
  */
 Polymer({
-  is: 'cr-settings-startup-urls-page',
+  is: 'settings-startup-urls-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/pref_tracker/pref_tracker.html b/chrome/browser/resources/settings/pref_tracker/pref_tracker.html
index a1f68d2..978dafe 100644
--- a/chrome/browser/resources/settings/pref_tracker/pref_tracker.html
+++ b/chrome/browser/resources/settings/pref_tracker/pref_tracker.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://md-settings/prefs/prefs_types.html">
 
-<dom-module id="cr-settings-pref-tracker">
+<dom-module id="settings-pref-tracker">
   <script src="pref_tracker.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/pref_tracker/pref_tracker.js b/chrome/browser/resources/settings/pref_tracker/pref_tracker.js
index b7124759..34d9473 100644
--- a/chrome/browser/resources/settings/pref_tracker/pref_tracker.js
+++ b/chrome/browser/resources/settings/pref_tracker/pref_tracker.js
@@ -4,21 +4,21 @@
 
 /**
  * @fileoverview
- * `cr-settings-pref-tracker` is a utility element used to track the
+ * `settings-pref-tracker` is a utility element used to track the
  * initialization of a specified preference and throw an error if the pref
  * is not defined after prefs have all been fetched.
  *
  * Example:
  *
- *    <cr-settings-pref-tracker pref="{{prefs.settings.foo.bar}}">
- *    </cr-settings-pref-tracker>
+ *    <settings-pref-tracker pref="{{prefs.settings.foo.bar}}">
+ *    </settings-pref-tracker>
  *
- * @element cr-settings-pref-tracker
+ * @element settings-pref-tracker
  */
 (function() {
 
   Polymer({
-    is: 'cr-settings-pref-tracker',
+    is: 'settings-pref-tracker',
 
     properties: {
       /**
diff --git a/chrome/browser/resources/settings/prefs/prefs.js b/chrome/browser/resources/settings/prefs/prefs.js
index ffb64dc9..d46ff88 100644
--- a/chrome/browser/resources/settings/prefs/prefs.js
+++ b/chrome/browser/resources/settings/prefs/prefs.js
@@ -4,7 +4,7 @@
 
 /**
  * @fileoverview
- * 'cr-settings-prefs' exposes a singleton model of Chrome settings and
+ * 'settings-prefs' exposes a singleton model of Chrome settings and
  * preferences, which listens to changes to Chrome prefs whitelisted in
  * chrome.settingsPrivate. When changing prefs in this element's 'prefs'
  * property via the UI, the singleton model tries to set those preferences in
@@ -13,12 +13,12 @@
  *
  * Example:
  *
- *    <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs>
+ *    <settings-prefs prefs="{{prefs}}"></settings-prefs>
  *    <settings-checkbox pref="{{prefs.homepage_is_newtabpage}}">
  *    </settings-checkbox>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-prefs
+ * @element settings-prefs
  */
 
 (function() {
@@ -125,7 +125,7 @@
   }
 
   Polymer({
-    is: 'cr-settings-prefs',
+    is: 'settings-prefs',
 
     properties: {
       /**
@@ -142,7 +142,7 @@
        */
       singleton_: {
         type: Object,
-        value: document.createElement('cr-settings-prefs-singleton'),
+        value: document.createElement('settings-prefs-singleton'),
       },
     },
 
@@ -157,7 +157,7 @@
     },
 
     /**
-     * Binds this.prefs to the cr-settings-prefs-singleton's shared prefs once
+     * Binds this.prefs to the settings-prefs-singleton's shared prefs once
      * preferences are initialized.
      * @private
      */
@@ -174,7 +174,7 @@
     },
 
     /**
-     * Stops listening for changes to cr-settings-prefs-singleton's shared
+     * Stops listening for changes to settings-prefs-singleton's shared
      * prefs.
      * @private
      */
@@ -196,7 +196,7 @@
     },
 
     /**
-     * Forwards changes to this.prefs to cr-settings-prefs-singleton.
+     * Forwards changes to this.prefs to settings-prefs-singleton.
      * @private
      */
     prefsChanged_: function(info) {
@@ -224,7 +224,7 @@
 
     /**
      * Uninitializes this element to remove it from tests. Also resets
-     * cr-settings-prefs-singleton, allowing newly created elements to
+     * settings-prefs-singleton, allowing newly created elements to
      * re-initialize it.
      */
     resetForTesting: function() {
@@ -238,7 +238,7 @@
    * prefs state.
    */
   Polymer({
-    is: 'cr-settings-prefs-singleton',
+    is: 'settings-prefs-singleton',
 
     properties: {
       /**
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 66f1edcc..b1b149b 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -10,7 +10,7 @@
 <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html">
 <link rel="import" href="chrome://md-settings/site_settings_page/site_settings_page.html">
 
-<dom-module id="cr-settings-privacy-page">
+<dom-module id="settings-privacy-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="privacy_page.css">
@@ -84,19 +84,19 @@
       <neon-animatable id="manage-certificates">
         <settings-subheader i18n-values="page-title:manageCertificates">
         </settings-subheader>
-        <cr-settings-certificate-manager-page>
-        </cr-settings-certificate-manager-page>
+        <settings-certificate-manager-page>
+        </settings-certificate-manager-page>
       </neon-animatable>
       <neon-animatable id="site-settings">
         <settings-subheader i18n-values="page-title:siteSettings">
         </settings-subheader>
-        <cr-settings-site-settings-page></cr-settings-site-settings-page>
+        <settings-site-settings-page></settings-site-settings-page>
       </neon-animatable>
       <neon-animatable id="clear-browsing-data">
         <settings-subheader i18n-values="page-title:clearBrowsingData">
         </settings-subheader>
-        <cr-settings-clear-browsing-data-page prefs="{{prefs}}">
-        </cr-settings-clear-browsing-data-page>
+        <settings-clear-browsing-data-page prefs="{{prefs}}">
+        </settings-clear-browsing-data-page>
       </neon-animatable>
     </settings-animated-pages>
   </template>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js
index a608276..5669824 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-privacy-page' is the settings page containing privacy and
+ * 'settings-privacy-page' is the settings page containing privacy and
  * security settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-privacy-page prefs="{{prefs}}">
- *      </cr-settings-privacy-page>
+ *      <settings-privacy-page prefs="{{prefs}}">
+ *      </settings-privacy-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-privacy-page
+ * @element settings-privacy-page
  */
 Polymer({
-  is: 'cr-settings-privacy-page',
+  is: 'settings-privacy-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_adder.js b/chrome/browser/resources/settings/search_engines_page/search_engine_adder.js
index 080a2de..927912e5 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_adder.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_adder.js
@@ -7,7 +7,7 @@
  * engine.
  *
  * @group Chrome Settings Elements
- * @element cr-settings-search-engine-adder
+ * @element settings-search-engine-adder
  */
 Polymer({
   is: 'cr-search-engine-adder',
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
index 58f097d..7929519 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -2,7 +2,7 @@
 <link rel="import" href="search_engine_adder.html">
 <link rel="import" href="search_engines_list.html">
 
-<dom-module id="cr-settings-search-engines-page">
+<dom-module id="settings-search-engines-page">
   <link rel="import" type="css" href="chrome://md-settings/settings_page/settings_page.css">
   <template>
     <div class="content">
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
index abd6bac..9aeb8599 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
@@ -3,22 +3,22 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview 'cr-settings-search-engines-page' is the settings page
+ * @fileoverview 'settings-search-engines-page' is the settings page
  * containing search engines settings.
  *
  * Example:
  *
  *    <core-animated-pages>
- *      <cr-settings-search-engines-page prefs="{{prefs}}">
- *      </cr-settings-search-engines-page>
+ *      <settings-search-engines-page prefs="{{prefs}}">
+ *      </settings-search-engines-page>
  *      ... other pages ...
  *    </core-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-search-engines-page
+ * @element settings-search-engines-page
  */
 Polymer({
-  is: 'cr-settings-search-engines-page',
+  is: 'settings-search-engines-page',
 
   properties: {
     /** @type {!Array<!SearchEngine>} */
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index ef08987..1753bd8 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -6,7 +6,7 @@
 <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html">
 <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html">
 
-<dom-module id="cr-settings-search-page">
+<dom-module id="settings-search-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="search_page.css">
@@ -32,7 +32,7 @@
       <neon-animatable id="search-engines">
         <settings-subheader i18n-values="page-title:searchEnginesLabel">
         </settings-subheader>
-        <cr-settings-search-engines-page></cr-settings-search-engines-page>
+        <settings-search-engines-page></settings-search-engines-page>
         <paper-button class="search-engines-advanced"
             i18n-content="advancedPageTitle"
             on-tap="onSearchEnginesAdvancedTap_" raised>
diff --git a/chrome/browser/resources/settings/search_page/search_page.js b/chrome/browser/resources/settings/search_page/search_page.js
index 34983d0..7f069b60 100644
--- a/chrome/browser/resources/settings/search_page/search_page.js
+++ b/chrome/browser/resources/settings/search_page/search_page.js
@@ -4,20 +4,20 @@
 
 /**
  * @fileoverview
- * 'cr-settings-search-page' is the settings page containing search settings.
+ * 'settings-search-page' is the settings page containing search settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-search-page prefs="{{prefs}}"></cr-settings-search-page>
+ *      <settings-search-page prefs="{{prefs}}"></settings-search-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-search-page
+ * @element settings-search-page
  */
 Polymer({
-  is: 'cr-settings-search-page',
+  is: 'settings-search-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/settings.html b/chrome/browser/resources/settings/settings.html
index 231b3d9..ec9542c 100644
--- a/chrome/browser/resources/settings/settings.html
+++ b/chrome/browser/resources/settings/settings.html
@@ -15,8 +15,8 @@
 
   <dom-module id="cr-settings">
     <template>
-      <cr-settings-prefs id="prefs" prefs="{{prefs_}}"></cr-settings-prefs>
-      <cr-settings-ui prefs="{{prefs_}}"></cr-settings-ui>
+      <settings-prefs id="prefs" prefs="{{prefs_}}"></settings-prefs>
+      <settings-ui prefs="{{prefs_}}"></settings-ui>
     </template>
     <script src="settings.js"></script>
   </dom-module>
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index cc17ba24..1bbdc278 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -3,18 +3,18 @@
 <link rel="import" href="chrome://md-settings/advanced_page/advanced_page.html">
 <link rel="import" href="chrome://md-settings/basic_page/basic_page.html">
 
-<dom-module id="cr-settings-main">
+<dom-module id="settings-main">
   <link rel="import" type="css" href="settings_main.css">
   <template>
     <content select="paper-icon-button"></content>
     <iron-pages id="pageContainer" attr-for-selected="data-route-page"
         selected="[[getSelectedPage_(currentRoute)]]">
-      <cr-settings-basic-page data-route-page="basic" prefs="{{prefs}}"
+      <settings-basic-page data-route-page="basic" prefs="{{prefs}}"
           current-route="{{currentRoute}}">
-      </cr-settings-basic-page>
-      <cr-settings-advanced-page data-route-page="advanced" prefs="{{prefs}}"
+      </settings-basic-page>
+      <settings-advanced-page data-route-page="advanced" prefs="{{prefs}}"
           current-route="{{currentRoute}}">
-      </cr-settings-advanced-page>
+      </settings-advanced-page>
     </iron-pages>
   </template>
   <script src="settings_main.js"></script>
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.js b/chrome/browser/resources/settings/settings_main/settings_main.js
index 77bd76134..1033b51 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.js
+++ b/chrome/browser/resources/settings/settings_main/settings_main.js
@@ -4,20 +4,20 @@
 
 /**
  * @fileoverview
- * 'cr-settings-main' displays the selected settings page.
+ * 'settings-main' displays the selected settings page.
  *
  * Example:
  *
- *     <cr-settings-main pages="[[pages]]" selected-page-id="{{selectedId}}">
- *     </cr-settings-main>
+ *     <settings-main pages="[[pages]]" selected-page-id="{{selectedId}}">
+ *     </settings-main>
  *
- * See cr-settings-drawer for example of use in 'paper-drawer-panel'.
+ * See settings-drawer for example of use in 'paper-drawer-panel'.
  *
  * @group Chrome Settings Elements
- * @element cr-settings-main
+ * @element settings-main
  */
 Polymer({
-  is: 'cr-settings-main',
+  is: 'settings-main',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 6d0ebbaf..12628ddf 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -10,7 +10,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/device-icons.html">
 </if>
 
-<dom-module id="cr-settings-menu">
+<dom-module id="settings-menu">
   <link rel="import" type="css" href="settings_menu.css">
   <template>
     <paper-menu name="root-menu">
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.js b/chrome/browser/resources/settings/settings_menu/settings_menu.js
index 73a4f5b6..9c873a1 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.js
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.js
@@ -4,18 +4,18 @@
 
 /**
  * @fileoverview
- * 'cr-settings-menu' shows a menu with a hardcoded set of pages and subpages.
+ * 'settings-menu' shows a menu with a hardcoded set of pages and subpages.
  *
  * Example:
  *
- *     <cr-settings-menu selected-page-id="{{selectedPageId}}">
- *     </cr-settings-menu>
+ *     <settings-menu selected-page-id="{{selectedPageId}}">
+ *     </settings-menu>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-menu
+ * @element settings-menu
  */
 Polymer({
-  is: 'cr-settings-menu',
+  is: 'settings-menu',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 97449f5..f6d5deba 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -364,6 +364,14 @@
       <structure name="IDR_SETTINGS_SYNC_PAGE_CSS"
                  file="sync_page/sync_page.css"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_SYNC_PRIVATE_API_JS"
+                 file="sync_page/sync_private_api.js"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_SYNC_PRIVATE_API_HTML"
+                 file="sync_page/sync_private_api.html"
+                 type="chrome_html"
+                 flattenhtml="true"
+                 allowexternalscript="true" />
       <structure name="IDR_SETTINGS_SETTINGS_HTML"
                  file="settings.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.css b/chrome/browser/resources/settings/settings_ui/settings_ui.css
index b679e44a..7b94109f 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.css
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.css
@@ -27,6 +27,6 @@
   position: relative;
 }
 
-cr-settings-main paper-icon-button {
+settings-main paper-icon-button {
   z-index: 10;
 }
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 0bb5ed8..5a65e761b 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -10,7 +10,7 @@
 <link rel="import" href="chrome://md-settings/settings_page/settings_router.html">
 <link rel="import" href="chrome://md-settings/settings_ui/breadcrumb.html">
 
-<dom-module id="cr-settings-ui">
+<dom-module id="settings-ui">
   <link rel="import" type="css" href="settings_ui.css">
   <template>
     <settings-router current-route="{{currentRoute}}"
@@ -23,11 +23,11 @@
       </settings-breadcrumb>
     </paper-toolbar>
     <paper-drawer-panel drawer-width="256px">
-      <cr-settings-menu drawer class="flex" current-route="{{currentRoute}}">
-      </cr-settings-menu>
-      <cr-settings-main main prefs="{{prefs}}" current-route="{{currentRoute}}">
+      <settings-menu drawer class="flex" current-route="{{currentRoute}}">
+      </settings-menu>
+      <settings-main main prefs="{{prefs}}" current-route="{{currentRoute}}">
         <paper-icon-button icon="menu" paper-drawer-toggle></paper-icon-button>
-      </cr-settings-main>
+      </settings-main>
     </paper-drawer-panel>
   </template>
   <script src="settings_ui.js"></script>
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index bc21caa7..513aa52 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -4,17 +4,17 @@
 
 /**
  * @fileoverview
- * 'cr-settings-ui' implements the UI for the Settings page.
+ * 'settings-ui' implements the UI for the Settings page.
  *
  * Example:
  *
- *    <cr-settings-ui prefs="{{prefs}}"></cr-settings-ui>
+ *    <settings-ui prefs="{{prefs}}"></settings-ui>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-ui
+ * @element settings-ui
  */
 Polymer({
-  is: 'cr-settings-ui',
+  is: 'settings-ui',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/signin_page/signin_page.html b/chrome/browser/resources/settings/signin_page/signin_page.html
index 87fab1b..adba474 100644
--- a/chrome/browser/resources/settings/signin_page/signin_page.html
+++ b/chrome/browser/resources/settings/signin_page/signin_page.html
@@ -18,8 +18,8 @@
       <neon-animatable id="sync">
         <settings-subheader i18n-values="page-title:syncPageTitle">
         </settings-subheader>
-        <cr-settings-sync-page current-route="[[currentRoute]]">
-        </cr-settings-sync-page>
+        <settings-sync-page current-route="[[currentRoute]]">
+        </settings-sync-page>
       </neon-animatable>
     </settings-animated-pages>
   </template>
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index 5f19eb55..bd29e73 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/communication-icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/social-icons.html">
 
-<dom-module id="cr-settings-site-settings-page">
+<dom-module id="settings-site-settings-page">
   <link rel="import" type="css" href="site_settings_page.css">
   <template>
     <div class="settings-list">
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index 19a6177..665c7abc 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-site-settings-page' is the settings page containing privacy and
+ * 'settings-site-settings-page' is the settings page containing privacy and
  * security site settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-site-settings-page prefs="{{prefs}}">
- *      </cr-settings-site-settings-page>
+ *      <settings-site-settings-page prefs="{{prefs}}">
+ *      </settings-site-settings-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-site-settings-page
+ * @element settings-site-settings-page
  */
 Polymer({
-  is: 'cr-settings-site-settings-page',
+  is: 'settings-site-settings-page',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/sync_page/sync_page.html b/chrome/browser/resources/settings/sync_page/sync_page.html
index e565d6a..c8a856c 100644
--- a/chrome/browser/resources/settings/sync_page/sync_page.html
+++ b/chrome/browser/resources/settings/sync_page/sync_page.html
@@ -5,7 +5,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html">
 <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html">
 
-<dom-module id="cr-settings-sync-page">
+<dom-module id="settings-sync-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="sync_page.css">
diff --git a/chrome/browser/resources/settings/sync_page/sync_page.js b/chrome/browser/resources/settings/sync_page/sync_page.js
index 062f3d22..d63575d 100644
--- a/chrome/browser/resources/settings/sync_page/sync_page.js
+++ b/chrome/browser/resources/settings/sync_page/sync_page.js
@@ -4,18 +4,18 @@
 
 /**
  * @fileoverview
- * 'cr-settings-sync-page' is the settings page containing sync settings.
+ * 'settings-sync-page' is the settings page containing sync settings.
  *
  * Example:
  *
  *    <iron-animated-pages>
- *      <cr-settings-sync-page></cr-settings-sync-page>
+ *      <settings-sync-page></settings-sync-page>
  *      ... other pages ...
  *    </iron-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-sync-page
+ * @element settings-sync-page
  */
 Polymer({
-  is: 'cr-settings-sync-page',
+  is: 'settings-sync-page',
 });
diff --git a/chrome/browser/resources/settings/sync_page/sync_private_api.html b/chrome/browser/resources/settings/sync_page/sync_private_api.html
new file mode 100644
index 0000000..b2c4a00
--- /dev/null
+++ b/chrome/browser/resources/settings/sync_page/sync_private_api.html
@@ -0,0 +1 @@
+<script src="chrome://md-settings/sync_page/sync_private_api.js"></script>
diff --git a/chrome/browser/resources/settings/sync_page/sync_private_api.js b/chrome/browser/resources/settings/sync_page/sync_private_api.js
new file mode 100644
index 0000000..d89302b
--- /dev/null
+++ b/chrome/browser/resources/settings/sync_page/sync_private_api.js
@@ -0,0 +1,176 @@
+// 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.
+
+cr.define('settings', function() {
+  /**
+   * API which encapsulates messaging between JS and C++ for the sync page.
+   * @constructor
+   */
+  function SyncPrivateApi() {}
+
+  /**
+   * The state of sync. This is the data structure sent back and forth between
+   * C++ and JS. Its naming and structure is not optimal, but changing it would
+   * require changes to the C++ handler, which is already functional.
+   * @typedef {{
+   *   appsEnforced: boolean,
+   *   appsRegistered: boolean,
+   *   appsSynced: boolean,
+   *   autofillEnforced: boolean,
+   *   autofillRegistered: boolean,
+   *   autofillSynced: boolean,
+   *   bookmarksEnforced: boolean,
+   *   bookmarksRegistered: boolean,
+   *   bookmarksSynced: boolean,
+   *   encryptAllData: boolean,
+   *   encryptAllDataAllowed: boolean,
+   *   enterGooglePassphraseBody: (string|undefined),
+   *   enterPassphraseBody: (string|undefined),
+   *   extensionsEnforced: boolean,
+   *   extensionsRegistered: boolean,
+   *   extensionsSynced: boolean,
+   *   fullEncryptionBody: string,
+   *   isGooglePassphrase: (boolean|undefined),
+   *   passphrase: (string|undefined),
+   *   passphraseFailed: boolean,
+   *   passwordsEnforced: boolean,
+   *   passwordsRegistered: boolean,
+   *   passwordsSynced: boolean,
+   *   preferencesEnforced: boolean,
+   *   preferencesRegistered: boolean,
+   *   preferencesSynced: boolean,
+   *   showPassphrase: boolean,
+   *   syncAllDataTypes: boolean,
+   *   syncNothing: boolean,
+   *   tabsEnforced: boolean,
+   *   tabsRegistered: boolean,
+   *   tabsSynced: boolean,
+   *   themesEnforced: boolean,
+   *   themesRegistered: boolean,
+   *   themesSynced: boolean,
+   *   typedUrlsEnforced: boolean,
+   *   typedUrlsRegistered: boolean,
+   *   typedUrlsSynced: boolean,
+   *   usePassphrase: boolean,
+   *   wifiCredentialsEnforced: (boolean|undefined),
+   *   wifiCredentialsSynced: (boolean|undefined)
+   * }}
+   */
+  SyncPrivateApi.SyncPrefs;
+
+  /**
+   * @enum {string}
+   */
+  SyncPrivateApi.PageStatus = {
+    CONFIGURE: 'configure',
+    TIMED_OUT: 'timeout',
+    DONE: 'done',
+    PASSPHRASE_ERROR: 'passphraseError',
+  };
+
+  /** @private {?function(SyncPrivateApi.SyncPrefs)} */
+  SyncPrivateApi.syncPrefsCallback_ = null;
+
+  /** @private {?function(SyncPrivateApi.PageStatus)} */
+  SyncPrivateApi.setPageStatusCallback_ = null;
+
+  /**
+   * Function to invoke when the sync page has been navigated to. This registers
+   * the UI as the "active" sync UI so that if the user tries to open another
+   * sync UI, this one will be shown instead.
+   */
+  SyncPrivateApi.didNavigateToSyncPage = function() {
+    chrome.send('SyncSetupShowSetupUI');
+  };
+
+  /**
+   * Function to invoke when leaving the sync page so that the C++ layer can be
+   * notified that the sync UI is no longer open.
+   */
+  SyncPrivateApi.didNavigateAwayFromSyncPage = function() {
+    SyncPrivateApi.setPageStatusCallback_ = null;
+    chrome.send('SyncSetupDidClosePage');
+  };
+
+  /**
+   * Sets the callback to be invoked when sync data has been fetched.
+   * @param {!function(SyncPrivateApi.SyncPrefs)} callback
+   */
+  SyncPrivateApi.setSyncPrefsCallback = function(callback) {
+    SyncPrivateApi.syncPrefsCallback_ = callback;
+  };
+
+  /**
+   * Handler for when state has been fetched from C++.
+   * @param {!SyncPrivateApi.SyncPrefs} syncPrefsFromCpp
+   * @private
+   */
+  SyncPrivateApi.sendSyncPrefs_ = function(syncPrefsFromCpp) {
+    if (SyncPrivateApi.syncPrefsCallback_)
+      SyncPrivateApi.syncPrefsCallback_(syncPrefsFromCpp);
+  };
+
+  /**
+   * Sets the sync state by sending it to the C++ layer.
+   * @param {!SyncPrivateApi.SyncPrefs} syncPrefs
+   * @param {!function(SyncPrivateApi.PageStatus)} callback
+   */
+  SyncPrivateApi.setSyncPrefs = function(syncPrefs, callback) {
+    SyncPrivateApi.setPageStatusCallback_ = callback;
+    chrome.send('SyncSetupConfigure', [JSON.stringify(syncPrefs)]);
+  };
+
+  /**
+   * Handler for when setSyncPrefs() has either succeeded or failed.
+   * @param {!SyncPrivateApi.SyncPrefs} state
+   * @private
+   */
+  SyncPrivateApi.setPageStatus_ = function(state) {
+    if (SyncPrivateApi.setPageStatusCallback_)
+      SyncPrivateApi.setPageStatusCallback_(state);
+
+    SyncPrivateApi.setPageStatusCallback_ = null;
+  };
+
+  /**
+   * Legacy object called by SyncSetupHandler. It's an inconsistent name, but
+   * needed to allow a single handler to support both old and new Sync settings.
+   * TODO(tommycli): Remove when old Sync in Options is removed.
+   */
+  function SyncSetupOverlay() {}
+
+  /**
+   * This function encapsulates the logic that maps from the legacy
+   * SyncSettingsHandler to an API natural to the new Polymer implementation.
+   * @param {!SyncPrivateApi.PageStatus} status
+   * @param {!SyncPrivateApi.SyncPrefs} prefs
+   */
+  SyncSetupOverlay.showSyncSetupPage = function(status, prefs) {
+    switch (status) {
+      case SyncPrivateApi.PageStatus.TIMED_OUT:
+      case SyncPrivateApi.PageStatus.DONE:
+        SyncPrivateApi.setPageStatus_(status);
+        break;
+      case SyncPrivateApi.PageStatus.CONFIGURE:
+        if (prefs.passphraseFailed) {
+          SyncPrivateApi.setPageStatus_(
+              SyncPrivateApi.PageStatus.PASSPHRASE_ERROR);
+          return;
+        }
+
+        SyncPrivateApi.sendSyncPrefs_(prefs);
+        break;
+      default:
+        // Other statuses (i.e. "spinner") are ignored.
+    }
+  };
+
+  return {
+    SyncPrivateApi: SyncPrivateApi,
+    SyncSetupOverlay: SyncSetupOverlay,
+  };
+});
+
+// Must be global for the legacy C++ handler to call.
+var SyncSetupOverlay = settings.SyncSetupOverlay;
diff --git a/chrome/browser/resources/settings/users_page/user_list.html b/chrome/browser/resources/settings/users_page/user_list.html
index 6663ceac..88f1bf52 100644
--- a/chrome/browser/resources/settings/users_page/user_list.html
+++ b/chrome/browser/resources/settings/users_page/user_list.html
@@ -2,7 +2,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 
-<dom-module id="cr-settings-user-list">
+<dom-module id="settings-user-list">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="user_list.css">
diff --git a/chrome/browser/resources/settings/users_page/user_list.js b/chrome/browser/resources/settings/users_page/user_list.js
index dae00f5b..35bce2380 100644
--- a/chrome/browser/resources/settings/users_page/user_list.js
+++ b/chrome/browser/resources/settings/users_page/user_list.js
@@ -4,19 +4,19 @@
 
 /**
  * @fileoverview
- * 'cr-settings-user-list' shows a list of users whitelisted on this Chrome OS
+ * 'settings-user-list' shows a list of users whitelisted on this Chrome OS
  * device.
  *
  * Example:
  *
- *    <cr-settings-user-list prefs="{{prefs}}">
- *    </cr-settings-user-list>
+ *    <settings-user-list prefs="{{prefs}}">
+ *    </settings-user-list>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-user-list
+ * @element settings-user-list
  */
 Polymer({
-  is: 'cr-settings-user-list',
+  is: 'settings-user-list',
 
   properties: {
     /**
diff --git a/chrome/browser/resources/settings/users_page/users_page.html b/chrome/browser/resources/settings/users_page/users_page.html
index eba63ee..2f33ad1 100644
--- a/chrome/browser/resources/settings/users_page/users_page.html
+++ b/chrome/browser/resources/settings/users_page/users_page.html
@@ -7,7 +7,7 @@
 <link rel="import" href="chrome://md-settings/settings_page/settings_section.html">
 <link rel="import" href="user_list.html">
 
-<dom-module id="cr-settings-users-page">
+<dom-module id="settings-users-page">
   <link rel="import" type="css"
       href="chrome://md-settings/settings_page/settings_page.css">
   <link rel="import" type="css" href="users_page.css">
@@ -44,9 +44,9 @@
       </div>
       <div class="users">
         <div>
-          <cr-settings-user-list prefs="[[prefs]]"
+          <settings-user-list prefs="[[prefs]]"
               disabled="[[editingUsersDisabled]]">
-          </cr-settings-user-list>
+          </settings-user-list>
         </div>
         <div>
           <paper-input id="addUserInput" i18n-values="label:addUsersLabel"
diff --git a/chrome/browser/resources/settings/users_page/users_page.js b/chrome/browser/resources/settings/users_page/users_page.js
index 44681b25..da31d39 100644
--- a/chrome/browser/resources/settings/users_page/users_page.js
+++ b/chrome/browser/resources/settings/users_page/users_page.js
@@ -4,22 +4,22 @@
 
 /**
  * @fileoverview
- * 'cr-settings-users-page' is the settings page for managing user accounts on
+ * 'settings-users-page' is the settings page for managing user accounts on
  * the device.
  *
  * Example:
  *
  *    <neon-animated-pages>
- *      <cr-settings-users-page prefs="{{prefs}}">
- *      </cr-settings-users-page>
+ *      <settings-users-page prefs="{{prefs}}">
+ *      </settings-users-page>
  *      ... other pages ...
  *    </neon-animated-pages>
  *
  * @group Chrome Settings Elements
- * @element cr-settings-users-page
+ * @element settings-users-page
  */
 Polymer({
-  is: 'cr-settings-users-page',
+  is: 'settings-users-page',
 
   behaviors: [
     Polymer.IronA11yKeysBehavior
diff --git a/chrome/browser/safe_browsing/srt_fetcher_win.cc b/chrome/browser/safe_browsing/srt_fetcher_win.cc
index e21be259..f999b77 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_win.cc
+++ b/chrome/browser/safe_browsing/srt_fetcher_win.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
 
+#include <vector>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
@@ -46,9 +48,25 @@
 
 const wchar_t kSoftwareRemovalToolRegistryKey[] =
     L"Software\\Google\\Software Removal Tool";
+const wchar_t kEndTimeValueName[] = L"EndTime";
+const wchar_t kStartTimeValueName[] = L"StartTime";
 
 namespace {
 
+// Used to send UMA information about missing start and end time registry values
+// for the reporter.
+enum SwReporterRunningTimeRegistryError {
+  REPORTER_RUNNING_TIME_ERROR_NO_ERROR = 0,
+  REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID = 1,
+  REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME = 2,
+  REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME = 3,
+  REPORTER_RUNNING_TIME_ERROR_MISSING_BOTH_TIMES = 4,
+  REPORTER_RUNNING_TIME_ERROR_MAX,
+};
+
+const char kRunningTimeErrorMetricName[] =
+    "SoftwareReporter.RunningTimeRegistryError";
+
 // Overrides for the reporter launcher and prompt triggers free function, used
 // by tests.
 ReporterLauncher g_reporter_launcher_;
@@ -287,6 +305,63 @@
   new SRTFetcher(profile);
 }
 
+// Report the SwReporter run time with UMA both as reported by the tool via
+// the registry and as measured by |ReporterRunner|.
+void ReportSwReporterRuntime(const base::TimeDelta& reporter_running_time) {
+  UMA_HISTOGRAM_LONG_TIMES("SoftwareReporter.RunningTimeAccordingToChrome",
+                           reporter_running_time);
+
+  base::win::RegKey reporter_key(
+      HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_ALL_ACCESS);
+  if (!reporter_key.Valid()) {
+    UMA_HISTOGRAM_ENUMERATION(kRunningTimeErrorMetricName,
+                              REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID,
+                              REPORTER_RUNNING_TIME_ERROR_MAX);
+    return;
+  }
+
+  bool has_start_time = false;
+  int64 start_time_value = 0;
+  if (reporter_key.HasValue(kStartTimeValueName) &&
+      reporter_key.ReadInt64(kStartTimeValueName, &start_time_value) ==
+          ERROR_SUCCESS) {
+    has_start_time = true;
+    reporter_key.DeleteValue(kStartTimeValueName);
+  }
+
+  bool has_end_time = false;
+  int64 end_time_value = 0;
+  if (reporter_key.HasValue(kEndTimeValueName) &&
+      reporter_key.ReadInt64(kEndTimeValueName, &end_time_value) ==
+          ERROR_SUCCESS) {
+    has_end_time = true;
+    reporter_key.DeleteValue(kEndTimeValueName);
+  }
+
+  if (has_start_time && has_end_time) {
+    base::TimeDelta registry_run_time =
+        base::Time::FromInternalValue(end_time_value) -
+        base::Time::FromInternalValue(start_time_value);
+    UMA_HISTOGRAM_LONG_TIMES("SoftwareReporter.RunningTime", registry_run_time);
+    UMA_HISTOGRAM_ENUMERATION(kRunningTimeErrorMetricName,
+                              REPORTER_RUNNING_TIME_ERROR_NO_ERROR,
+                              REPORTER_RUNNING_TIME_ERROR_MAX);
+  } else if (!has_start_time && !has_end_time) {
+    UMA_HISTOGRAM_ENUMERATION(kRunningTimeErrorMetricName,
+                              REPORTER_RUNNING_TIME_ERROR_MISSING_BOTH_TIMES,
+                              REPORTER_RUNNING_TIME_ERROR_MAX);
+  } else if (!has_start_time) {
+    UMA_HISTOGRAM_ENUMERATION(kRunningTimeErrorMetricName,
+                              REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME,
+                              REPORTER_RUNNING_TIME_ERROR_MAX);
+  } else {
+    DCHECK(!has_end_time);
+    UMA_HISTOGRAM_ENUMERATION(kRunningTimeErrorMetricName,
+                              REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME,
+                              REPORTER_RUNNING_TIME_ERROR_MAX);
+  }
+}
+
 // This class tries to run the reporter and reacts to its exit code. It
 // schedules subsequent runs as needed, or retries as soon as a browser is
 // available when none is on first try.
@@ -335,9 +410,11 @@
   // This method is called on the UI thread when the reporter run has completed.
   // This is run as a task posted from an interruptible worker thread so should
   // be resilient to unexpected shutdown.
-  void ReporterDone(int exit_code) {
+  void ReporterDone(const base::Time& reporter_start_time, int exit_code) {
     DCHECK(thread_checker_.CalledOnValidThread());
 
+    base::TimeDelta reporter_running_time =
+        base::Time::Now() - reporter_start_time;
     // Don't continue when the reporter process failed to launch, but still try
     // again after the regular delay. It's not worth retrying earlier, risking
     // running too often if it always fails, since not many users fail here.
@@ -356,6 +433,7 @@
       local_state->SetInt64(prefs::kSwReporterLastTimeTriggered,
                             base::Time::Now().ToInternalValue());
     }
+    ReportSwReporterRuntime(reporter_running_time);
 
     if (!IsInSRTPromptFieldTrialGroups()) {
       // Knowing about disabled field trial is more important than reporter not
@@ -421,7 +499,8 @@
       base::PostTaskAndReplyWithResult(
           blocking_task_runner_.get(), FROM_HERE,
           base::Bind(&LaunchAndWaitForExit, exe_path_, version_),
-          base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this)));
+          base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this),
+                     base::Time::Now()));
     } else {
       main_thread_task_runner_->PostDelayedTask(
           FROM_HERE,
diff --git a/chrome/browser/safe_browsing/srt_fetcher_win.h b/chrome/browser/safe_browsing/srt_fetcher_win.h
index 76f2813..a2e180b 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_win.h
+++ b/chrome/browser/safe_browsing/srt_fetcher_win.h
@@ -19,8 +19,10 @@
 
 namespace safe_browsing {
 
-// The registry key for the Reporter and Cleaner.
+// SRT registry keys and value names.
 extern const wchar_t kSoftwareRemovalToolRegistryKey[];
+extern const wchar_t kEndTimeValueName[];
+extern const wchar_t kStartTimeValueName[];
 
 // Reporter exit codes.
 const int kSwReporterCleanupNeeded = 0;
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 3ea6584..9dec1aa 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/memory_pressure_listener.h"
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 6d50c0b..8b79012 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index c8c24c6..e3593a6 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -41,6 +41,7 @@
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_manager_base.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/user_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc b/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
index a734f1b0..3a4eea6a 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
+++ b/chrome/browser/tracing/chrome_tracing_delegate_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/prefs/pref_service.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3e5991d..42718b9 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -535,6 +535,8 @@
       "exclusive_access/fullscreen_controller_state_tests.h",
       "exclusive_access/fullscreen_controller_test.cc",
       "exclusive_access/fullscreen_controller_test.h",
+      "test/test_confirm_bubble_model.cc",
+      "test/test_confirm_bubble_model.h",
     ]
   }
 
diff --git a/chrome/browser/ui/android/infobars/confirm_infobar.cc b/chrome/browser/ui/android/infobars/confirm_infobar.cc
index 508de2b9..8052f6f 100644
--- a/chrome/browser/ui/android/infobars/confirm_infobar.cc
+++ b/chrome/browser/ui/android/infobars/confirm_infobar.cc
@@ -58,7 +58,8 @@
           env, delegate->GetLinkText());
 
   ScopedJavaLocalRef<jobject> java_bitmap;
-  if (!delegate->GetIcon().IsEmpty()) {
+  if (delegate->GetIconId() == infobars::InfoBarDelegate::kNoIconID &&
+      !delegate->GetIcon().IsEmpty()) {
     java_bitmap = gfx::ConvertToJavaBitmap(delegate->GetIcon().ToSkBitmap());
   }
 
diff --git a/chrome/browser/ui/app_list/app_list_service_mac.mm b/chrome/browser/ui/app_list/app_list_service_mac.mm
index b6673cdf..b5c6d95 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac.mm
@@ -250,7 +250,7 @@
   const NSSize ns_window_size = [window frame].size;
   gfx::Size window_size(ns_window_size.width, ns_window_size.height);
   int primary_display_height =
-      NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]);
+      NSMaxY([[[NSScreen screens] firstObject] frame]);
   AppListServiceMac::FindAnchorPoint(window_size,
                                      display,
                                      primary_display_height,
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
index 337fe2e..bad78a5d 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
@@ -57,7 +57,7 @@
 NSRect GfxToCocoaBounds(gfx::Rect bounds) {
   typedef AppWindow::BoundsSpecification BoundsSpecification;
 
-  NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
+  NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame];
 
   // If coordinates are unspecified, center window on primary screen.
   if (bounds.x() == BoundsSpecification::kUnspecifiedPosition)
@@ -452,7 +452,7 @@
 
 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   NSRect frame = restored_bounds_;
   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
@@ -469,7 +469,7 @@
 
 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   NSRect frame = [window() frame];
   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
@@ -687,7 +687,7 @@
 
   // Flip the coordinates based on the main screen.
   NSInteger screen_height =
-      NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
+      NSHeight([[[NSScreen screens] firstObject] frame]);
 
   NSRect frame_nsrect = [window() frame];
   gfx::Rect frame_rect(NSRectToCGRect(frame_nsrect));
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.mm b/chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.mm
index 8d54aac9..73e2f32a 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.mm
+++ b/chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.mm
@@ -139,7 +139,7 @@
   // coordinate space places the origin at the top-left of the first screen,
   // whereas Cocoa's coordinate space expects the origin to be at the
   // bottom-left of this same screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   frame.origin.y = NSMaxY([screen frame]) - NSMaxY(frame);
 
   // TODO(isherman): The view should support scrolling if the popup gets too
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
index c4945fd..40584c3 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
+++ b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
@@ -406,7 +406,7 @@
       [[field window] convertBaseToScreen:textFrameInScreen.origin];
 
   // And adjust for gfx::Rect being flipped compared to OSX coordinates.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   textFrameInScreen.origin.y =
       NSMaxY([screen frame]) - NSMaxY(textFrameInScreen);
   gfx::Rect textFrameRect(NSRectToCGRect(textFrameInScreen));
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 028c744..a3716da4 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -210,7 +210,7 @@
                                    real_bounds.width(),
                                    real_bounds.height());
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   cocoa_bounds.origin.y =
       NSHeight([screen frame]) - real_bounds.height() - real_bounds.y();
 
@@ -348,7 +348,7 @@
 
 gfx::Rect BrowserWindowCocoa::GetRestoredBounds() const {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   NSRect frame = [controller_ regularWindowFrame];
   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index 99ef304..876f3dc 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -122,7 +122,7 @@
       [self isInAnyFullscreenMode] ? savedRegularWindow_ : [self window];
 
   // Window positions are stored relative to the origin of the primary monitor.
-  NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame];
+  NSRect monitorFrame = [[[NSScreen screens] firstObject] frame];
   NSScreen* windowScreen = [window screen];
 
   // Start with the window's frame, which is in virtual coordinates.
@@ -682,7 +682,7 @@
 
   enteringAppKitFullscreen_ = YES;
   enteringAppKitFullscreenOnPrimaryScreen_ =
-      [[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
+      [[[self window] screen] isEqual:[[NSScreen screens] firstObject]];
 
   fullscreen_mac::SlidingStyle style;
   if (browser_->exclusive_access_manager()
@@ -1098,7 +1098,7 @@
   // transition from working well. See http://crbug.com/396980 for more
   // details.
   if ([[self class] systemSettingsRequireMavericksAppKitFullscreenHack] &&
-      ![[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
+      ![[[self window] screen] isEqual:[[NSScreen screens] firstObject]]) {
     return NO;
   }
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
index 9786b9b..0620ce5a 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
@@ -114,7 +114,7 @@
 
 gfx::Rect OmniboxPopupViewMac::GetTargetBounds() {
   // Flip the coordinate system before returning.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   NSRect monitor_frame = [screen frame];
   gfx::Rect bounds(NSRectToCGRect(target_popup_frame_));
   bounds.set_y(monitor_frame.size.height - bounds.y() - bounds.height());
diff --git a/chrome/browser/ui/cocoa/panels/panel_utils_cocoa.mm b/chrome/browser/ui/cocoa/panels/panel_utils_cocoa.mm
index a5f1739..2fe8a56 100644
--- a/chrome/browser/ui/cocoa/panels/panel_utils_cocoa.mm
+++ b/chrome/browser/ui/cocoa/panels/panel_utils_cocoa.mm
@@ -8,7 +8,7 @@
 
 NSRect ConvertRectToCocoaCoordinates(const gfx::Rect& bounds) {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
 
   return NSMakeRect(
       bounds.x(), NSHeight([screen frame]) - bounds.height() - bounds.y(),
@@ -17,7 +17,7 @@
 
 gfx::Rect ConvertRectFromCocoaCoordinates(NSRect bounds) {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
 
   return gfx::Rect(
       NSMinX(bounds), NSHeight([screen frame]) - NSMaxY(bounds),
@@ -26,14 +26,14 @@
 
 NSPoint ConvertPointToCocoaCoordinates(const gfx::Point& point) {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
 
   return NSMakePoint(point.x(), NSHeight([screen frame]) - point.y());
 }
 
 gfx::Point ConvertPointFromCocoaCoordinates(NSPoint point) {
   // Flip coordinates based on the primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
 
   return gfx::Point(point.x, NSHeight([screen frame]) - point.y);
 }
diff --git a/chrome/browser/ui/cocoa/presentation_mode_controller.mm b/chrome/browser/ui/cocoa/presentation_mode_controller.mm
index 8f7c688..0998ad7 100644
--- a/chrome/browser/ui/cocoa/presentation_mode_controller.mm
+++ b/chrome/browser/ui/cocoa/presentation_mode_controller.mm
@@ -535,7 +535,7 @@
 
 - (BOOL)isWindowOnPrimaryScreen {
   NSScreen* screen = [[browserController_ window] screen];
-  NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* primaryScreen = [[NSScreen screens] firstObject];
   return (screen == primaryScreen);
 }
 
diff --git a/chrome/browser/ui/extensions/extension_message_bubble_factory.cc b/chrome/browser/ui/extensions/extension_message_bubble_factory.cc
index 6bda1e5..d898377 100644
--- a/chrome/browser/ui/extensions/extension_message_bubble_factory.cc
+++ b/chrome/browser/ui/extensions/extension_message_bubble_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/extensions/extension_message_bubble_factory.h"
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/metrics/field_trial.h"
@@ -17,7 +18,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_switches.h"
 #include "components/version_info/version_info.h"
 #include "extensions/common/feature_switch.h"
 
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 150d5b3..aae0adf 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -4,6 +4,7 @@
 
 #include <sstream>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_samples.h"
@@ -36,7 +37,6 @@
 #include "chrome/browser/ui/search/search_tab_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/theme_source.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/instant_types.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
index 085afa1..4d3b560 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
@@ -55,14 +55,17 @@
       return base::string16();
   }
 
-  // Don't autogenerate keywords for referrers that are anything other than HTTP
-  // or have a path.
+  // Don't autogenerate keywords for referrers that
+  // a) are anything other than HTTP/HTTPS or
+  // b) have a path.
   //
   // If we relax the path constraint, we need to be sure to sanitize the path
   // elements and update AutocompletePopup to look for keywords using the path.
   // See http://b/issue?id=863583.
-  if (!url.SchemeIs(url::kHttpScheme) || (url.path().length() > 1))
+  if (!(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme)) ||
+      (url.path().length() > 1)) {
     return base::string16();
+  }
 
   return TemplateURL::GenerateKeyword(url, accept_languages);
 }
@@ -87,7 +90,9 @@
   GenerateKeywordIfNecessary(params);
 }
 
-bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) {
+bool SearchEngineTabHelper::OnMessageReceived(
+    const IPC::Message& message,
+    content::RenderFrameHost* render_frame_host) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD, OnPageHasOSDD)
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.h b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
index 4ccc1d6d..cab3cb8 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.h
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
@@ -30,7 +30,8 @@
   void DidNavigateMainFrame(
       const content::LoadCommittedDetails& details,
       const content::FrameNavigateParams& params) override;
-  bool OnMessageReceived(const IPC::Message& message) override;
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* rfh) override;
 
  private:
   explicit SearchEngineTabHelper(content::WebContents* web_contents);
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 82d9317..88b827a 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -16,6 +16,7 @@
 #include "chrome/common/switch_utils.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/common/autofill_switches.h"
 #include "components/infobars/core/simple_alert_infobar_delegate.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/nacl/common/nacl_switches.h"
diff --git a/chrome/browser/ui/toolbar/media_router_action.cc b/chrome/browser/ui/toolbar/media_router_action.cc
index 72a06cef4..34877266f4 100644
--- a/chrome/browser/ui/toolbar/media_router_action.cc
+++ b/chrome/browser/ui/toolbar/media_router_action.cc
@@ -57,10 +57,13 @@
       weak_ptr_factory_(this) {
   DCHECK(browser_);
   tab_strip_model_observer_.Add(browser_->tab_strip_model());
+
+  RegisterObserver();
   OnHasLocalRouteUpdated(GetMediaRouter(browser)->HasLocalRoute());
 }
 
 MediaRouterAction::~MediaRouterAction() {
+  UnregisterObserver();
 }
 
 std::string MediaRouterAction::GetId() const {
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index 29c1272..39b360a 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -48,7 +48,7 @@
 const int kNonClientRestoredExtraThickness = 9;
 // In the window corners, the resize areas don't actually expand bigger, but the
 // 16 px at the end of the top and bottom edges triggers diagonal resizing.
-const int kResizeAreaCornerSize = 16;
+const int kResizeCornerWidth = 16;
 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
 // user).
@@ -243,7 +243,7 @@
   // first so that clicks in a tab don't get treated as sysmenu clicks.
   int nonclient_border_thickness = NonClientBorderThickness();
   if (gfx::Rect(nonclient_border_thickness,
-                gfx::win::GetSystemMetricsInDIP(SM_CXSIZEFRAME),
+                gfx::win::GetSystemMetricsInDIP(SM_CYSIZEFRAME),
                 gfx::win::GetSystemMetricsInDIP(SM_CXSMICON),
                 gfx::win::GetSystemMetricsInDIP(SM_CYSMICON)).Contains(point))
     return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
@@ -251,11 +251,17 @@
   if (frame_component != HTNOWHERE)
     return frame_component;
 
-  int frame_border_thickness = FrameBorderThickness();
-  int window_component = GetHTComponentForFrame(point, frame_border_thickness,
-      nonclient_border_thickness, frame_border_thickness,
-      kResizeAreaCornerSize - frame_border_thickness,
-      frame()->widget_delegate()->CanResize());
+  int frame_top_border_height = FrameTopBorderHeight();
+  // We want the resize corner behavior to apply to the kResizeCornerWidth
+  // pixels at each end of the top and bottom edges.  Because |point|'s x
+  // coordinate is based on the DWM-inset portion of the window (so, it's 0 at
+  // the first pixel inside the left DWM margin), we need to subtract the DWM
+  // margin thickness, which we calculate as the total frame border thickness
+  // minus the nonclient border thickness.
+  const int dwm_margin = FrameBorderThickness() - nonclient_border_thickness;
+  int window_component = GetHTComponentForFrame(point, frame_top_border_height,
+      nonclient_border_thickness, frame_top_border_height,
+      kResizeCornerWidth - dwm_margin, frame()->widget_delegate()->CanResize());
   // Fall back to the caption if no other component matches.
   return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
 }
@@ -325,6 +331,14 @@
       0 : gfx::win::GetSystemMetricsInDIP(SM_CXSIZEFRAME);
 }
 
+int GlassBrowserFrameView::FrameTopBorderHeight() const {
+  // We'd like to use FrameBorderThickness() here, but the maximized Aero glass
+  // frame has a 0 frame border around most edges and a CYSIZEFRAME-thick border
+  // at the top (see AeroGlassFrame::OnGetMinMaxInfo()).
+  return frame()->IsFullscreen() ?
+      0 : gfx::win::GetSystemMetricsInDIP(SM_CYSIZEFRAME);
+}
+
 int GlassBrowserFrameView::NonClientBorderThickness() const {
   if (frame()->IsMaximized() || frame()->IsFullscreen())
     return 0;
@@ -338,13 +352,13 @@
   if (frame()->IsFullscreen())
     return 0;
 
-  // We'd like to use FrameBorderThickness() here, but the maximized Aero glass
-  // frame has a 0 frame border around most edges and a CYSIZEFRAME-thick border
-  // at the top (see AeroGlassFrame::OnGetMinMaxInfo()).
-  return gfx::win::GetSystemMetricsInDIP(SM_CYSIZEFRAME) +
+  // The tab top inset is equal to the height of any shadow region above the
+  // tabs, plus a 1 px top stroke.  In maximized mode, we want to push the
+  // shadow region off the top of the screen.
+  const int tab_shadow_height = GetLayoutInsets(TAB).top() - 1;
+  return FrameTopBorderHeight() +
       (frame()->IsMaximized() ?
-          -GetLayoutConstant(TABSTRIP_TOP_SHADOW_HEIGHT) :
-          kNonClientRestoredExtraThickness);
+          -tab_shadow_height : kNonClientRestoredExtraThickness);
 }
 
 void GlassBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
@@ -497,13 +511,19 @@
     button_x = width() - frame()->GetMinimizeButtonOffset() +
         kNewAvatarButtonOffset;
 
-  // We need to offset the button correctly in maximized mode, so that the
-  // custom glass style aligns with the native control glass style. The
-  // glass shadow is off by 1px, which was determined by visual inspection.
-  const int shadow_height = GetLayoutConstant(TABSTRIP_TOP_SHADOW_HEIGHT);
-  int button_y = frame()->IsMaximized() ?
-      (NonClientTopBorderHeight() + shadow_height - 1) : 1;
-
+  // The caption button position and size is confusing.  In maximized mode, the
+  // caption buttons are SM_CYMENUSIZE pixels high and are placed
+  // FrameTopBorderHeight() pixels from the top of the window; all those top
+  // border pixels are offscreen, so this result in caption buttons flush with
+  // the top of the screen.  In restored mode, the caption buttons are first
+  // placed just below a 2 px border at the top of the window (which is the
+  // first two pixels' worth of FrameTopBorderHeight()), then extended upwards
+  // one extra pixel to overlap part of this border.
+  //
+  // To match both of these, we size the button as if it's always the extra one
+  // pixel in height, then we place it at the correct position in restored mode,
+  // or one pixel above the top of the screen in maximized mode.
+  int button_y = frame()->IsMaximized() ? (FrameTopBorderHeight() - 1) : 1;
   new_avatar_button()->SetBounds(
       button_x,
       button_y,
@@ -524,10 +544,8 @@
 
   int avatar_bottom = GetTopInset() +
       browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
-  int avatar_restored_y = avatar_bottom - incognito_icon.height();
-  const int shadow_height = GetLayoutConstant(TABSTRIP_TOP_SHADOW_HEIGHT);
   int avatar_y = frame()->IsMaximized() ?
-      (NonClientTopBorderHeight() + shadow_height) : avatar_restored_y;
+      FrameTopBorderHeight() : (avatar_bottom - incognito_icon.height());
   avatar_bounds_.SetRect(avatar_x, avatar_y, incognito_icon.width(),
       browser_view()->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0);
   if (avatar_button())
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index 292d56e1..748ef25 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -53,10 +53,13 @@
   bool DoesIntersectRect(const views::View* target,
                          const gfx::Rect& rect) const override;
 
-  // Returns the thickness of the border that makes up the window frame edges.
-  // This does not include any client edge.
+  // Returns the thickness of the border that makes up the window left, right,
+  // and bottom frame edges.  This does not include any client edge.
   int FrameBorderThickness() const;
 
+  // Returns the height of the window top frame edge.
+  int FrameTopBorderHeight() const;
+
   // Returns the thickness of the entire nonclient left, right, and bottom
   // borders, including both the window frame and any client edge.
   int NonClientBorderThickness() const;
diff --git a/chrome/browser/ui/views/layout_constants.cc b/chrome/browser/ui/views/layout_constants.cc
index 2414aba..66ee3fc 100644
--- a/chrome/browser/ui/views/layout_constants.cc
+++ b/chrome/browser/ui/views/layout_constants.cc
@@ -27,7 +27,6 @@
   const int kTabstripTabOverlap[] = {26, 26, 26};
 #endif
   const int kTabstripToolbarOverlap[] = {3, 3, 3};
-  const int kTabstripTopShadowHeight[] = {3, 3, 3};
   const int kToolbarContentShadowHeight[] = {0, 0, 0};
   const int kToolbarContentShadowHeightAsh[] = {2, 0, 0};
   const int kToolbarElementPadding[] = {0, 0, 8};
@@ -58,8 +57,6 @@
       return kTabstripTabOverlap[mode];
     case TABSTRIP_TOOLBAR_OVERLAP:
       return kTabstripToolbarOverlap[mode];
-    case TABSTRIP_TOP_SHADOW_HEIGHT:
-      return kTabstripTopShadowHeight[mode];
     case TAB_CLOSE_BUTTON_TRAILING_PADDING_OVERLAP:
       return kTabCloseButtonTrailingPaddingOverlap[mode];
     case TAB_FAVICON_TITLE_SPACING:
diff --git a/chrome/browser/ui/views/layout_constants.h b/chrome/browser/ui/views/layout_constants.h
index 3c14b98..0125719 100644
--- a/chrome/browser/ui/views/layout_constants.h
+++ b/chrome/browser/ui/views/layout_constants.h
@@ -46,9 +46,6 @@
   // The vertical overlap of the tabstrip atop the toolbar.
   TABSTRIP_TOOLBAR_OVERLAP,
 
-  // The height of the shadow region above the top of the tabs.
-  TABSTRIP_TOP_SHADOW_HEIGHT,
-
   // The amount by which the tab close button should overlap the trailing
   // padding region after the tab's contents region.
   TAB_CLOSE_BUTTON_TRAILING_PADDING_OVERLAP,
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index ebaf89c..30b7602 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -157,6 +157,9 @@
 }
 
 MediaRouterUI::~MediaRouterUI() {
+  if (issues_observer_)
+    issues_observer_->UnregisterObserver();
+
   if (query_result_manager_.get())
     query_result_manager_->RemoveObserver(this);
   if (presentation_service_delegate_.get())
@@ -204,8 +207,7 @@
   DCHECK(initiator);
   DCHECK(router_);
 
-  // Register for Issue and MediaRoute updates.
-  issues_observer_.reset(new UIIssuesObserver(router_, this));
+  // Register for MediaRoute updates.
   routes_observer_.reset(new UIMediaRoutesObserver(
       router_,
       base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
@@ -253,88 +255,15 @@
 
 void MediaRouterUI::UIInitialized() {
   ui_initialized_ = true;
+
+  // Register for Issue updates.
+  if (!issues_observer_)
+    issues_observer_.reset(new UIIssuesObserver(router_, this));
+  issues_observer_->RegisterObserver();
 }
 
-bool MediaRouterUI::CreateRoute(const MediaSink::Id& sink_id) {
-  return DoCreateRoute(sink_id, GetPreferredCastMode(cast_modes_));
-}
-
-bool MediaRouterUI::CreateRouteWithCastModeOverride(
-    const MediaSink::Id& sink_id,
-    MediaCastMode cast_mode_override) {
-  // NOTE: It's actually not an override if
-  // |cast_mode_override| == |GetPreferredCastMode(cast_modes_)|.
-  return DoCreateRoute(sink_id, cast_mode_override);
-}
-
-void MediaRouterUI::CloseRoute(const MediaRoute::Id& route_id) {
-  router_->CloseRoute(route_id);
-}
-
-void MediaRouterUI::AddIssue(const Issue& issue) {
-  router_->AddIssue(issue);
-}
-
-void MediaRouterUI::ClearIssue(const std::string& issue_id) {
-  router_->ClearIssue(issue_id);
-}
-
-std::string MediaRouterUI::GetInitialHeaderText() const {
-  if (cast_modes_.empty())
-    return std::string();
-
-  return MediaCastModeToDescription(GetPreferredCastMode(cast_modes_),
-                                    GetTruncatedHostFromURL(frame_url_));
-}
-
-std::string MediaRouterUI::GetInitialHeaderTextTooltip() const {
-  if (cast_modes_.empty())
-    return std::string();
-
-  return GetHostFromURL(frame_url_);
-}
-
-void MediaRouterUI::OnResultsUpdated(
-    const std::vector<MediaSinkWithCastModes>& sinks) {
-  sinks_ = sinks;
-  if (ui_initialized_)
-    handler_->UpdateSinks(sinks_);
-}
-
-void MediaRouterUI::SetIssue(const Issue* issue) {
-  if (ui_initialized_)
-    handler_->UpdateIssue(issue);
-}
-
-void MediaRouterUI::OnRoutesUpdated(const std::vector<MediaRoute>& routes) {
-  routes_ = routes;
-  if (ui_initialized_)
-    handler_->UpdateRoutes(routes_);
-}
-
-void MediaRouterUI::OnRouteResponseReceived(const int route_request_id,
-                                            const MediaSink::Id& sink_id,
-                                            const MediaRoute* route,
-                                            const std::string& presentation_id,
-                                            const std::string& error) {
-  DVLOG(1) << "OnRouteResponseReceived";
-  // If we receive a new route that we aren't expecting, do nothing.
-  if (route_request_id != current_route_request_id_)
-    return;
-
-  if (!route) {
-    // The provider will handle sending an issue for a failed route request.
-    DVLOG(0) << "MediaRouteResponse returned error: " << error;
-  }
-
-  handler_->OnCreateRouteResponseReceived(sink_id, route);
-  requesting_route_for_default_source_ = false;
-  current_route_request_id_ = -1;
-  route_creation_timer_.Stop();
-}
-
-bool MediaRouterUI::DoCreateRoute(const MediaSink::Id& sink_id,
-                                  MediaCastMode cast_mode) {
+bool MediaRouterUI::CreateRoute(const MediaSink::Id& sink_id,
+                                MediaCastMode cast_mode) {
   DCHECK(query_result_manager_.get());
   DCHECK(initiator_);
 
@@ -405,6 +334,57 @@
   return true;
 }
 
+void MediaRouterUI::CloseRoute(const MediaRoute::Id& route_id) {
+  router_->CloseRoute(route_id);
+}
+
+void MediaRouterUI::AddIssue(const Issue& issue) {
+  router_->AddIssue(issue);
+}
+
+void MediaRouterUI::ClearIssue(const std::string& issue_id) {
+  router_->ClearIssue(issue_id);
+}
+
+void MediaRouterUI::OnResultsUpdated(
+    const std::vector<MediaSinkWithCastModes>& sinks) {
+  sinks_ = sinks;
+  if (ui_initialized_)
+    handler_->UpdateSinks(sinks_);
+}
+
+void MediaRouterUI::SetIssue(const Issue* issue) {
+  if (ui_initialized_)
+    handler_->UpdateIssue(issue);
+}
+
+void MediaRouterUI::OnRoutesUpdated(const std::vector<MediaRoute>& routes) {
+  routes_ = routes;
+  if (ui_initialized_)
+    handler_->UpdateRoutes(routes_);
+}
+
+void MediaRouterUI::OnRouteResponseReceived(const int route_request_id,
+                                            const MediaSink::Id& sink_id,
+                                            const MediaRoute* route,
+                                            const std::string& presentation_id,
+                                            const std::string& error) {
+  DVLOG(1) << "OnRouteResponseReceived";
+  // If we receive a new route that we aren't expecting, do nothing.
+  if (route_request_id != current_route_request_id_)
+    return;
+
+  if (!route) {
+    // The provider will handle sending an issue for a failed route request.
+    DVLOG(0) << "MediaRouteResponse returned error: " << error;
+  }
+
+  handler_->OnCreateRouteResponseReceived(sink_id, route);
+  requesting_route_for_default_source_ = false;
+  current_route_request_id_ = -1;
+  route_creation_timer_.Stop();
+}
+
 void MediaRouterUI::RouteCreationTimeout() {
   requesting_route_for_default_source_ = false;
   current_route_request_id_ = -1;
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.h b/chrome/browser/ui/webui/media_router/media_router_ui.h
index 7ef49b7e..3d7859f4 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -87,20 +87,12 @@
   // Notifies this instance that the UI has been initialized.
   void UIInitialized();
 
-  // Requests a route be created from the source determined by the preferred
-  // MediaCastMode, to the sink given by |sink_id|.
-  // The preferred cast mode is determined from the set of currently supported
-  // cast modes in |cast_modes_|.
-  // Returns false if unable to request the route.
+  // Requests a route be created from the source mapped to
+  // |cast_mode|, to the sink given by |sink_id|.
+  // Returns true if a route request is successfully submitted.
   // |OnRouteResponseReceived()| will be invoked when the route request
   // completes.
-  bool CreateRoute(const MediaSink::Id& sink_id);
-
-  // Requests a route be created from the source mapped to
-  // |cast_mode_override|, to the sink given by |sink_id|.
-  // Returns true if a route request is successfully submitted.
-  bool CreateRouteWithCastModeOverride(const MediaSink::Id& sink_id,
-                                       MediaCastMode cast_mode_override);
+  bool CreateRoute(const MediaSink::Id& sink_id, MediaCastMode cast_mode);
 
   // Calls MediaRouter to close the given route.
   void CloseRoute(const MediaRoute::Id& route_id);
@@ -111,15 +103,6 @@
   // Calls MediaRouter to clear the given issue.
   void ClearIssue(const Issue::Id& issue_id);
 
-  // Returns the header text that should be displayed in the UI when it is
-  // initially loaded. The header text is determined by the preferred cast mode.
-  std::string GetInitialHeaderText() const;
-
-  // Returns the tooltip text for the header that should be displayed
-  // in the UI when it is initially loaded. At present, this text is
-  // just the full hostname of the current site.
-  std::string GetInitialHeaderTextTooltip() const;
-
   // Returns the hostname of the default source's parent frame URL.
   std::string GetFrameURLHost() const;
   bool HasPendingRouteRequest() const {
@@ -177,8 +160,6 @@
                                const std::string& presentation_id,
                                const std::string& error);
 
-  bool DoCreateRoute(const MediaSink::Id& sink_id, MediaCastMode cast_mode);
-
   // Creates and sends an issue if route creation times out.
   void RouteCreationTimeout();
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
index 452de54..3c44c1c3c 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
@@ -253,11 +253,6 @@
   DVLOG(1) << "OnRequestInitialData";
   base::DictionaryValue initial_data;
 
-  initial_data.SetString("headerText",
-      media_router_ui_->GetInitialHeaderText());
-  initial_data.SetString("headerTextTooltip",
-      media_router_ui_->GetInitialHeaderTextTooltip());
-
   // "No Cast devices found?" Chromecast help center page.
   initial_data.SetString("deviceMissingUrl",
       base::StringPrintf(kHelpPageUrlPrefix, 3249268));
@@ -269,10 +264,14 @@
       media_router_ui_->GetRouteProviderExtensionId()));
   initial_data.Set("routes", routes.release());
 
-  scoped_ptr<base::ListValue> cast_modes(CastModesToValue(
-      media_router_ui_->cast_modes(),
-      media_router_ui_->GetFrameURLHost()));
-  initial_data.Set("castModes", cast_modes.release());
+  const std::set<MediaCastMode> cast_modes = media_router_ui_->cast_modes();
+  scoped_ptr<base::ListValue> cast_modes_list(
+      CastModesToValue(cast_modes, media_router_ui_->GetFrameURLHost()));
+  initial_data.Set("castModes", cast_modes_list.release());
+  if (!cast_modes.empty()) {
+    initial_data.SetInteger("initialCastModeType",
+                            GetPreferredCastMode(cast_modes));
+  }
 
   web_ui()->CallJavascriptFunction(kSetInitialData, initial_data);
   media_router_ui_->UIInitialized();
@@ -297,6 +296,11 @@
     return;
   }
 
+  if (!IsValidCastModeNum(cast_mode_num)) {
+    DVLOG(1) << "Invalid cast mode: " << cast_mode_num << ". Aborting.";
+    return;
+  }
+
   MediaRouterUI* media_router_ui =
       static_cast<MediaRouterUI*>(web_ui()->GetController());
   if (media_router_ui->HasPendingRouteRequest()) {
@@ -310,22 +314,15 @@
     return;
   }
 
-  DVLOG(2) << "sink id: " << sink_id << ", cast mode: " << cast_mode_num;
+  DVLOG(2) << __FUNCTION__ << ": sink id: " << sink_id
+           << ", cast mode: " << cast_mode_num;
 
   // TODO(haibinlu): Pass additional parameters into the CreateRoute request,
   // e.g. low-fps-mirror, user-override. (crbug.com/490364)
-  bool success = false;
-  if (IsValidCastModeNum(cast_mode_num)) {
-    // User explicitly selected cast mode.
-    DVLOG(2) << "Cast mode override: " << cast_mode_num;
-    success = media_router_ui->CreateRouteWithCastModeOverride(
-        sink_id, static_cast<MediaCastMode>(cast_mode_num));
-  } else {
-    success = media_router_ui->CreateRoute(sink_id);
-  }
-
-  if (!success) {
-    // The provider will handle sending an issue for a failed route request.
+  if (!media_router_ui->CreateRoute(
+          sink_id, static_cast<MediaCastMode>(cast_mode_num))) {
+    // TODO(imcheng): Need to add an issue if failed to initiate a CreateRoute
+    // request.
     DVLOG(1) << "Error initiating route request.";
   }
 }
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index ecc1cf6..0eb06442 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -12,6 +12,8 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/google_chrome_strings.h"
 #include "chrome/grit/locale_settings.h"
+#include "chrome/grit/settings_chromium_strings.h"
+#include "chrome/grit/settings_google_chrome_strings.h"
 #include "chrome/grit/settings_strings.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "grit/components_strings.h"
@@ -190,6 +192,23 @@
                                   IDS_SETTINGS_CLEAR_DATA_EVERYTHING);
 }
 
+#if !defined(OS_CHROMEOS)
+void AddDefaultBrowserStrings(content::WebUIDataSource* html_source) {
+  html_source->AddLocalizedString(
+      "defaultBrowser", IDS_SETTINGS_DEFAULT_BROWSER);
+  html_source->AddLocalizedString(
+      "defaultBroswerDefault", IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT);
+  html_source->AddLocalizedString(
+      "defaultBroswerNotDefault", IDS_SETTINGS_DEFAULT_BROWSER_NOT_DEFAULT);
+  html_source->AddLocalizedString(
+      "defaultBroswerMakeDefault", IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT);
+  html_source->AddLocalizedString(
+      "defaultBroswerUnknown", IDS_SETTINGS_DEFAULT_BROWSER_UNKNOWN);
+  html_source->AddLocalizedString(
+      "defaultBroswerSecondary", IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY);
+}
+#endif
+
 void AddDownloadsStrings(content::WebUIDataSource* html_source) {
   html_source->AddLocalizedString(
       "downloadsPageTitle", IDS_SETTINGS_DOWNLOADS);
@@ -547,6 +566,9 @@
   AddAppearanceStrings(html_source);
   AddCertificateManagerStrings(html_source);
   AddClearBrowsingDataStrings(html_source);
+#if !defined(OS_CHROMEOS)
+  AddDefaultBrowserStrings(html_source);
+#endif
   AddDateTimeStrings(html_source);
   AddDownloadsStrings(html_source);
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 3f01c4b..cfacee5 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -7,11 +7,13 @@
 #include <string>
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/options/sync_setup_handler.h"
 #include "chrome/browser/ui/webui/settings/appearance_handler.h"
 #include "chrome/browser/ui/webui/settings/downloads_handler.h"
 #include "chrome/browser/ui/webui/settings/languages_handler.h"
 #include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/web_contents.h"
@@ -32,9 +34,11 @@
     : content::WebUIController(web_ui) {
   AddSettingsPageUIHandler(new AppearanceHandler(web_ui));
   AddSettingsPageUIHandler(new ClearBrowsingDataHandler(web_ui));
+  AddSettingsPageUIHandler(new DefaultBrowserHandler(web_ui));
   AddSettingsPageUIHandler(new DownloadsHandler());
   AddSettingsPageUIHandler(new LanguagesHandler(web_ui));
   AddSettingsPageUIHandler(new StartupPagesHandler(web_ui));
+  AddSettingsPageUIHandler(new SyncSetupHandler());
 
   content::WebUIDataSource* html_source =
       content::WebUIDataSource::Create(chrome::kChromeUIMdSettingsHost);
@@ -56,8 +60,8 @@
 }
 
 void MdSettingsUI::AddSettingsPageUIHandler(
-    settings::SettingsPageUIHandler* handler_raw) {
-  scoped_ptr<settings::SettingsPageUIHandler> handler(handler_raw);
+    content::WebUIMessageHandler* handler_raw) {
+  scoped_ptr<content::WebUIMessageHandler> handler(handler_raw);
   DCHECK(handler.get());
 
   web_ui()->AddMessageHandler(handler.release());
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.h b/chrome/browser/ui/webui/settings/md_settings_ui.h
index cfab2b4b..5623dc3 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.h
@@ -35,8 +35,7 @@
   ~MdSettingsUI() override;
 
  private:
-  // Adds SettingsPageUiHandler to the handlers list if handler is enabled.
-  void AddSettingsPageUIHandler(SettingsPageUIHandler* handler);
+  void AddSettingsPageUIHandler(content::WebUIMessageHandler* handler);
 
   DISALLOW_COPY_AND_ASSIGN(MdSettingsUI);
 };
diff --git a/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc b/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
new file mode 100644
index 0000000..75fc76f
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/settings_default_browser_handler.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 "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
+
+#include "base/bind.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+DefaultBrowserHandler::DefaultBrowserHandler(content::WebUI* webui)
+    : default_browser_worker_(
+          new ShellIntegration::DefaultBrowserWorker(this)) {
+  default_browser_policy_.Init(
+      prefs::kDefaultBrowserSettingEnabled, g_browser_process->local_state(),
+      base::Bind(&DefaultBrowserHandler::RequestDefaultBrowserState,
+                 base::Unretained(this), nullptr));
+}
+
+DefaultBrowserHandler::~DefaultBrowserHandler() {
+  default_browser_worker_->ObserverDestroyed();
+}
+
+void DefaultBrowserHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "SettingsDefaultBrowser.requestDefaultBrowserState",
+      base::Bind(&DefaultBrowserHandler::RequestDefaultBrowserState,
+                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "SettingsDefaultBrowser.setAsDefaultBrowser",
+      base::Bind(&DefaultBrowserHandler::SetAsDefaultBrowser,
+                 base::Unretained(this)));
+}
+
+void DefaultBrowserHandler::SetDefaultWebClientUIState(
+    ShellIntegration::DefaultWebClientUIState state) {
+  if (state == ShellIntegration::STATE_PROCESSING)
+    return;
+
+  if (state == ShellIntegration::STATE_IS_DEFAULT) {
+    // Notify the user in the future if Chrome ceases to be the user's chosen
+    // default browser.
+    Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
+        prefs::kCheckDefaultBrowser, true);
+  }
+
+  base::FundamentalValue is_default(
+      state == ShellIntegration::STATE_IS_DEFAULT);
+  base::FundamentalValue can_be_default(
+      state != ShellIntegration::STATE_UNKNOWN &&
+      !default_browser_policy_.IsManaged() &&
+      ShellIntegration::CanSetAsDefaultBrowser() !=
+          ShellIntegration::SET_DEFAULT_NOT_ALLOWED);
+
+  web_ui()->CallJavascriptFunction("Settings.updateDefaultBrowserState",
+                                   is_default, can_be_default);
+}
+
+bool DefaultBrowserHandler::IsInteractiveSetDefaultPermitted() {
+  return true;
+}
+
+void DefaultBrowserHandler::OnSetAsDefaultConcluded(bool succeeded) {
+  base::FundamentalValue success(succeeded);
+  web_ui()->CallJavascriptFunction("Settings.setAsDefaultConcluded", success);
+}
+
+void DefaultBrowserHandler::RequestDefaultBrowserState(
+    const base::ListValue* /*args*/) {
+  default_browser_worker_->StartCheckIsDefault();
+}
+
+void DefaultBrowserHandler::SetAsDefaultBrowser(const base::ListValue* args) {
+  CHECK(!default_browser_policy_.IsManaged());
+
+  default_browser_worker_->StartSetAsDefault();
+
+  // If the user attempted to make Chrome the default browser, notify
+  // them when this changes.
+  Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
+      prefs::kCheckDefaultBrowser, true);
+}
+
+}  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/settings_default_browser_handler.h b/chrome/browser/ui/webui/settings/settings_default_browser_handler.h
new file mode 100644
index 0000000..fad3006
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/settings_default_browser_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/prefs/pref_member.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/webui/settings/md_settings_ui.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+namespace settings {
+
+// The application used by the OS to open web documents (e.g. *.html)
+// is the "default browser".  This class is an API for the JavaScript
+// settings code to change the default browser settings.
+class DefaultBrowserHandler
+    : public SettingsPageUIHandler,
+      public ShellIntegration::DefaultWebClientObserver {
+ public:
+  explicit DefaultBrowserHandler(content::WebUI* webui);
+  ~DefaultBrowserHandler() override;
+
+  // SettingsPageUIHandler implementation.
+  void RegisterMessages() override;
+
+  // ShellIntegration::DefaultWebClientObserver implementation.
+  void SetDefaultWebClientUIState(
+      ShellIntegration::DefaultWebClientUIState state) override;
+  bool IsInteractiveSetDefaultPermitted() override;
+  void OnSetAsDefaultConcluded(bool succeeded) override;
+
+ private:
+  // Called from WebUI to request the current state.
+  void RequestDefaultBrowserState(const base::ListValue* args);
+
+  // Makes this the default browser. Called from WebUI.
+  void SetAsDefaultBrowser(const base::ListValue* args);
+
+  // Reference to a background worker that handles default browser settings.
+  scoped_refptr<ShellIntegration::DefaultBrowserWorker> default_browser_worker_;
+
+  // Policy setting to determine if default browser setting is managed.
+  BooleanPrefMember default_browser_policy_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultBrowserHandler);
+};
+
+}  // namespace settings
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
diff --git a/chrome/browser/ui/window_sizer/window_sizer_mac.mm b/chrome/browser/ui/window_sizer/window_sizer_mac.mm
index b73c2db..64d049f 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_mac.mm
+++ b/chrome/browser/ui/window_sizer/window_sizer_mac.mm
@@ -19,7 +19,7 @@
 gfx::Point WindowSizer::GetDefaultPopupOrigin(const gfx::Size& size,
                                               chrome::HostDesktopType type) {
   NSRect work_area = [[NSScreen mainScreen] visibleFrame];
-  NSRect main_area = [[[NSScreen screens] objectAtIndex:0] frame];
+  NSRect main_area = [[[NSScreen screens] firstObject] frame];
   NSPoint corner = NSMakePoint(NSMinX(work_area), NSMaxY(work_area));
 
   if (Browser* browser = chrome::FindLastActiveWithHostDesktopType(type)) {
diff --git a/chrome/chrome_android_paks.gypi b/chrome/chrome_android_paks.gypi
index d3a105c..1ebd363 100644
--- a/chrome/chrome_android_paks.gypi
+++ b/chrome/chrome_android_paks.gypi
@@ -87,14 +87,14 @@
         '<(DEPTH)/chrome/chrome_resources.gyp:packed_resources',
         '<(DEPTH)/chrome/chrome_resources.gyp:packed_extra_resources',
       ],
-      'copies': [
-        {
-          'destination': '<(chrome_android_pak_output_folder)',
-          'files': [
-            '<@(chrome_android_pak_input_resources)',
-          ],
-        }
-      ],
+      'variables': {
+        'dest_path': '<(chrome_android_pak_output_folder)',
+        'src_files': [
+          '<@(chrome_android_pak_input_resources)',
+        ],
+        'clear': 1,
+      },
+      'includes': ['../build/android/copy_ex.gypi'],
     },
   ],
 }
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 471be68..c16a2917 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -30,6 +30,7 @@
       'browser/android/accessibility_util.h',
       'browser/android/activity_type_ids.cc',
       'browser/android/activity_type_ids.h',
+      'browser/android/android_theme_resources.h',
       'browser/android/appmenu/app_menu_drag_helper.cc',
       'browser/android/appmenu/app_menu_drag_helper.h',
       'browser/android/banners/app_banner_data_fetcher_android.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 0f46be5..ce1ee3ab0 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1935,6 +1935,8 @@
       'browser/ui/webui/settings/md_settings_ui.h',
       'browser/ui/webui/settings/settings_clear_browsing_data_handler.cc',
       'browser/ui/webui/settings/settings_clear_browsing_data_handler.h',
+      'browser/ui/webui/settings/settings_default_browser_handler.cc',
+      'browser/ui/webui/settings/settings_default_browser_handler.h',
       'browser/ui/webui/settings/settings_startup_pages_handler.cc',
       'browser/ui/webui/settings/settings_startup_pages_handler.h',
       'browser/ui/webui/signin/inline_login_handler.cc',
diff --git a/chrome/chrome_repack_resources.gypi b/chrome/chrome_repack_resources.gypi
index cea4203..8b5b3df 100644
--- a/chrome/chrome_repack_resources.gypi
+++ b/chrome/chrome_repack_resources.gypi
@@ -21,6 +21,15 @@
     ],
     'pak_output': '<(SHARED_INTERMEDIATE_DIR)/repack/resources.pak',
     'conditions': [
+      ['branding=="Chrome"', {
+        'pak_inputs': [
+          '<(grit_out_dir)/settings_google_chrome_strings.pak',
+        ],
+      }, {
+        'pak_inputs': [
+          '<(grit_out_dir)/settings_chromium_strings.pak',
+        ],
+      }],
       ['chromeos==1', {
         'pak_inputs': [
           '<(SHARED_INTERMEDIATE_DIR)/ui/file_manager/file_manager_resources.pak',
diff --git a/chrome/chrome_resources.gyp b/chrome/chrome_resources.gyp
index 041e51d..492536ca 100644
--- a/chrome/chrome_resources.gyp
+++ b/chrome/chrome_resources.gyp
@@ -278,6 +278,22 @@
           },
           'includes': [ '../build/grit_action.gypi' ],
         },
+        {
+          # GN version: //chrome/app:settings_chromium_strings
+          'action_name': 'generate_settings_chromium_strings',
+          'variables': {
+            'grit_grd_file': 'app/settings_chromium_strings.grd',
+          },
+          'includes': [ '../build/grit_action.gypi' ],
+        },
+        {
+          # GN version: //chrome/app:settings_google_chrome_strings
+          'action_name': 'generate_settings_google_chrome_strings',
+          'variables': {
+            'grit_grd_file': 'app/settings_google_chrome_strings.grd',
+          },
+          'includes': [ '../build/grit_action.gypi' ],
+        },
       ],
     },
     {
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 3a27481..672373dc 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -328,7 +328,7 @@
       'browser/media/chrome_webrtc_webcam_browsertest.cc',
       'browser/media/defer_background_media_browsertest.cc',
       'browser/media/encrypted_media_browsertest.cc',
-      'browser/media/encrypted_media_istypesupported_browsertest.cc',
+      'browser/media/encrypted_media_supported_types_browsertest.cc',
       'browser/media/media_browsertest.cc',
       'browser/media/media_browsertest.h',
       'browser/media/media_stream_devices_controller_browsertest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 27fec64..59b77d7 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -194,8 +194,6 @@
       'browser/profiles/profile_shortcut_manager_unittest_win.cc',
       'browser/push_messaging/push_messaging_app_identifier_unittest.cc',
       'browser/push_messaging/push_messaging_permission_context_unittest.cc',
-      'browser/renderer_context_menu/render_view_context_menu_test_util.cc',
-      'browser/renderer_context_menu/render_view_context_menu_test_util.h',
       'browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm',
       'browser/resources_util_unittest.cc',
       'browser/search/contextual_search_policy_handler_android_unittest.cc',
@@ -554,6 +552,8 @@
       'browser/permissions/permission_queue_controller_unittest.cc',
     ],
     'chrome_unit_tests_non_mobile_sources': [
+      'browser/renderer_context_menu/render_view_context_menu_test_util.cc',
+      'browser/renderer_context_menu/render_view_context_menu_test_util.h',
       'browser/ui/website_settings/mock_permission_bubble_view.cc',
       'browser/ui/website_settings/mock_permission_bubble_view.h',
     ],
@@ -1898,7 +1898,16 @@
           ]
         }],
         ['OS=="android"', {
+          'dependencies!': [
+            '../ui/message_center/message_center.gyp:message_center_test_support',
+          ],
           'sources!': [
+            'browser/download/test_download_shelf.cc',
+            'browser/download/test_download_shelf.h',
+            'browser/profile_resetter/profile_resetter_test_base.cc',
+            'browser/profile_resetter/profile_resetter_test_base.h',
+            'browser/sessions/session_restore_test_helper.cc',
+            'browser/sessions/session_restore_test_helper.h',
             'browser/sessions/session_service_test_helper.cc',
             'browser/sessions/session_service_test_helper.h',
             'browser/ui/exclusive_access/fullscreen_controller_state_test.cc',
@@ -1906,6 +1915,10 @@
             'browser/ui/exclusive_access/fullscreen_controller_state_tests.h',
             'browser/ui/exclusive_access/fullscreen_controller_test.cc',
             'browser/ui/exclusive_access/fullscreen_controller_test.h',
+            'browser/ui/test/test_confirm_bubble_model.cc',
+            'browser/ui/test/test_confirm_bubble_model.h',
+            'renderer/safe_browsing/mock_feature_extractor_clock.cc',
+            'renderer/safe_browsing/mock_feature_extractor_clock.h',
             'test/base/dialog_test_browser_window.cc',
             'test/base/dialog_test_browser_window.h',
             'test/base/test_browser_window.cc',
@@ -2916,15 +2929,15 @@
               'dependencies': [
                 '../v8/tools/gyp/v8.gyp:v8_external_snapshot',
               ],
-              'copies': [
-                {
-                'destination': '<(asset_location)',
-                  'files': [
-                    '<(PRODUCT_DIR)/natives_blob.bin',
-                    '<(PRODUCT_DIR)/snapshot_blob.bin',
-                  ],
-                },
-              ],
+              'variables': {
+                'dest_path': '<(asset_location)',
+                'src_files': [
+                  '<(PRODUCT_DIR)/natives_blob.bin',
+                  '<(PRODUCT_DIR)/snapshot_blob.bin',
+                ],
+                'clear': 1,
+              },
+              'includes': ['../build/android/copy_ex.gypi'],
             }],
           ],
           'includes': [ '../build/apk_test.gypi' ],
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index f44c566..f66b3d3f 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -2,18 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Defines all the command-line switches used by Chrome.
+// Defines the shared command-line switches used by code in the Chrome
+// directory that don't have anywhere more specific to go.
 
 #ifndef CHROME_COMMON_CHROME_SWITCHES_H_
 #define CHROME_COMMON_CHROME_SWITCHES_H_
 
 #include "build/build_config.h"
 
-#include "base/base_switches.h"
-#include "components/autofill/core/common/autofill_switches.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "components/password_manager/core/common/password_manager_switches.h"
-#include "components/signin/core/common/signin_switches.h"
+// Don't add more switch files here. This is linked into some places like the
+// installer where dependencies should be limited. Instead, have files
+// directly include your switch file.
+//
+// TODO(brettw) delete content_switches.h include and make callers include that
+// file manually if they need a content switch.
 #include "content/public/common/content_switches.h"
 
 namespace switches {
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index d172c464..3cd3cd4 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/common/crash_keys.h"
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/format_macros.h"
 #include "base/logging.h"
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index c8458ef9..0710441b 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -31,6 +31,7 @@
 #include <fstream>  // NOLINT
 #include <string>  // NOLINT
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/debugger.h"
diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc
index 9bb3464c..2974185 100644
--- a/chrome/common/service_process_util.cc
+++ b/chrome/common/service_process_util.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 3d2c2f5..dc76f432 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -215,4 +215,11 @@
       "media/mock_webrtc_logging_message_filter.h",
     ]
   }
+
+  if (is_android) {
+    sources -= [
+      "safe_browsing/mock_feature_extractor_clock.cc",
+      "safe_browsing/mock_feature_extractor_clock.h",
+    ]
+  }
 }
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index 6b126c5a6..0725a54 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/base_switches.h"
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/command_line.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3e159608..9d36c566 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -161,6 +161,7 @@
   }
 
   if (is_android) {
+    deps -= [ "//ui/message_center:test_support" ]
     sources -= [
       "base/dialog_test_browser_window.cc",
       "base/dialog_test_browser_window.h",
diff --git a/chrome/test/chromedriver/capabilities_unittest.cc b/chrome/test/chromedriver/capabilities_unittest.cc
index 9b8a4e0ef8..a1a3e7f 100644
--- a/chrome/test/chromedriver/capabilities_unittest.cc
+++ b/chrome/test/chromedriver/capabilities_unittest.cc
@@ -510,9 +510,9 @@
   ASSERT_EQ(1u, capabilities.switches.GetSize());
   ASSERT_TRUE(capabilities.switches.HasSwitch("user-agent"));
   ASSERT_EQ(
-      "Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 5 Build/JOP40D) "
-      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 "
-      "Mobile Safari/537.36",
+      "Mozilla/5.0 (Linux; Android 4.4.4; Nexus 5 Build/KTU84P) "
+      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.114 Mobile "
+      "Safari/537.36",
       capabilities.switches.GetSwitchValue("user-agent"));
 
   ASSERT_EQ(360, capabilities.device_metrics->width);
diff --git a/chrome/test/chromedriver/chrome/mobile_device.cc b/chrome/test/chromedriver/chrome/mobile_device.cc
index 26a8e75..faad5518 100644
--- a/chrome/test/chromedriver/chrome/mobile_device.cc
+++ b/chrome/test/chromedriver/chrome/mobile_device.cc
@@ -23,66 +23,49 @@
                   "could not parse mobile device list because " +
                   json_reader.GetErrorMessage());
 
-  base::ListValue* mobile_devices;
-  if (!devices_value->GetAsList(&mobile_devices))
-    return Status(kUnknownError, "malformed device metrics list");
+  base::DictionaryValue* mobile_devices;
+  if (!devices_value->GetAsDictionary(&mobile_devices))
+    return Status(kUnknownError, "malformed device metrics dictionary");
 
-  for (base::ListValue::iterator it = mobile_devices->begin();
-       it != mobile_devices->end();
-       ++it) {
-    base::DictionaryValue* device = NULL;
-    if (!(*it)->GetAsDictionary(&device)) {
-      return Status(kUnknownError,
-                    "malformed device in list: should be a dictionary");
-    }
+  base::DictionaryValue* device = NULL;
+  if (!mobile_devices->GetDictionary(device_name, &device))
+    return Status(kUnknownError, "must be a valid device");
 
-    if (device != NULL) {
-      std::string name;
-      if (!device->GetString("title", &name)) {
-        return Status(kUnknownError,
-                      "malformed device name: should be a string");
-      }
-      if (name != device_name)
-        continue;
-
-      scoped_ptr<MobileDevice> tmp_mobile_device(new MobileDevice());
-      std::string device_metrics_string;
-      if (!device->GetString("userAgent", &tmp_mobile_device->user_agent)) {
-        return Status(kUnknownError,
-                      "malformed device user agent: should be a string");
-      }
-      int width = 0;
-      int height = 0;
-      double device_scale_factor = 0.0;
-      bool touch = true;
-      bool mobile = true;
-      if (!device->GetInteger("width",  &width)) {
-        return Status(kUnknownError,
-                      "malformed device width: should be an integer");
-      }
-      if (!device->GetInteger("height", &height)) {
-        return Status(kUnknownError,
-                      "malformed device height: should be an integer");
-      }
-      if (!device->GetDouble("deviceScaleFactor", &device_scale_factor)) {
-        return Status(kUnknownError,
-                      "malformed device scale factor: should be a double");
-      }
-      if (!device->GetBoolean("touch", &touch)) {
-        return Status(kUnknownError,
-                      "malformed touch: should be a bool");
-      }
-      if (!device->GetBoolean("mobile", &mobile)) {
-        return Status(kUnknownError,
-                      "malformed mobile: should be a bool");
-      }
-      tmp_mobile_device->device_metrics.reset(
-          new DeviceMetrics(width, height, device_scale_factor, touch, mobile));
-
-      *mobile_device = tmp_mobile_device.Pass();
-      return Status(kOk);
-    }
+  scoped_ptr<MobileDevice> tmp_mobile_device(new MobileDevice());
+  std::string device_metrics_string;
+  if (!device->GetString("userAgent", &tmp_mobile_device->user_agent)) {
+    return Status(kUnknownError,
+                  "malformed device user agent: should be a string");
   }
+  int width = 0;
+  int height = 0;
+  double device_scale_factor = 0.0;
+  bool touch = true;
+  bool mobile = true;
+  if (!device->GetInteger("width",  &width)) {
+    return Status(kUnknownError,
+                  "malformed device width: should be an integer");
+  }
+  if (!device->GetInteger("height", &height)) {
+    return Status(kUnknownError,
+                  "malformed device height: should be an integer");
+  }
+  if (!device->GetDouble("deviceScaleFactor", &device_scale_factor)) {
+    return Status(kUnknownError,
+                  "malformed device scale factor: should be a double");
+  }
+  if (!device->GetBoolean("touch", &touch)) {
+    return Status(kUnknownError,
+                  "malformed touch: should be a bool");
+  }
+  if (!device->GetBoolean("mobile", &mobile)) {
+    return Status(kUnknownError,
+                  "malformed mobile: should be a bool");
+  }
+  tmp_mobile_device->device_metrics.reset(
+      new DeviceMetrics(width, height, device_scale_factor, touch, mobile));
 
-  return Status(kUnknownError, "must be a valid device");
+  *mobile_device = tmp_mobile_device.Pass();
+  return Status(kOk);
+
 }
diff --git a/chrome/test/chromedriver/chrome/mobile_device_list.cc b/chrome/test/chromedriver/chrome/mobile_device_list.cc
index aa4704b..13cd982 100644
--- a/chrome/test/chromedriver/chrome/mobile_device_list.cc
+++ b/chrome/test/chromedriver/chrome/mobile_device_list.cc
@@ -2,101 +2,102 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This file was generated at (2015-03-30 16:08:35.102812) by running:
+// This file was generated at (2015-10-05 13:42:15.387188) by running:
 //     chrome/test/chromedriver/embed_mobile_devices_in_cpp.py --directory
 //     chrome/test/chromedriver/chrome/
-//     third_party/WebKit/Source/devtools/front_end/toolbox/OverridesUI.js
+//     third_party/WebKit/Source/devtools/front_end/emulated_devices/module.json
 
 #include "chrome/test/chromedriver/chrome/mobile_device_list.h"
 
 const char kMobileDevices[] =
-    "[{\"title\": \"Apple iPhone 4\", \"width\": 320, \"height\": 480, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (iPhone; U; CPU "
-    "iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like "
-    "Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5\", \"touch\": true, "
-    "\"mobile\": true},{\"title\": \"Apple iPhone 5\", \"width\": 320, "
-    "\"height\": 568, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 "
-    "(iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 "
-    "(KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53\", "
-    "\"touch\": true, \"mobile\": true},{\"title\": \"Apple iPhone 6\", "
-    "\"width\": 375, \"height\": 667, \"deviceScaleFactor\": 2, \"userAgent\": "
-    "\"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) "
-    "AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d "
-    "Safari/600.1.4\", \"touch\": true, \"mobile\": true},{\"title\": \"Apple "
-    "iPhone 6 Plus\", \"width\": 414, \"height\": 736, \"deviceScaleFactor\": "
-    "3, \"userAgent\": \"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) "
-    "AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d "
-    "Safari/600.1.4\", \"touch\": true, \"mobile\": true},{\"title\": "
-    "\"BlackBerry Z30\", \"width\": 360, \"height\": 640, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (BB10; Touch) "
-    "AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile "
-    "Safari/537.10+\", \"touch\": true, \"mobile\": true},{\"title\": \"Google "
-    "Nexus 4\", \"width\": 384, \"height\": 640, \"deviceScaleFactor\": 2, "
-    "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 4 "
-    "Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 "
-    "Mobile Safari/537.36\", \"touch\": true, \"mobile\": true},{\"title\": "
-    "\"Google Nexus 5\", \"width\": 360, \"height\": 640, "
-    "\"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 (Linux; Android "
-    "4.4.4; en-us; Nexus 5 Build/JOP40D) AppleWebKit/537.36 (KHTML, like "
-    "Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, "
-    "\"mobile\": true},{\"title\": \"LG Optimus L70\", \"width\": 384, "
-    "\"height\": 640, \"deviceScaleFactor\": 1.25, \"userAgent\": "
-    "\"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 "
-    "Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 "
-    "Chrome/30.0.1599.103 Mobile Safari/537.36\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Nokia N9\", \"width\": 360, \"height\": 640, "
-    "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (MeeGo; NokiaN9) "
+    "{\"Laptop with touch\": {\"deviceScaleFactor\": 1, \"mobile\": false, "
+    "\"height\": 1280, \"width\": 950, \"touch\": true, \"userAgent\": \"\"}, "
+    "\"BlackBerry Z30\": {\"deviceScaleFactor\": 2, \"mobile\": true, "
+    "\"height\": 640, \"width\": 360, \"touch\": true, \"userAgent\": "
+    "\"Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) "
+    "Version/10.0.9.2372 Mobile Safari/537.10+\"}, \"Google Nexus 6\": "
+    "{\"deviceScaleFactor\": 3.5, \"mobile\": true, \"height\": 732, "
+    "\"width\": 412, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; "
+    "Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like "
+    "Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36\"}, \"Google Nexus 7\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 960, \"width\": "
+    "600, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; Android 4.3; "
+    "Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) "
+    "Chrome/42.0.2307.2 Safari/537.36\"}, \"Google Nexus 4\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 640, \"width\": "
+    "384, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.2; "
+    "Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) "
+    "Chrome/35.0.1916.122 Mobile Safari/537.36\"}, \"Google Nexus 5\": "
+    "{\"deviceScaleFactor\": 3, \"mobile\": true, \"height\": 640, \"width\": "
+    "360, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.4; "
+    "Nexus 5 Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) "
+    "Chrome/38.0.2125.114 Mobile Safari/537.36\"}, \"Apple iPad\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 1024, \"width\": "
+    "768, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (iPad; CPU OS 7_0 like "
+    "Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 "
+    "Mobile/11A465 Safari/9537.53\"}, \"Laptop with HiDPI screen\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": false, \"height\": 1440, "
+    "\"width\": 900, \"touch\": false, \"userAgent\": \"\"}, \"Samsung Galaxy "
+    "Note II\": {\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 640, "
+    "\"width\": 360, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; U; "
+    "Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, "
+    "like Gecko) Version/4.0 Mobile Safari/534.30\"}, \"Nokia N9\": "
+    "{\"deviceScaleFactor\": 1, \"mobile\": true, \"height\": 640, \"width\": "
+    "360, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (MeeGo; NokiaN9) "
     "AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile "
-    "Safari/534.13\", \"touch\": true, \"mobile\": true},{\"title\": \"Nokia "
-    "Lumia 520\", \"width\": 320, \"height\": 533, \"deviceScaleFactor\": 1.4, "
+    "Safari/534.13\"}, \"Samsung Galaxy S4\": {\"deviceScaleFactor\": 3, "
+    "\"mobile\": true, \"height\": 640, \"width\": 360, \"touch\": true, "
+    "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) "
+    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile "
+    "Safari/537.36\"}, \"Nokia Lumia 520\": {\"deviceScaleFactor\": 1.4, "
+    "\"mobile\": true, \"height\": 533, \"width\": 320, \"touch\": true, "
     "\"userAgent\": \"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; "
-    "Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)\", \"touch\": "
-    "true, \"mobile\": true},{\"title\": \"Samsung Galaxy S III\", \"width\": "
-    "360, \"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": "
-    "\"Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) "
-    "AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile "
-    "Safari/534.30\", \"touch\": true, \"mobile\": true},{\"title\": \"Samsung "
-    "Galaxy S4\", \"width\": 360, \"height\": 640, \"deviceScaleFactor\": 3, "
-    "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.2; GT-I9505 Build/JDQ39) "
-    "AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 "
-    "Mobile Safari/537.36\", \"touch\": true, \"mobile\": true},{\"title\": "
-    "\"Amazon Kindle Fire HDX\", \"width\": 2560, \"height\": 1600, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; U; en-us; "
-    "KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 "
-    "Safari/535.19 Silk-Accelerated=true\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Apple iPad Mini\", \"width\": 1024, \"height\": 768, "
-    "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (iPad; CPU OS "
-    "4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) "
-    "Version/5.0.2 Mobile/8L1 Safari/6533.18.5\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Apple iPad\", \"width\": 1024, \"height\": 768, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (iPad; CPU OS 7_0 "
-    "like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 "
-    "Mobile/11A465 Safari/9537.53\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"BlackBerry PlayBook\", \"width\": 1024, \"height\": "
-    "600, \"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (PlayBook; U; "
-    "RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) "
-    "Version/7.2.1.0 Safari/536.2+\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Google Nexus 10\", \"width\": 1280, \"height\": 800, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android "
-    "4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) "
-    "Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Google Nexus 7\", \"width\": 960, \"height\": 600, "
-    "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android "
-    "4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) "
-    "Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, \"mobile\": "
-    "true},{\"title\": \"Samsung Galaxy Note 3\", \"width\": 360, \"height\": "
-    "640, \"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 (Linux; U; "
+    "Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)\"}, "
+    "\"BlackBerry PlayBook\": {\"deviceScaleFactor\": 1, \"mobile\": true, "
+    "\"height\": 1024, \"width\": 600, \"touch\": true, \"userAgent\": "
+    "\"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) "
+    "AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+\"}, "
+    "\"Apple iPhone 5\": {\"deviceScaleFactor\": 2, \"mobile\": true, "
+    "\"height\": 568, \"width\": 320, \"touch\": true, \"userAgent\": "
+    "\"Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) "
+    "AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 "
+    "Safari/9537.53\"}, \"Apple iPhone 4\": {\"deviceScaleFactor\": 2, "
+    "\"mobile\": true, \"height\": 480, \"width\": 320, \"touch\": true, "
+    "\"userAgent\": \"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS "
+    "X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 "
+    "Mobile/8C148 Safari/6533.18.5\"}, \"Apple iPhone 6\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 667, \"width\": "
+    "375, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (iPhone; CPU iPhone OS "
+    "8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 "
+    "Mobile/12A4345d Safari/600.1.4\"}, \"LG Optimus L70\": "
+    "{\"deviceScaleFactor\": 1.25, \"mobile\": true, \"height\": 640, "
+    "\"width\": 384, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; U; "
+    "Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 "
+    "(KHTML, like Gecko) Version/4.0 Chrome/30.0.1599.103 Mobile "
+    "Safari/537.36\"}, \"Apple iPhone 6 Plus\": {\"deviceScaleFactor\": 3, "
+    "\"mobile\": true, \"height\": 736, \"width\": 414, \"touch\": true, "
+    "\"userAgent\": \"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) "
+    "AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d "
+    "Safari/600.1.4\"}, \"Apple iPad Mini\": {\"deviceScaleFactor\": 2, "
+    "\"mobile\": true, \"height\": 1024, \"width\": 768, \"touch\": true, "
+    "\"userAgent\": \"Mozilla/5.0 (iPad; CPU OS 7_0_4 like Mac OS X) "
+    "AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a "
+    "Safari/9537.53\"}, \"Amazon Kindle Fire HDX\": {\"deviceScaleFactor\": 2, "
+    "\"mobile\": true, \"height\": 2560, \"width\": 1600, \"touch\": true, "
+    "\"userAgent\": \"Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) "
+    "AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 "
+    "Silk-Accelerated=true\"}, \"Samsung Galaxy S III\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 640, \"width\": "
+    "360, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; U; Android "
+    "4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) "
+    "Version/4.0 Mobile Safari/534.30\"}, \"Laptop with MDPI screen\": "
+    "{\"deviceScaleFactor\": 1, \"mobile\": false, \"height\": 1280, "
+    "\"width\": 800, \"touch\": false, \"userAgent\": \"\"}, \"Samsung Galaxy "
+    "Note 3\": {\"deviceScaleFactor\": 3, \"mobile\": true, \"height\": 640, "
+    "\"width\": 360, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; U; "
     "Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, "
-    "like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, "
-    "\"mobile\": true},{\"title\": \"Samsung Galaxy Note II\", \"width\": 360, "
-    "\"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 "
-    "(Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 "
-    "(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, "
-    "\"mobile\": true},{\"title\": \"Laptop with touch\", \"width\": 1280, "
-    "\"height\": 950, \"deviceScaleFactor\": 1, \"userAgent\": \"\", "
-    "\"touch\": true, \"mobile\": false},{\"title\": \"Laptop with HiDPI "
-    "screen\", \"width\": 1440, \"height\": 900, \"deviceScaleFactor\": 2, "
-    "\"userAgent\": \"\", \"touch\": false, \"mobile\": false},{\"title\": "
-    "\"Laptop with MDPI screen\", \"width\": 1280, \"height\": 800, "
-    "\"deviceScaleFactor\": 1, \"userAgent\": \"\", \"touch\": false, "
-    "\"mobile\": false}]";
+    "like Gecko) Version/4.0 Mobile Safari/534.30\"}, \"Google Nexus 10\": "
+    "{\"deviceScaleFactor\": 2, \"mobile\": true, \"height\": 1280, \"width\": "
+    "800, \"touch\": true, \"userAgent\": \"Mozilla/5.0 (Linux; Android 4.3; "
+    "Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) "
+    "Chrome/42.0.2307.2 Safari/537.36\"}}";
diff --git a/chrome/test/chromedriver/chrome/mobile_device_list.h b/chrome/test/chromedriver/chrome/mobile_device_list.h
index e85e3a4..d11484f 100644
--- a/chrome/test/chromedriver/chrome/mobile_device_list.h
+++ b/chrome/test/chromedriver/chrome/mobile_device_list.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This file was generated at (2015-03-30 16:08:35.102812) by running:
+// This file was generated at (2015-10-05 13:42:15.387188) by running:
 //     chrome/test/chromedriver/embed_mobile_devices_in_cpp.py --directory
 //     chrome/test/chromedriver/chrome/
-//     third_party/WebKit/Source/devtools/front_end/toolbox/OverridesUI.js
+//     third_party/WebKit/Source/devtools/front_end/emulated_devices/module.json
 
 #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_DEVICE_LIST_H_
 #define CHROME_TEST_CHROMEDRIVER_CHROME_MOBILE_DEVICE_LIST_H_
diff --git a/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
index 9fe2ad41..58665fe 100755
--- a/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
+++ b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
@@ -5,12 +5,13 @@
 
 """Embeds standalone JavaScript snippets in C++ code.
 
-The script requires the devtools/front_end/toolbox/OverridesUI.js file from
-WebKit that lists the known mobile devices to be passed in as the only argument.
-The list of known devices will be written to a C-style string to be parsed with
-JSONReader.
+The script requires the Source/devtools/front_end/emulated_devices/module.json
+file from Blink that lists the known mobile devices to be passed in as the only
+argument.  The list of known devices will be written to a C-style string to be
+parsed with JSONReader.
 """
 
+import json
 import optparse
 import re
 import subprocess
@@ -19,18 +20,6 @@
 import cpp_source
 
 
-def quotizeKeys(s, keys):
-  """Returns the string |s| with each instance of each key wrapped in quotes.
-
-  Args:
-    s: a string containing keys that need to be wrapped in quotes.
-    keys: an iterable of keys to be wrapped in quotes in the string.
-  """
-  for key in keys:
-    s = re.sub('%s: ' % key, '"%s": ' % key, s)
-  return s
-
-
 def main():
   parser = optparse.OptionParser()
   parser.add_option(
@@ -38,34 +27,29 @@
       help='Path to directory where the cc/h files should be created')
   options, args = parser.parse_args()
 
-  devices = '['
+  devices = {}
   file_name = args[0]
   inside_list = False
   with open(file_name, 'r') as f:
-    for line in f:
-      if not inside_list:
-        if 'WebInspector.OverridesUI._phones = [' in line:
-          inside_list = True
-        if 'WebInspector.OverridesUI._tablets = [' in line:
-          devices += ','
-          inside_list = True
-        if 'WebInspector.OverridesUI._notebooks = [' in line:
-          devices += ','
-          inside_list = True
-      else:
-        if line.strip() == '];':
-          inside_list = False
-          continue
-        devices += line.strip()
+    emulated_devices = json.load(f)
+  extensions = emulated_devices['extensions']
+  for extension in extensions:
+    if extension['type'] == 'emulated-device':
+      device = extension['device']
+      devices[device['title']] = {
+        'userAgent': device['user-agent'],
+        'width': device['screen']['vertical']['width'],
+        'height': device['screen']['vertical']['height'],
+        'deviceScaleFactor': device['screen']['device-pixel-ratio'],
+        'touch': 'touch' in device['capabilities'],
+        'mobile': 'mobile' in device['capabilities'],
+      }
 
   output_dir = 'chrome/test/chromedriver/chrome'
-  devices += ']'
-  devices = quotizeKeys(devices,
-                        ['title', 'width', 'height', 'deviceScaleFactor',
-                         'userAgent', 'touch', 'mobile'])
   cpp_source.WriteSource('mobile_device_list',
                          output_dir,
-                         options.directory, {'kMobileDevices': devices})
+                         options.directory,
+                         {'kMobileDevices': json.dumps(devices)})
 
   clang_format = ['clang-format', '-i']
   subprocess.Popen(clang_format + ['%s/mobile_device_list.cc' % output_dir])
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index d84d3ed..fbbc3f7 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -1447,8 +1447,8 @@
     self.assertEqual(640, driver.ExecuteScript('return window.screen.height'))
     body_tag = driver.FindElement('tag name', 'body')
     self.assertEqual(
-        'Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 5 Build/JOP40D) '
-        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile '
+        'Mozilla/5.0 (Linux; Android 4.4.4; Nexus 5 Build/KTU84P) '
+        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.114 Mobile '
         'Safari/537.36',
         body_tag.GetText())
 
diff --git a/chrome/test/data/webui/media_router/media_router_container_tests.js b/chrome/test/data/webui/media_router/media_router_container_tests.js
index c337d1ba..20b8a46 100644
--- a/chrome/test/data/webui/media_router/media_router_container_tests.js
+++ b/chrome/test/data/webui/media_router/media_router_container_tests.js
@@ -160,6 +160,9 @@
             'issue id 2', 'Issue Title 2', 'Issue Message 2', 0, 1,
             'route id 2', false, 1234);
 
+        container.initializeCastModes(
+            fakeCastModeList, fakeCastModeList[1].type);
+
         // Allow for the media router container to be created and attached.
         setTimeout(done);
       });
@@ -176,21 +179,20 @@
       // Tests for 'create-route' event firing when a sink with no associated
       // route is clicked.
       test('select sink without a route', function(done) {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
 
         setTimeout(function() {
           var sinkList =
               container.$['sink-list'].querySelectorAll('paper-item');
-
           container.addEventListener('create-route', function(data) {
             assertEquals(fakeSinkList[2].id, data.detail.sinkId);
             assertEquals(container.selectedCastModeValue_,
                 data.detail.selectedCastModeValue);
             done();
           });
-
           // Tap on a sink without a route, which should fire a 'create-route'
           // event.
+          assertEquals(fakeSinkList.length, sinkList.length);
           MockInteractions.tap(sinkList[2]);
         });
       });
@@ -198,7 +200,7 @@
       // Tests that selecting a sink with an associated route will make the
       // |container| switch to ROUTE_DETAILS view.
       test('select sink with a route', function(done) {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteList;
 
         setTimeout(function() {
@@ -217,7 +219,7 @@
       // Tests that |container| returns to SINK_LIST view and arrow drop icon
       // toggles after a cast mode is selected.
       test('select cast mode', function(done) {
-        container.castModeList = fakeCastModeListWithNonDefaultModesOnly;
+        container.castModeList_ = fakeCastModeListWithNonDefaultModesOnly;
 
         MockInteractions.tap(container.$['arrow-drop-icon']);
         checkArrowDropIcon(container.CONTAINER_VIEW_.CAST_MODE_LIST);
@@ -265,21 +267,16 @@
       test('header text with no default cast modes', function(done) {
         checkElementTextWithId(loadTimeData.getString('selectCastModeHeader'),
             'cast-mode-header-text');
+        checkElementTextWithId(fakeCastModeList[1].description,
+            'sink-list-header-text');
 
-        var fakeHeaderText = 'fake header text';
-        container.headerText = fakeHeaderText;
-        checkElementTextWithId(fakeHeaderText, 'sink-list-header-text');
-
-        // Set the cast mode list to update the header text when one is
-        // selected.
-        container.castModeList = fakeCastModeListWithNonDefaultModesOnly;
-
+        container.castModeList_ = fakeCastModeListWithNonDefaultModesOnly;
         setTimeout(function() {
           var castModeList =
               container.$['cast-mode-list'].querySelectorAll('paper-item');
-
-          for (var i = 0; i < fakeCastModeListWithNonDefaultModesOnly.length;
-              i++) {
+          assertEquals(fakeCastModeListWithNonDefaultModesOnly.length,
+              castModeList.length);
+          for (var i = 0; i < castModeList.length; i++) {
             MockInteractions.tap(castModeList[i]);
             checkElementTextWithId(
                 fakeCastModeListWithNonDefaultModesOnly[i].description,
@@ -296,7 +293,7 @@
       // Tests the header text when updated with a cast mode list with a mix of
       // default and non-default cast modes.
       test('cast modes with one default mode', function(done) {
-        container.castModeList = fakeCastModeList;
+        container.castModeList_ = fakeCastModeList;
 
         setTimeout(function() {
           var castModeList =
@@ -323,12 +320,12 @@
 
       // Tests that text shown for each sink matches their names.
       test('sink list text', function(done) {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
 
         setTimeout(function() {
           var sinkList =
               container.$['sink-list'].querySelectorAll('paper-item');
-
+          assertEquals(fakeSinkList.length, sinkList.length);
           for (var i = 0; i < fakeSinkList.length; i++) {
             checkElementText(fakeSinkList[i].name, sinkList[i]);
           }
@@ -338,13 +335,13 @@
 
       // Tests the text shown for the sink list.
       test('initial sink list route text', function(done) {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteList;
 
         setTimeout(function() {
           var routeList =
               container.$['sink-list'].querySelectorAll('.route');
-
+          assertEquals(fakeSinkList.length, routeList.length);
           checkElementText(fakeRouteList[0].description, routeList[0]);
           checkElementText(fakeRouteList[1].description, routeList[1]);
           checkElementText('', routeList[2]);
@@ -354,7 +351,7 @@
 
       // Tests the visibility of routes in the sink list.
       test('initial route visibility', function(done) {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteList;
 
         setTimeout(function() {
@@ -371,7 +368,7 @@
       // Tests the expected view when there is only one local active route and
       // media_router_container is created for the first time.
       test('initial view with one local route', function() {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteList;
 
         checkCurrentView(container.CONTAINER_VIEW_.ROUTE_DETAILS);
@@ -380,7 +377,7 @@
       // Tests the expected view when there are multiple local active routes
       // and media_router_container is created for the first time.
       test('initial view with multiple local routes', function() {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteListWithLocalRoutesOnly;
 
         checkCurrentView(container.CONTAINER_VIEW_.SINK_LIST);
@@ -389,7 +386,7 @@
       // Tests the expected view when there are no local active routes and
       // media_router_container is created for the first time.
       test('initial view with no local route', function() {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = [];
 
         checkCurrentView(container.CONTAINER_VIEW_.SINK_LIST);
@@ -398,7 +395,7 @@
       // Tests the expected view when there are no local active routes and
       // media_router_container is created for the first time.
       test('view after route is closed remotely', function() {
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         container.routeList = fakeRouteList;
         checkCurrentView(container.CONTAINER_VIEW_.ROUTE_DETAILS);
 
@@ -414,7 +411,6 @@
                                     'container-header',
                                     'device-missing',
                                     'sink-list']);
-
         // Set a non-blocking issue. The issue should stay hidden.
         container.issue = fakeNonBlockingIssue;
         checkElementsVisibleWithId(['cast-mode-header-text',
@@ -464,7 +460,7 @@
                                     'sink-list-view']);
 
         // Set an non-empty sink list.
-        container.sinkList = fakeSinkList;
+        container.allSinks = fakeSinkList;
         checkElementsVisibleWithId(['container-header',
                                     'sink-list',
                                     'sink-list-header-text',
@@ -483,6 +479,81 @@
         container.issue = fakeBlockingIssue;
         checkElementsVisibleWithId(['issue-banner', 'sink-list']);
       });
+
+      // Tests that the sink list does not contain any sinks that are not
+      // compatible with the current cast mode and are not associated with a
+      // route.
+      test('sink list filtering based on initial cast mode', function(done) {
+        var newSinks = [
+          new media_router.Sink('sink id 10', 'Sink 10',
+              media_router.SinkIconType.CAST,
+              media_router.SinkStatus.ACTIVE, [2, 3]),
+          new media_router.Sink('sink id 20', 'Sink 20',
+              media_router.SinkIconType.CAST,
+              media_router.SinkStatus.ACTIVE, [1, 2, 3]),
+          new media_router.Sink('sink id 30', 'Sink 30',
+              media_router.SinkIconType.CAST,
+              media_router.SinkStatus.PENDING, [2, 3]),
+        ];
+
+        container.allSinks = newSinks;
+        container.routeList = [
+          new media_router.Route('id 1', 'sink id 30', 'Title 1', 1, false),
+        ];
+
+        setTimeout(function() {
+          var sinkList =
+              container.$['sink-list'].querySelectorAll('paper-item');
+
+          // newSinks[0] got filtered out since it is not compatible with cast
+          // mode 1.
+          // 'Sink 20' should be on the list because it contains the selected
+          // cast mode. (sinkList[0] = newSinks[1])
+          // 'Sink 30' should be on the list because it has a route
+          // (sinkList[1] = newSinks[2])
+          assertEquals(2, sinkList.length);
+
+          checkElementText(newSinks[1].name, sinkList[0]);
+
+          // |sinkList[1]| contains route title in addition to sink name.
+          assertTrue(sinkList[1].textContent.trim().startsWith(
+              newSinks[2].name.trim()));
+
+          done();
+        });
+      });
+
+      // Tests that after a different cast mode is selected, the sink list will
+      // change based on the sinks compatibility with the new cast mode.
+      test('changing cast mode changes sink list', function(done) {
+        container.allSinks = fakeSinkList;
+
+        var castModeList =
+              container.$['cast-mode-list'].querySelectorAll('paper-item');
+        MockInteractions.tap(castModeList[0]);
+        checkElementTextWithId(
+            fakeCastModeList[0].description, 'sink-list-header-text');
+
+        setTimeout(function() {
+          var sinkList =
+              container.$['sink-list'].querySelectorAll('paper-item');
+
+          // The sink list is empty because none of the sinks in fakeSinkList
+          // is compatible with cast mode 0.
+          assertEquals(0, sinkList.length);
+          MockInteractions.tap(castModeList[2]);
+          checkElementTextWithId(
+              fakeCastModeList[2].description, 'sink-list-header-text');
+
+          setTimeout(function() {
+            var sinkList =
+                container.$['sink-list'].querySelectorAll('paper-item');
+
+            assertEquals(3, sinkList.length);
+            done();
+          });
+         });
+      });
     });
   }
 
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index e2ac5c5..a1976a2 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -38,7 +38,7 @@
 TEST_F('CrSettingsBrowserTest', 'DISABLED_CrSettingsTest', function() {
   // Register mocha tests for each element.
   settings_checkbox.registerTests();
-  cr_settings_prefs.registerTests();
+  settings_prefs.registerTests();
 
   // Run all registered tests.
   mocha.run();
diff --git a/chrome/test/data/webui/settings/prefs_tests.js b/chrome/test/data/webui/settings/prefs_tests.js
index b5a3d92..99eeaf6 100644
--- a/chrome/test/data/webui/settings/prefs_tests.js
+++ b/chrome/test/data/webui/settings/prefs_tests.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @fileoverview Suite of tests for cr-settings-prefs. */
-cr.define('cr_settings_prefs', function() {
+/** @fileoverview Suite of tests for settings-prefs. */
+cr.define('settings_prefs', function() {
   /**
    * Creates a deep copy of the object.
    * @param {!Object} obj
@@ -144,7 +144,7 @@
       var mockApi = null;
 
       /**
-       * @param {!Object} prefStore Pref store from <cr-settings-prefs>.
+       * @param {!Object} prefStore Pref store from <settings-prefs>.
        * @param {string} key Pref key of the pref to return.
        * @return {chrome.settingsPrivate.PrefObject|undefined}
        */
@@ -174,7 +174,7 @@
       }
 
       /**
-       * Checks that the <cr-settings-prefs> contains the expected values.
+       * Checks that the <settings-prefs> contains the expected values.
        * @param {number} testCaseValueIndex The index of possible values from
        *     the test case to check.
        */
@@ -194,17 +194,17 @@
        */
       var createdElements = [];
 
-      // Initialize <cr-settings-prefs> elements before each test.
+      // Initialize <settings-prefs> elements before each test.
       setup(function() {
         mockApi = new MockSettingsApi();
         // TODO(michaelpg): don't use global variables to inject the API.
         window.mockApi = mockApi;
 
-        // Create and attach the <cr-settings-prefs> elements. Make several of
+        // Create and attach the <settings-prefs> elements. Make several of
         // them to test that the shared state model scales correctly.
         createdElements = [];
         for (var i = 0; i < 100; i++) {
-          var prefsInstance = document.createElement('cr-settings-prefs');
+          var prefsInstance = document.createElement('settings-prefs');
           document.body.appendChild(prefsInstance);
           createdElements.push(prefsInstance);
         }
@@ -219,7 +219,7 @@
       teardown(function() {
         CrSettingsPrefs.resetForTesting();
 
-        // Reset each <cr-settings-prefs>.
+        // Reset each <settings-prefs>.
         for (var i = 0; i < createdElements.length; i++)
           createdElements[i].resetForTesting();
 
@@ -244,7 +244,7 @@
       });
 
       test('forwards pref changes to API', function() {
-        // Test that cr-settings-prefs uses the setPref API.
+        // Test that settings-prefs uses the setPref API.
         for (var testCase of prefsTestCases) {
           prefs.set('prefs.' + testCase.key + '.value',
                     deepCopy(testCase.values[1]));
diff --git a/chrome/test/media_router/media_router_integration_ui_browsertest.cc b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
index d4f50ed..66025623 100644
--- a/chrome/test/media_router/media_router_integration_ui_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
@@ -27,7 +27,7 @@
   std::string sink_length_script = base::StringPrintf(
       "domAutomationController.send("
       "window.document.getElementById('media-router-container')."
-      "sinkList.length)");
+      "sinksToShow_.length)");
   ASSERT_EQ(2, ExecuteScriptAndExtractInt(dialog_contents, sink_length_script));
 
   ChooseSink(web_contents, kTestSinkName);
diff --git a/cloud_print/service/win/service_controller.cc b/cloud_print/service/win/service_controller.cc
index 102e998..def8f1a 100644
--- a/cloud_print/service/win/service_controller.cc
+++ b/cloud_print/service/win/service_controller.cc
@@ -8,6 +8,7 @@
 #include <atlcom.h>
 #include <atlctl.h>
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/cloud_print/service/win/service_utils.cc b/cloud_print/service/win/service_utils.cc
index e64b5cb..279ff9e 100644
--- a/cloud_print/service/win/service_utils.cc
+++ b/cloud_print/service/win/service_utils.cc
@@ -3,15 +3,16 @@
 // found in the LICENSE file.
 
 #include "cloud_print/service/win/service_utils.h"
-#include "google_apis/gaia/gaia_switches.h"
 
 #include <windows.h>
 #include <security.h>  // NOLINT
 
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
+#include "google_apis/gaia/gaia_switches.h"
 
 base::string16 GetLocalComputerName() {
   DWORD size = 0;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 16f8006..f5d6c8b2 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -310,6 +310,7 @@
     "//components/leveldb_proto:unit_tests",
     "//components/suggestions:unit_tests",
     "//components/omnibox/browser:unit_tests",
+    "//components/sessions:test_support",
   ]
 
   if (!is_ios) {
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 247b5f03..7e2f83ad 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -223,7 +223,8 @@
 
   if (is_same_page_navigation) {
     OnSamePageNavigationCompleted();
-  } else if (is_new_navigation) {
+  } else {
+    // Navigation to a new page or a page refresh.
     form_cache_.Reset();
     submitted_forms_.clear();
     last_interacted_form_.reset();
@@ -627,8 +628,8 @@
   render_frame()->GetWebFrame()->document().forms(forms);
   for (size_t i = 0; i < forms.size(); ++i) {
     const blink::WebFormElement& form = forms[i];
-    if (GetCanonicalActionForForm(last_interacted_form_) ==
-            GetCanonicalActionForForm(form) &&
+    if (form_util::GetCanonicalActionForForm(last_interacted_form_) ==
+            form_util::GetCanonicalActionForForm(form) &&
         IsWebNodeVisible(form)) {
       // Found our candidate form, and it is still visible.
       return;
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index ea4fe67..9f1d765 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -52,6 +52,9 @@
 using blink::WebVector;
 
 namespace autofill {
+
+const size_t kMaxParseableFields = 200;
+
 namespace {
 
 // A bit field mask for FillForm functions to not fill some fields.
@@ -1075,9 +1078,99 @@
                                    control_elements, extract_mask, form, field);
 }
 
+GURL StripAuthAndParams(const GURL& gurl) {
+  // We want to keep the path but strip any authentication data, as well as
+  // query and ref portions of URL, for the form action and form origin.
+  GURL::Replacements rep;
+  rep.ClearUsername();
+  rep.ClearPassword();
+  rep.ClearQuery();
+  rep.ClearRef();
+  return gurl.ReplaceComponents(rep);
+}
+
 }  // namespace
 
-const size_t kMaxParseableFields = 200;
+namespace form_util {
+
+bool IsNamedElementVisible(
+    const std::vector<blink::WebFormControlElement>& control_elements,
+    const base::string16& name) {
+  for (size_t i = 0; i < control_elements.size(); ++i) {
+    if (control_elements[i].nameForAutofill() == name) {
+      return IsWebNodeVisible(control_elements[i]);
+    }
+  }
+  return false;
+}
+
+bool IsFormVisible(blink::WebFrame* frame,
+                   const GURL& action,
+                   const GURL& origin,
+                   const FormData& form_data,
+                   const FormsPredictionsMap& form_predictions) {
+  const GURL frame_url = GURL(frame->document().url().string().utf8());
+  blink::WebVector<blink::WebFormElement> forms;
+  frame->document().forms(forms);
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+  const bool action_is_empty = action == origin;
+#endif
+
+  // Since empty or unspecified action fields are automatically set to page URL,
+  // action field for forms cannot be used for comparing (all forms with
+  // empty/unspecified actions have the same value). If an action field is set
+  // to the page URL, this method checks ALL fields of the form instead (using
+  // FormData.SameFormAs). This is also true if the action was set to the page
+  // URL on purpose.
+  for (size_t i = 0; i < forms.size(); ++i) {
+    const blink::WebFormElement& form = forms[i];
+    if (!IsWebNodeVisible(form))
+      continue;
+
+    GURL canonical_action = GetCanonicalActionForForm(form);
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+    bool form_action_is_empty = canonical_action == frame_url;
+
+    if (action_is_empty != form_action_is_empty)
+      continue;
+
+    if (action_is_empty) {  // Both actions are empty, compare all fields.
+      FormData extracted_form_data;
+      WebFormElementToFormData(form, blink::WebFormControlElement(),
+                               EXTRACT_NONE, &extracted_form_data, nullptr);
+      if (form_data.SameFormAs(extracted_form_data)) {
+        return true;  // Form still exists.
+      }
+    } else {  // Both actions are non-empty, compare actions only.
+      if (action == canonical_action) {
+        return true;  // Form still exists.
+      }
+    }
+#else  // OS_MACOSX or OS_ANDROID
+    if (action == canonical_action) {
+      return true;  // Form still exists.
+    }
+#endif
+  }
+
+  return false;
+}
+
+GURL GetCanonicalActionForForm(const WebFormElement& form) {
+  WebString action = form.action();
+  if (action.isNull())
+    action = WebString("");  // missing 'action' attribute implies current URL.
+  GURL full_action(form.document().completeURL(action));
+  return StripAuthAndParams(full_action);
+}
+
+GURL GetCanonicalOriginForDocument(const WebDocument& document) {
+  GURL full_origin(document.url());
+  return StripAuthAndParams(full_origin);
+}
+
+}  // namespace form_util
 
 bool IsMonthInput(const WebInputElement* element) {
   CR_DEFINE_STATIC_LOCAL(WebString, kMonth, ("month"));
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 391551ea..9ba7efb 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -9,6 +9,7 @@
 
 #include "base/strings/string16.h"
 #include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebElementCollection.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -53,6 +54,36 @@
 // Copied to components/autofill/ios/browser/resources/autofill_controller.js.
 extern const size_t kMaxParseableFields;
 
+namespace form_util {
+// TODO(mathp): Move more functions in the form_util namespace.
+
+// Returns true if |control_elements| contains an element named |name| and is
+// visible.
+bool IsNamedElementVisible(
+    const std::vector<blink::WebFormControlElement>& control_elements,
+    const base::string16& name);
+
+// Helper function to check if there exist any form on |frame| where its action
+// equals |action|. Returns true if so. For forms with empty or unspecified
+// actions, all form data are used for comparison. Form data comparison is
+// disabled on Mac and Android because the update prompt isn't implemented.
+// It may cause many false password updates.
+// TODO(kolos) Turn on all data comparing when the update prompt will be
+// implemented on Mac and Android.
+bool IsFormVisible(blink::WebFrame* frame,
+                   const GURL& action,
+                   const GURL& origin,
+                   const FormData& form_data,
+                   const FormsPredictionsMap& form_predictions);
+
+// Helper functions to assist in getting the canonical form of the action and
+// origin. The action will proplerly take into account <BASE>, and both will
+// strip unnecessary data (e.g. query params and HTTP credentials).
+GURL GetCanonicalActionForForm(const blink::WebFormElement& form);
+GURL GetCanonicalOriginForDocument(const blink::WebDocument& document);
+
+}  // namespace form_util
+
 // Returns true if |element| is a month input element.
 bool IsMonthInput(const blink::WebInputElement* element);
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 9e6df579..d515592 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -73,19 +73,6 @@
           !fill_data.username_field.value.empty());
 }
 
-// Returns true if |control_elements| contains an element named |name| and is
-// visible.
-bool IsNamedElementVisible(
-    const std::vector<blink::WebFormControlElement>& control_elements,
-    const base::string16& name) {
-  for (const blink::WebFormControlElement& control_element : control_elements) {
-    if (control_element.nameForAutofill() == name) {
-      return IsWebNodeVisible(control_element);
-    }
-  }
-  return false;
-}
-
 // Returns true if password form has username and password fields with either
 // same or no name and id attributes supplied.
 bool DoesFormContainAmbiguousOrEmptyNames(
@@ -132,6 +119,37 @@
              : field.name;
 }
 
+bool IsUnownedPasswordFormVisible(blink::WebFrame* frame,
+                                  const GURL& action,
+                                  const GURL& origin,
+                                  const FormData& form_data,
+                                  const FormsPredictionsMap& form_predictions) {
+  scoped_ptr<PasswordForm> unowned_password_form(
+      CreatePasswordFormFromUnownedInputElements(*frame, nullptr,
+                                                 &form_predictions));
+  std::vector<blink::WebFormControlElement> control_elements =
+      GetUnownedAutofillableFormFieldElements(frame->document().all(), nullptr);
+  if (unowned_password_form &&
+      form_util::IsNamedElementVisible(
+          control_elements, unowned_password_form->username_element) &&
+      form_util::IsNamedElementVisible(
+          control_elements, unowned_password_form->password_element)) {
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+    const bool action_is_empty = action == origin;
+    bool forms_are_same =
+        action_is_empty ? form_data.SameFormAs(unowned_password_form->form_data)
+                        : action == unowned_password_form->action;
+    if (forms_are_same)
+      return true;  // Form still exists.
+#else               // OS_MACOSX or OS_ANDROID
+    if (action == unowned_password_form->action)
+      return true;  // Form still exists.
+#endif
+  }
+
+  return false;
+}
+
 // Utility function to find the unique entry of |control_elements| for the
 // specified input |field|. On successful find, adds it to |result| and returns
 // |true|. Otherwise clears the references from each |HTMLInputElement| from
@@ -267,7 +285,7 @@
   if (!doc.isHTMLDocument())
     return;
 
-  if (data.origin != GetCanonicalOriginForDocument(doc))
+  if (data.origin != form_util::GetCanonicalOriginForDocument(doc))
     return;
 
   blink::WebVector<blink::WebFormElement> forms;
@@ -277,7 +295,7 @@
     blink::WebFormElement fe = forms[i];
 
     // Action URL must match.
-    if (data.action != GetCanonicalActionForForm(fe))
+    if (data.action != form_util::GetCanonicalActionForForm(fe))
       continue;
 
     std::vector<blink::WebFormControlElement> control_elements =
@@ -596,91 +614,6 @@
   return it != map.end() && it->second.get();
 }
 
-
-// Helper function to check if there exist any form on |frame| where its action
-// equals |saved form|'s action or input elements outside a <form> tag if the
-// action equals the current url. Return true if so.
-// For forms with empty or unspecified actions, all form data are used for
-// comparing. All data comparing is disabled on Mac and Android because the
-// update prompt isn't implemented. It may cause many false password updates.
-// TODO(kolos) Turn on all data comparing when the update prompt will be
-// implemented on Mac and Android.
-bool IsFormVisible(blink::WebFrame* frame,
-                   const PasswordForm& saved_form,
-                   const FormsPredictionsMap& form_predictions) {
-  const GURL frame_url = GURL(frame->document().url().string().utf8());
-  blink::WebVector<blink::WebFormElement> forms;
-  frame->document().forms(forms);
-
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
-  const bool saved_form_action_is_empty =
-      saved_form.action == saved_form.origin;
-#endif
-
-  // Since empty or unspecified action fields are automatically set to page URL,
-  // action field for forms cannot be used for comparing (all forms with
-  // empty/unspecified actions have the same value). If an action field is set
-  // to the page URL, this method checks ALL fields of the form instead (using
-  // FormData.SameFormAs). This is also true if the action was set to the page
-  // URL on purpose.
-  for (size_t i = 0; i < forms.size(); ++i) {
-    const blink::WebFormElement& form = forms[i];
-    if (!IsWebNodeVisible(form))
-      continue;
-
-    GURL canonical_action = GetCanonicalActionForForm(form);
-
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
-    bool action_is_empty = canonical_action == frame_url;
-
-    if (saved_form_action_is_empty != action_is_empty)
-      continue;
-
-    if (action_is_empty) {  // Both actions are empty, compare all fields.
-      FormData form_data;
-      WebFormElementToFormData(form, blink::WebFormControlElement(),
-                               EXTRACT_NONE, &form_data, nullptr);
-      if (saved_form.form_data.SameFormAs(form_data)) {
-        return true;  // Form still exists.
-      }
-    } else {  // Both actions are non-empty, compare actions only.
-      if (saved_form.action == canonical_action) {
-        return true;  // Form still exists.
-      }
-    }
-#else  // OS_MACOSX or OS_ANDROID
-    if (saved_form.action == canonical_action) {
-      return true;  // Form still exists.
-    }
-#endif
-  }
-
-  scoped_ptr<PasswordForm> unowned_password_form(
-      CreatePasswordFormFromUnownedInputElements(*frame, nullptr,
-                                                 &form_predictions));
-  std::vector<blink::WebFormControlElement> control_elements =
-      GetUnownedAutofillableFormFieldElements(frame->document().all(), nullptr);
-  if (unowned_password_form &&
-      IsNamedElementVisible(control_elements,
-                            unowned_password_form->username_element) &&
-      IsNamedElementVisible(control_elements,
-                            unowned_password_form->password_element)) {
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
-    bool forms_are_same =
-        saved_form_action_is_empty
-            ? saved_form.form_data.SameFormAs(unowned_password_form->form_data)
-            : saved_form.action == unowned_password_form->action;
-    if (forms_are_same)
-      return true;  // Form still exists.
-#else               // OS_MACOSX or OS_ANDROID
-    if (saved_form.action == unowned_password_form->action)
-      return true;  // Form still exists.
-#endif
-  }
-
-  return false;
-}
-
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1001,8 +934,16 @@
   // Prompt to save only if the form is now gone, either invisible or
   // removed from the DOM.
   blink::WebFrame* frame = render_frame()->GetWebFrame();
-  if (IsFormVisible(frame, *provisionally_saved_form_, form_predictions_))
+  if (form_util::IsFormVisible(frame, provisionally_saved_form_->action,
+                               provisionally_saved_form_->origin,
+                               provisionally_saved_form_->form_data,
+                               form_predictions_) ||
+      IsUnownedPasswordFormVisible(frame, provisionally_saved_form_->action,
+                                   provisionally_saved_form_->origin,
+                                   provisionally_saved_form_->form_data,
+                                   form_predictions_)) {
     return;
+  }
 
   Send(new AutofillHostMsg_InPageNavigation(routing_id(),
                                             *provisionally_saved_form_));
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 3a89823..62ade2b 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -491,7 +491,8 @@
   if (!LocateSpecificPasswords(passwords, &password, &new_password))
     return false;
 
-  password_form->origin = GetCanonicalOriginForDocument(form.document);
+  password_form->origin =
+      form_util::GetCanonicalOriginForDocument(form.document);
   GURL::Replacements rep;
   rep.SetPathStr("");
   password_form->signon_realm =
@@ -539,32 +540,8 @@
   return true;
 }
 
-GURL StripAuthAndParams(const GURL& gurl) {
-  // We want to keep the path but strip any authentication data, as well as
-  // query and ref portions of URL, for the form action and form origin.
-  GURL::Replacements rep;
-  rep.ClearUsername();
-  rep.ClearPassword();
-  rep.ClearQuery();
-  rep.ClearRef();
-  return gurl.ReplaceComponents(rep);
-}
-
 }  // namespace
 
-GURL GetCanonicalActionForForm(const WebFormElement& form) {
-  WebString action = form.action();
-  if (action.isNull())
-    action = WebString(""); // missing 'action' attribute implies current URL
-  GURL full_action(form.document().completeURL(action));
-  return StripAuthAndParams(full_action);
-}
-
-GURL GetCanonicalOriginForDocument(const WebDocument& document) {
-  GURL full_origin(document.url());
-  return StripAuthAndParams(full_origin);
-}
-
 bool IsGaiaReauthenticationForm(
     const GURL& origin,
     const WebVector<blink::WebFormControlElement>& control_elements) {
@@ -605,7 +582,7 @@
     return scoped_ptr<PasswordForm>();
 
   scoped_ptr<PasswordForm> password_form(new PasswordForm());
-  password_form->action = GetCanonicalActionForForm(web_form);
+  password_form->action = form_util::GetCanonicalActionForForm(web_form);
   if (!password_form->action.is_valid())
     return scoped_ptr<PasswordForm>();
 
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.h b/components/autofill/content/renderer/password_form_conversion_utils.h
index d931e2a0..257e202 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -27,12 +27,6 @@
 struct FormFieldData;
 struct PasswordForm;
 
-// Helper functions to assist in getting the canonical form of the action and
-// origin. The action will proplerly take into account <BASE>, and both will
-// strip unnecessary data (e.g. query params and HTTP credentials).
-GURL GetCanonicalActionForForm(const blink::WebFormElement& form);
-GURL GetCanonicalOriginForDocument(const blink::WebDocument& document);
-
 // Tests whether the given form is a GAIA reauthentication form. The form is
 // not passed directly as WebFormElement, but by specifying its |url| and
 // |control_elements|. This is for better performance and easier testing.
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 4f43573b..d1d3843 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -1459,9 +1459,7 @@
           'target_name': 'components_browsertests_apk',
           'type': 'none',
           'dependencies': [
-            '../content/content.gyp:content_icudata',
             '../content/content.gyp:content_java',
-            '../content/content.gyp:content_v8_external_data',
             '../content/content_shell_and_tests.gyp:content_java_test_support',
             '../content/content_shell_and_tests.gyp:content_shell_browsertests_java',
             '../content/content_shell_and_tests.gyp:content_shell_java',
diff --git a/components/cronet.gypi b/components/cronet.gypi
index 5a63b94..10a7fecd 100644
--- a/components/cronet.gypi
+++ b/components/cronet.gypi
@@ -49,10 +49,10 @@
           'includes': [ '../build/android/java_cpp_enum.gypi' ],
         },
         {
-          'target_name': 'cronet_url_request_context_config_list',
+          'target_name': 'cronet_engine_builder_list',
           'type': 'none',
           'sources': [
-            'cronet/android/java/src/org/chromium/net/UrlRequestContextConfigList.template',
+            'cronet/android/java/src/org/chromium/net/CronetEngineBuilderList.template',
           ],
           'variables': {
             'package_name': 'org/chromium/cronet',
@@ -214,7 +214,7 @@
           'target_name': 'cronet_api',
           'type': 'none',
           'dependencies': [
-            'cronet_url_request_context_config_list',
+            'cronet_engine_builder_list',
             'cronet_version',
             'load_states_list',
             'network_quality_observations_java',
@@ -223,6 +223,8 @@
             'java_in_dir': 'cronet/android/java',
             'javac_includes': [
               '**/ChunkedWritableByteChannel.java',
+              '**/CronetEngine.java',
+              '**/CronetEngineBuilderList.java',
               '**/ExtendedResponseInfo.java',
               '**/HistogramManager.java',
               '**/HttpUrlConnection*.java',
@@ -235,9 +237,7 @@
               '**/UploadDataProvider.java',
               '**/UploadDataSink.java',
               '**/UrlRequest.java',
-              '**/UrlRequestContext.java',
               '**/UrlRequestContextConfig.java',
-              '**/UrlRequestContextConfigList.java',
               '**/UrlRequestException.java',
               '**/UrlRequestListener.java',
               '**/UserAgent.java',
diff --git a/components/cronet/README.md b/components/cronet/README.md
index eb7d81a3..c917eb2d 100644
--- a/components/cronet/README.md
+++ b/components/cronet/README.md
@@ -68,13 +68,13 @@
 
 Make a request like this:
 
-    UrlRequestContextConfig myConfig = new UrlRequestContextConfig();
-    CronetUrlRequestContext myRequestContext =
-            new CronetUrlRequestContext(getContext(), myConfig);
+    CronetEngine.Builder engineBuilder = new CronetEngine.Builder(getContext());
+    CronetEngine engine = engineBuilder.build();
     Executor executor = Executors.newSingleThreadExecutor();
     MyListener listener = new MyListener();
-    UrlRequest request = myRequestContext.createRequest(
-            "https://www.example.com", listener, executor);
+    UrlRequest.Builder requestBuilder = new UrlRequest.Builder(
+            "https://www.example.com", listener, executor, engine);
+    UrlRequest request = requestBuilder.build();
     request.start();
 
 In the above example, `MyListener` extends `UrlRequestListener`. The request
@@ -97,9 +97,8 @@
 
 ### Uploading Data
     MyUploadDataProvider myUploadDataProvider = new MyUploadDataProvider();
-    request.setHttpMethod("POST");
-    request.setUploadDataProvider(myUploadDataProvider, executor);
-    request.start();
+    requestBuilder.setHttpMethod("POST");
+    requestBuilder.setUploadDataProvider(myUploadDataProvider, executor);
 
 In the above example, `MyUploadDataProvider` extends `UploadDataProvider`.
 When Cronet is ready to send the request body,
@@ -112,32 +111,32 @@
 invoked again. For more details, please see the API reference.
 
 ### <a id=configuring-cronet></a> Configuring Cronet
-Various configuration options are available via the `UrlRequestContextConfig`
+Various configuration options are available via the `CronetEngine.Builder`
 object.
 
 Enabling HTTP/2, QUIC, or SDCH:
 
 - For Example:
 
-        myConfig.enableSPDY(true).enableQUIC(true).enableSDCH(true);
+        engineBuilder.enableSPDY(true).enableQUIC(true).enableSDCH(true);
 
 Controlling the cache:
 
 - Use a 100KiB in-memory cache:
 
-        myConfig.enableHttpCache(
-                UrlRequestContextConfig.HttpCache.IN_MEMORY, 100 * 1024);
+        engineBuilder.enableHttpCache(
+                CronetEngine.Builder.HttpCache.IN_MEMORY, 100 * 1024);
 
 - or use a 1MiB disk cache:
 
-        myConfig.setStoragePath(storagePathString);
-        myConfig.enableHttpCache(UrlRequestContextConfig.HttpCache.DISK,
+        engineBuilder.setStoragePath(storagePathString);
+        engineBuilder.enableHttpCache(CronetEngine.Builder.HttpCache.DISK,
                 1024 * 1024);
 
 ### Debugging
 To get more information about how Cronet is processing network
 requests, you can start and stop **NetLog** logging by calling
-`UrlRequestContext.startNetLogToFile` and `UrlRequestContext.stopNetLog`.
+`CronetEngine.startNetLogToFile` and `CronetEngine.stopNetLog`.
 Bear in mind that logs may contain sensitive data. You may analyze the
 generated log by navigating to [chrome://net-internals#import] using a
 Chrome browser.
@@ -149,7 +148,7 @@
 simply do the following:
 
     CronetURLStreamHandlerFactory streamHandlerFactory =
-            new CronetURLStreamHandlerFactory(getContext(), myConfig);
+            new CronetURLStreamHandlerFactory(engine);
     URL.setURLStreamHandlerFactory(streamHandlerFactory);
 
 Cronet's
@@ -158,7 +157,7 @@
 see {@link org.chromium.net.urlconnection.CronetURLStreamHandlerFactory} for
 more information).
 You can configure Cronet and control caching through the
-`UrlRequestContextConfig` instance, `myConfig`
+`CronetEngine.Builder` instance, `engineBuilder`
 (See [Configuring Cronet](#configuring-cronet) section), before you pass it
 into the `CronetURLStreamHandlerFactory` constructor.
 
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
index 2901071a..6c875e1 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
@@ -15,7 +15,7 @@
 
 /**
  * Provides context for the native HTTP operations.
- * @deprecated Use {@link CronetUrlRequestContext} instead.
+ * @deprecated Use {@link CronetEngine} instead.
  */
 @JNINamespace("cronet")
 @Deprecated
@@ -34,7 +34,7 @@
      * Constructor.
      */
     protected ChromiumUrlRequestContext(
-            final Context context, String userAgent, UrlRequestContextConfig config) {
+            final Context context, String userAgent, CronetEngine.Builder config) {
         CronetLibraryLoader.ensureInitialized(context, config);
         mChromiumUrlRequestContextAdapter =
                 nativeCreateRequestContextAdapter(userAgent, getLoggingLevel(), config.toString());
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
index 95200820..1fc9393 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
@@ -14,7 +14,7 @@
 
 /**
  * Network request factory using the native http stack implementation.
- * @deprecated Use {@link CronetUrlRequestContext} instead.
+ * @deprecated Use {@link CronetEngine} instead.
  */
 @UsedByReflection("HttpUrlRequestFactory.java")
 @Deprecated
@@ -22,8 +22,7 @@
     private ChromiumUrlRequestContext mRequestContext;
 
     @UsedByReflection("HttpUrlRequestFactory.java")
-    public ChromiumUrlRequestFactory(
-            Context context, UrlRequestContextConfig config) {
+    public ChromiumUrlRequestFactory(Context context, CronetEngine.Builder config) {
         if (isEnabled()) {
             String userAgent = config.userAgent();
             if (userAgent.isEmpty()) {
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetEngine.java b/components/cronet/android/java/src/org/chromium/net/CronetEngine.java
new file mode 100644
index 0000000..2de7e8d0
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/CronetEngine.java
@@ -0,0 +1,563 @@
+// 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.net;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.util.concurrent.Executor;
+
+/**
+ * An engine to process {@link UrlRequest}s, which uses the best HTTP stack
+ * available on the current platform.
+ */
+public abstract class CronetEngine {
+    /**
+     * A builder for {@link CronetEngine}s, which allows runtime configuration of
+     * {@code CronetEngine}. Configuration options are set on the builder and
+     * then {@link #build} is called to create the {@code CronetEngine}.
+     */
+    public static class Builder {
+        private final JSONObject mConfig;
+        private final Context mContext;
+
+        /**
+         * Default config enables SPDY, disables QUIC, SDCH and HTTP cache.
+         * @param context Android {@link Context} for engine to use.
+         */
+        public Builder(Context context) {
+            mConfig = new JSONObject();
+            mContext = context;
+            enableLegacyMode(false);
+            enableQUIC(false);
+            enableHTTP2(true);
+            enableSDCH(false);
+            enableHttpCache(HTTP_CACHE_DISABLED, 0);
+        }
+
+        /**
+         * Creates a config from a JSON string, which was serialized using
+         * {@link #toString}.
+         *
+         * @param context Android {@link Context} for engine to use.
+         * @param json JSON string of configuration parameters, which was
+         *         serialized using {@link #toString}.
+         */
+        public Builder(Context context, String json) throws JSONException {
+            mConfig = new JSONObject(json);
+            mContext = context;
+        }
+
+        /**
+         * Overrides the user-agent header for all requests.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setUserAgent(String userAgent) {
+            return putString(CronetEngineBuilderList.USER_AGENT, userAgent);
+        }
+
+        String userAgent() {
+            return mConfig.optString(CronetEngineBuilderList.USER_AGENT);
+        }
+
+        /**
+         * Sets directory for HTTP Cache and Cookie Storage. The directory must
+         * exist.
+         * @param value path to existing directory.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setStoragePath(String value) {
+            if (!new File(value).isDirectory()) {
+                throw new IllegalArgumentException(
+                        "Storage path must be set to existing directory");
+            }
+
+            return putString(CronetEngineBuilderList.STORAGE_PATH, value);
+        }
+
+        String storagePath() {
+            return mConfig.optString(CronetEngineBuilderList.STORAGE_PATH);
+        }
+
+        /**
+         * Sets whether falling back to implementation based on system's
+         * {@link java.net.HttpURLConnection} implementation is enabled.
+         * Defaults to disabled.
+         * @return the builder to facilitate chaining.
+         * @deprecated Not supported by the new API.
+         */
+        @Deprecated
+        public Builder enableLegacyMode(boolean value) {
+            return putBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE, value);
+        }
+
+        boolean legacyMode() {
+            return mConfig.optBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE);
+        }
+
+        /**
+         * Overrides the name of the native library backing Cronet.
+         * @return the builder to facilitate chaining.
+         */
+        Builder setLibraryName(String libName) {
+            return putString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME, libName);
+        }
+
+        String libraryName() {
+            return mConfig.optString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME, "cronet");
+        }
+
+        /**
+         * Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protocol
+         * is enabled. Defaults to disabled.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder enableQUIC(boolean value) {
+            return putBoolean(CronetEngineBuilderList.ENABLE_QUIC, value);
+        }
+
+        /**
+         * Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a>
+         * protocol is enabled. Defaults to enabled.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder enableHTTP2(boolean value) {
+            return putBoolean(CronetEngineBuilderList.ENABLE_SPDY, value);
+        }
+
+        /**
+         * Sets whether
+         * <a
+         * href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/att-0441/Shared_Dictionary_Compression_over_HTTP.pdf">
+         * SDCH</a> compression is enabled. Defaults to disabled.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder enableSDCH(boolean value) {
+            return putBoolean(CronetEngineBuilderList.ENABLE_SDCH, value);
+        }
+
+        /**
+         * Enables
+         * <a href="https://developer.chrome.com/multidevice/data-compression">Data
+         * Reduction Proxy</a>. Defaults to disabled.
+         * @param key key to use when authenticating with the proxy.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder enableDataReductionProxy(String key) {
+            return (putString(CronetEngineBuilderList.DATA_REDUCTION_PROXY_KEY, key));
+        }
+
+        /**
+         * Overrides
+         * <a href="https://developer.chrome.com/multidevice/data-compression">
+         * Data Reduction Proxy</a> configuration parameters with a primary
+         * proxy name, fallback proxy name, and a secure proxy check URL. Proxies
+         * are specified as [scheme://]host[:port]. Used for testing.
+         * @param primaryProxy the primary data reduction proxy to use.
+         * @param fallbackProxy a fallback data reduction proxy to use.
+         * @param secureProxyCheckUrl a URL to fetch to determine if using a secure
+         * proxy is allowed.
+         * @return the builder to facilitate chaining.
+         * @hide
+         */
+        public Builder setDataReductionProxyOptions(
+                String primaryProxy, String fallbackProxy, String secureProxyCheckUrl) {
+            if (primaryProxy.isEmpty() || fallbackProxy.isEmpty()
+                    || secureProxyCheckUrl.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "Primary and fallback proxies and check url must be set");
+            }
+            putString(CronetEngineBuilderList.DATA_REDUCTION_PRIMARY_PROXY, primaryProxy);
+            putString(CronetEngineBuilderList.DATA_REDUCTION_FALLBACK_PROXY, fallbackProxy);
+            putString(CronetEngineBuilderList.DATA_REDUCTION_SECURE_PROXY_CHECK_URL,
+                    secureProxyCheckUrl);
+            return this;
+        }
+
+        /**
+         * Setting to disable HTTP cache. Some data may still be temporarily stored in memory.
+         * Passed to {@link #enableHttpCache}.
+         */
+        public static final int HTTP_CACHE_DISABLED = 0;
+
+        /**
+         * Setting to enable in-memory HTTP cache, including HTTP data.
+         * Passed to {@link #enableHttpCache}.
+         */
+        public static final int HTTP_CACHE_IN_MEMORY = 1;
+
+        /**
+         * Setting to enable on-disk cache, excluding HTTP data.
+         * {@link #setStoragePath} must be called prior to passing this constant to
+         * {@link #enableHttpCache}.
+         */
+        public static final int HTTP_CACHE_DISK_NO_HTTP = 2;
+
+        /**
+         * Setting to enable on-disk cache, including HTTP data.
+         * {@link #setStoragePath} must be called prior to passing this constant to
+         * {@link #enableHttpCache}.
+         */
+        public static final int HTTP_CACHE_DISK = 3;
+
+        /**
+         * Enables or disables caching of HTTP data and other information like QUIC
+         * server information.
+         * @param cacheMode control location and type of cached data.
+         * @param maxSize maximum size in bytes used to cache data (advisory and maybe
+         * exceeded at times).
+         * @return the builder to facilitate chaining.
+         */
+        public Builder enableHttpCache(int cacheMode, long maxSize) {
+            if (cacheMode == HTTP_CACHE_DISK || cacheMode == HTTP_CACHE_DISK_NO_HTTP) {
+                if (storagePath().isEmpty()) {
+                    throw new IllegalArgumentException("Storage path must be set");
+                }
+            } else {
+                if (!storagePath().isEmpty()) {
+                    throw new IllegalArgumentException("Storage path must be empty");
+                }
+            }
+            putBoolean(CronetEngineBuilderList.LOAD_DISABLE_CACHE,
+                    cacheMode == HTTP_CACHE_DISABLED || cacheMode == HTTP_CACHE_DISK_NO_HTTP);
+            putLong(CronetEngineBuilderList.HTTP_CACHE_MAX_SIZE, maxSize);
+
+            switch (cacheMode) {
+                case HTTP_CACHE_DISABLED:
+                    return putString(CronetEngineBuilderList.HTTP_CACHE,
+                            CronetEngineBuilderList.HTTP_CACHE_DISABLED);
+                case HTTP_CACHE_DISK_NO_HTTP:
+                case HTTP_CACHE_DISK:
+                    return putString(CronetEngineBuilderList.HTTP_CACHE,
+                            CronetEngineBuilderList.HTTP_CACHE_DISK);
+
+                case HTTP_CACHE_IN_MEMORY:
+                    return putString(CronetEngineBuilderList.HTTP_CACHE,
+                            CronetEngineBuilderList.HTTP_CACHE_MEMORY);
+            }
+            return this;
+        }
+
+        /**
+         * Adds hint that {@code host} supports QUIC.
+         * Note that {@link #enableHttpCache enableHttpCache}
+         * ({@link HttpCache#DISK DISK}) is needed to take advantage of 0-RTT
+         * connection establishment between sessions.
+         *
+         * @param host hostname of the server that supports QUIC.
+         * @param port host of the server that supports QUIC.
+         * @param alternatePort alternate port to use for QUIC.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder addQuicHint(String host, int port, int alternatePort) {
+            if (host.contains("/")) {
+                throw new IllegalArgumentException("Illegal QUIC Hint Host: " + host);
+            }
+            try {
+                JSONArray quicHints = mConfig.optJSONArray(CronetEngineBuilderList.QUIC_HINTS);
+                if (quicHints == null) {
+                    quicHints = new JSONArray();
+                    mConfig.put(CronetEngineBuilderList.QUIC_HINTS, quicHints);
+                }
+
+                JSONObject hint = new JSONObject();
+                hint.put(CronetEngineBuilderList.QUIC_HINT_HOST, host);
+                hint.put(CronetEngineBuilderList.QUIC_HINT_PORT, port);
+                hint.put(CronetEngineBuilderList.QUIC_HINT_ALT_PORT, alternatePort);
+                quicHints.put(hint);
+            } catch (JSONException e) {
+                // Intentionally do nothing.
+            }
+            return this;
+        }
+
+        /**
+         * Sets experimental QUIC connection options, overwriting any pre-existing
+         * options. List of options is subject to change.
+         *
+         * @param quicConnectionOptions comma-separated QUIC options (for example
+         * "PACE,IW10") to use if QUIC is enabled.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setExperimentalQuicConnectionOptions(String quicConnectionOptions) {
+            return putString(CronetEngineBuilderList.QUIC_OPTIONS, quicConnectionOptions);
+        }
+
+        /**
+         * Get JSON string representation of the builder.
+         */
+        @Override
+        public String toString() {
+            return mConfig.toString();
+        }
+
+        /**
+         * Returns {@link Context} for builder.
+         *
+         * @return {@link Context} for builder.
+         */
+        Context getContext() {
+            return mContext;
+        }
+
+        /**
+         * Sets a boolean value in the config. Returns a reference to the same
+         * config object, so you can chain put calls together.
+         * @return the builder to facilitate chaining.
+         */
+        private Builder putBoolean(String key, boolean value) {
+            try {
+                mConfig.put(key, value);
+            } catch (JSONException e) {
+                // Intentionally do nothing.
+            }
+            return this;
+        }
+
+        /**
+         * Sets a long value in the config. Returns a reference to the same
+         * config object, so you can chain put calls together.
+         * @return the builder to facilitate chaining.
+         */
+        private Builder putLong(String key, long value) {
+            try {
+                mConfig.put(key, value);
+            } catch (JSONException e) {
+                // Intentionally do nothing.
+            }
+            return this;
+        }
+
+        /**
+         * Sets a string value in the config. Returns a reference to the same
+         * config object, so you can chain put calls together.
+         * @return the builder to facilitate chaining.
+         */
+        private Builder putString(String key, String value) {
+            try {
+                mConfig.put(key, value);
+            } catch (JSONException e) {
+                // Intentionally do nothing.
+            }
+            return this;
+        }
+
+        /**
+         * Build a {@link CronetEngine} using this builder's configuration.
+         */
+        public CronetEngine build() {
+            return createContext(this);
+        }
+    }
+
+    private static final String TAG = "UrlRequestFactory";
+    private static final String CRONET_URL_REQUEST_CONTEXT =
+            "org.chromium.net.CronetUrlRequestContext";
+
+    /**
+     * Creates a {@link UrlRequest} object. All callbacks will
+     * be called on {@code executor}'s thread. {@code executor} must not run
+     * tasks on the current thread to prevent blocking networking operations
+     * and causing exceptions during shutdown. Request is given medium priority,
+     * see {@link UrlRequest#REQUEST_PRIORITY_MEDIUM}. To specify other
+     * priorities see {@link #createRequest(String, UrlRequestListener,
+     * Executor, int priority)}.
+     *
+     * @param url {@link java.net.URL} for the request.
+     * @param listener callback class that gets called on different events.
+     * @param executor {@link Executor} on which all callbacks will be called.
+     * @return new request.
+     * @deprecated Use {@link Builder#build}.
+     */
+    @Deprecated
+    public abstract UrlRequest createRequest(
+            String url, UrlRequestListener listener, Executor executor);
+
+    /**
+     * Creates a {@link UrlRequest} object. All callbacks will
+     * be called on {@code executor}'s thread. {@code executor} must not run
+     * tasks on the current thread to prevent blocking networking operations
+     * and causing exceptions during shutdown.
+     *
+     * @param url {@link java.net.URL} for the request.
+     * @param listener callback class that gets called on different events.
+     * @param executor {@link Executor} on which all callbacks will be called.
+     * @param priority priority of the request which should be one of the
+     *         {@link UrlRequest#REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*}
+     *         values.
+     * @return new request.
+     * @deprecated Use {@link Builder#build}.
+     */
+    @Deprecated
+    public abstract UrlRequest createRequest(
+            String url, UrlRequestListener listener, Executor executor, int priority);
+
+    /**
+     * @return {@code true} if the engine is enabled.
+     */
+    abstract boolean isEnabled();
+
+    /**
+     * @return a human-readable version string of the engine.
+     */
+    public abstract String getVersionString();
+
+    /**
+     * Shuts down the {@link CronetEngine} if there are no active requests,
+     * otherwise throws an exception.
+     *
+     * Cannot be called on network thread - the thread Cronet calls into
+     * Executor on (which is different from the thread the Executor invokes
+     * callbacks on). May block until all the {@code CronetEngine}'s
+     * resources have been cleaned up.
+     */
+    public abstract void shutdown();
+
+    /**
+     * Starts NetLog logging to a file. The NetLog is useful for debugging.
+     * The file can be viewed using a Chrome browser navigated to
+     * chrome://net-internals/#import
+     * @param fileName the complete file path. It must not be empty. If the file
+     *            exists, it is truncated before starting. If actively logging,
+     *            this method is ignored.
+     * @param logAll {@code true} to include basic events, user cookies,
+     *            credentials and all transferred bytes in the log.
+     *            {@code false} to just include basic events.
+     */
+    public abstract void startNetLogToFile(String fileName, boolean logAll);
+
+    /**
+     * Stops NetLog logging and flushes file to disk. If a logging session is
+     * not in progress, this call is ignored.
+     */
+    public abstract void stopNetLog();
+
+    /**
+     * Enables the network quality estimator, which collects and reports
+     * measurements of round trip time (RTT) and downstream throughput at
+     * various layers of the network stack. After enabling the estimator,
+     * listeners of RTT and throughput can be added with
+     * {@link #addRttListener} and {@link #addThroughputListener} and
+     * removed with {@link #removeRttListener} and
+     * {@link #removeThroughputListener}. The estimator uses memory and CPU
+     * only when enabled.
+     * @param executor an executor that will be used to notified all
+     *            added RTT and throughput listeners.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated public abstract void enableNetworkQualityEstimator(Executor executor);
+
+    /**
+     * Enables the network quality estimator for testing. This must be called
+     * before round trip time and throughput listeners are added. Set both
+     * boolean parameters to false for default behavior.
+     * @param useLocalHostRequests include requests to localhost in estimates.
+     * @param useSmallerResponses include small responses in throughput estimates.
+     * @param executor an {@link java.util.concurrent.Executor} on which all
+     *            listeners will be called.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated
+    abstract void enableNetworkQualityEstimatorForTesting(
+            boolean useLocalHostRequests, boolean useSmallerResponses, Executor executor);
+
+    /**
+     * Registers a listener that gets called whenever the network quality
+     * estimator witnesses a sample round trip time. This must be called
+     * after {@link #enableNetworkQualityEstimator}, and with throw an
+     * exception otherwise. Round trip times may be recorded at various layers
+     * of the network stack, including TCP, QUIC, and at the URL request layer.
+     * The listener is called on the {@link java.util.concurrent.Executor} that
+     * is passed to {@link #enableNetworkQualityEstimator}.
+     * @param listener the listener of round trip times.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated public abstract void addRttListener(NetworkQualityRttListener listener);
+
+    /**
+     * Removes a listener of round trip times if previously registered with
+     * {@link #addRttListener}. This should be called after a
+     * {@link NetworkQualityRttListener} is added in order to stop receiving
+     * observations.
+     * @param listener the listener of round trip times.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated public abstract void removeRttListener(NetworkQualityRttListener listener);
+
+    /**
+     * Registers a listener that gets called whenever the network quality
+     * estimator witnesses a sample throughput measurement. This must be called
+     * after {@link #enableNetworkQualityEstimator}. Throughput observations
+     * are computed by measuring bytes read over the active network interface
+     * at times when at least one URL response is being received. The listener
+     * is called on the {@link java.util.concurrent.Executor} that is passed to
+     * {@link #enableNetworkQualityEstimator}.
+     * @param listener the listener of throughput.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated
+    public abstract void addThroughputListener(NetworkQualityThroughputListener listener);
+
+    /**
+     * Removes a listener of throughput. This should be called after a
+     * {@link NetworkQualityThroughputListener} is added with
+     * {@link #addThroughputListener} in order to stop receiving observations.
+     * @param listener the listener of throughput.
+     * @deprecated not really deprecated but hidden for now as it's a prototype.
+     */
+    @Deprecated
+    public abstract void removeThroughputListener(NetworkQualityThroughputListener listener);
+
+    /**
+     * Creates a {@link CronetEngine} with the given {@link Builder}.
+     * @param context Android {@link Context}.
+     * @param config engine configuration.
+     * @deprecated Use {@link CronetEngine.Builder}.
+     */
+    @Deprecated
+    public static CronetEngine createContext(Builder config) {
+        CronetEngine cronetEngine = null;
+        if (config.userAgent().isEmpty()) {
+            config.setUserAgent(UserAgent.from(config.getContext()));
+        }
+        if (!config.legacyMode()) {
+            cronetEngine = createCronetEngine(config);
+        }
+        if (cronetEngine == null) {
+            // TODO(mef): Fallback to stub implementation. Once stub
+            // implementation is available merge with createCronetFactory.
+            cronetEngine = createCronetEngine(config);
+        }
+        Log.i(TAG, "Using network stack: " + cronetEngine.getVersionString());
+        return cronetEngine;
+    }
+
+    private static CronetEngine createCronetEngine(Builder config) {
+        CronetEngine cronetEngine = null;
+        try {
+            Class<? extends CronetEngine> engineClass =
+                    CronetEngine.class.getClassLoader()
+                            .loadClass(CRONET_URL_REQUEST_CONTEXT)
+                            .asSubclass(CronetEngine.class);
+            Constructor<? extends CronetEngine> constructor =
+                    engineClass.getConstructor(Builder.class);
+            CronetEngine possibleEngine = constructor.newInstance(config);
+            if (possibleEngine.isEnabled()) {
+                cronetEngine = possibleEngine;
+            }
+        } catch (ClassNotFoundException e) {
+            // Leave as null.
+        } catch (Exception e) {
+            throw new IllegalStateException("Cannot instantiate: " + CRONET_URL_REQUEST_CONTEXT, e);
+        }
+        return cronetEngine;
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfigList.template b/components/cronet/android/java/src/org/chromium/net/CronetEngineBuilderList.template
similarity index 72%
rename from components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfigList.template
rename to components/cronet/android/java/src/org/chromium/net/CronetEngineBuilderList.template
index cbb9350..f30a09f 100644
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfigList.template
+++ b/components/cronet/android/java/src/org/chromium/net/CronetEngineBuilderList.template
@@ -5,9 +5,9 @@
 package org.chromium.net;
 
 // A simple auto-generated interface used to list config params as used by
-// both org.chromium.net.UrlRequestContextConfig and
-// net/cronet/android/org_chromium_net_UrlRequestContext.h
-public interface UrlRequestContextConfigList {
+// both org.chromium.net.CronetEngine.Builder and
+// components/cronet/url_request_context_config.h
+public interface CronetEngineBuilderList {
 #define DEFINE_CONTEXT_CONFIG(x) public static final String x = #x;
 #include "components/cronet/url_request_context_config_list.h"
 #undef DEFINE_CONTEXT_CONFIG
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
index 9894ecd..60a7675 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
@@ -26,12 +26,12 @@
      * any thread, the load and initialization is performed on main thread.
      */
     public static void ensureInitialized(
-            final Context context, final UrlRequestContextConfig config) {
+            final Context context, final CronetEngine.Builder builder) {
         synchronized (sLoadLock) {
             if (sInitTaskPosted) {
                 return;
             }
-            System.loadLibrary(config.libraryName());
+            System.loadLibrary(builder.libraryName());
             if (!Version.CRONET_VERSION.equals(nativeGetCronetVersion())) {
                 throw new RuntimeException(String.format(
                       "Expected Cronet version number %s, "
@@ -40,7 +40,7 @@
                       nativeGetCronetVersion()));
             }
             nativeCronetInitApplicationContext(context.getApplicationContext());
-            // Init native Chromium URLRequestContext on Main UI thread.
+            // Init native Chromium CronetEngine on Main UI thread.
             Runnable task = new Runnable() {
                 public void run() {
                     initOnMainThread(context);
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
index 9381f0a..104b722 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
@@ -466,15 +466,15 @@
 
     private static int convertRequestPriority(int priority) {
         switch (priority) {
-            case REQUEST_PRIORITY_IDLE:
+            case Builder.REQUEST_PRIORITY_IDLE:
                 return RequestPriority.IDLE;
-            case REQUEST_PRIORITY_LOWEST:
+            case Builder.REQUEST_PRIORITY_LOWEST:
                 return RequestPriority.LOWEST;
-            case REQUEST_PRIORITY_LOW:
+            case Builder.REQUEST_PRIORITY_LOW:
                 return RequestPriority.LOW;
-            case REQUEST_PRIORITY_MEDIUM:
+            case Builder.REQUEST_PRIORITY_MEDIUM:
                 return RequestPriority.MEDIUM;
-            case REQUEST_PRIORITY_HIGHEST:
+            case Builder.REQUEST_PRIORITY_HIGHEST:
                 return RequestPriority.HIGHEST;
             default:
                 return RequestPriority.MEDIUM;
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
index cd4b710..7cdabde2 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
@@ -4,7 +4,6 @@
 
 package org.chromium.net;
 
-import android.content.Context;
 import android.os.Build;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -26,11 +25,11 @@
 import javax.annotation.concurrent.GuardedBy;
 
 /**
- * UrlRequestContext using Chromium HTTP stack implementation.
+ * CronetEngine using Chromium HTTP stack implementation.
  */
 @JNINamespace("cronet")
-@UsedByReflection("UrlRequestContext.java")
-class CronetUrlRequestContext extends UrlRequestContext {
+@UsedByReflection("CronetEngine.java")
+class CronetUrlRequestContext extends CronetEngine {
     private static final int LOG_NONE = 3;  // LOG(FATAL), no VLOG.
     private static final int LOG_DEBUG = -1;  // LOG(FATAL...INFO), VLOG(1)
     private static final int LOG_VERBOSE = -2;  // LOG(FATAL...INFO), VLOG(2)
@@ -62,12 +61,11 @@
     private final ObserverList<NetworkQualityThroughputListener> mThroughputListenerList =
             new ObserverList<NetworkQualityThroughputListener>();
 
-    @UsedByReflection("UrlRequestContext.java")
-    public CronetUrlRequestContext(Context context,
-                                   UrlRequestContextConfig config) {
-        CronetLibraryLoader.ensureInitialized(context, config);
+    @UsedByReflection("CronetEngine.java")
+    public CronetUrlRequestContext(CronetEngine.Builder builder) {
+        CronetLibraryLoader.ensureInitialized(builder.getContext(), builder);
         nativeSetMinLogLevel(getLoggingLevel());
-        mUrlRequestContextAdapter = nativeCreateRequestContextAdapter(config.toString());
+        mUrlRequestContextAdapter = nativeCreateRequestContextAdapter(builder.toString());
         if (mUrlRequestContextAdapter == 0) {
             throw new NullPointerException("Context Adapter creation failed.");
         }
@@ -98,7 +96,7 @@
         synchronized (mLock) {
             checkHaveAdapter();
             return new CronetUrlRequest(this, mUrlRequestContextAdapter, url,
-                    UrlRequest.REQUEST_PRIORITY_MEDIUM, listener, executor);
+                    UrlRequest.Builder.REQUEST_PRIORITY_MEDIUM, listener, executor);
         }
     }
 
@@ -282,7 +280,7 @@
 
     private void checkHaveAdapter() throws IllegalStateException {
         if (!haveRequestContextAdapter()) {
-            throw new IllegalStateException("Context is shut down.");
+            throw new IllegalStateException("Engine is shut down.");
         }
     }
 
diff --git a/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
index ebefa6b..2bbcca1f 100644
--- a/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
@@ -13,7 +13,7 @@
 
 /**
  * Network request using {@link java.net.HttpURLConnection}.
- * @deprecated Use {@link UrlRequestContext} instead.
+ * @deprecated Use {@link CronetEngine} instead.
  */
 @Deprecated
 class HttpUrlConnectionUrlRequestFactory extends HttpUrlRequestFactory {
@@ -21,8 +21,7 @@
     private final Context mContext;
     private final String mDefaultUserAgent;
 
-    public HttpUrlConnectionUrlRequestFactory(
-            Context context, UrlRequestContextConfig config) {
+    public HttpUrlConnectionUrlRequestFactory(Context context, CronetEngine.Builder config) {
         mContext = context;
         String userAgent = config.userAgent();
         if (userAgent.isEmpty()) {
diff --git a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactory.java
index fe84f5f..fd92f7e 100644
--- a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactory.java
@@ -14,7 +14,7 @@
 /**
  * A factory for {@link HttpUrlRequest}'s, which uses the best HTTP stack
  * available on the current platform.
- * @deprecated Use {@link UrlRequestContext} instead.
+ * @deprecated Use {@link CronetEngine} instead.
  */
 @Deprecated
 public abstract class HttpUrlRequestFactory {
@@ -24,7 +24,7 @@
             "org.chromium.net.ChromiumUrlRequestFactory";
 
     public static HttpUrlRequestFactory createFactory(
-            Context context, UrlRequestContextConfig config) {
+            Context context, CronetEngine.Builder config) {
         HttpUrlRequestFactory factory = null;
         if (!config.legacyMode()) {
             factory = createChromiumFactory(context, config);
@@ -80,7 +80,7 @@
     public abstract void stopNetLog();
 
     private static HttpUrlRequestFactory createChromiumFactory(
-            Context context, UrlRequestContextConfig config) {
+            Context context, CronetEngine.Builder config) {
         HttpUrlRequestFactory factory = null;
         try {
             Class<? extends HttpUrlRequestFactory> factoryClass =
@@ -88,8 +88,7 @@
                             .loadClass(CHROMIUM_URL_REQUEST_FACTORY)
                             .asSubclass(HttpUrlRequestFactory.class);
             Constructor<? extends HttpUrlRequestFactory> constructor =
-                    factoryClass.getConstructor(
-                            Context.class, UrlRequestContextConfig.class);
+                    factoryClass.getConstructor(Context.class, CronetEngine.Builder.class);
             HttpUrlRequestFactory chromiumFactory =
                     constructor.newInstance(context, config);
             if (chromiumFactory.isEnabled()) {
diff --git a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactoryConfig.java b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactoryConfig.java
index 3da1c38..65434e4 100644
--- a/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactoryConfig.java
+++ b/components/cronet/android/java/src/org/chromium/net/HttpUrlRequestFactoryConfig.java
@@ -9,7 +9,7 @@
 /**
  * A config for HttpUrlRequestFactory, which allows runtime configuration of
  * HttpUrlRequestFactory.
- * @deprecated Use {@link UrlRequestContextConfig} instead.
+ * @deprecated Use {@link CronetEngine.Builder} instead.
  */
 @Deprecated
 public class HttpUrlRequestFactoryConfig extends UrlRequestContextConfig {
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
index 6a784c30..714567b 100644
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
@@ -4,16 +4,215 @@
 
 package org.chromium.net;
 
+import android.util.Pair;
+
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.concurrent.Executor;
 
 /**
  * Controls an HTTP request (GET, PUT, POST etc).
- * Created using {@link UrlRequestContext#createRequest UrlRequestContext.createRequest()}.
+ * Created using {@link UrlRequest.Builder}.
  * Note: All methods must be called on the {@link Executor} passed in during creation.
  */
 public interface UrlRequest {
     /**
+     * Builder for {@link UrlRequest}s. Allows configuring requests before constructing them
+     * with {@link Builder#build}.
+     */
+    public static final class Builder {
+        // All fields are temporary storage of UrlRequest configuration to be
+        // copied to built UrlRequests.
+
+        // CronetEngine to execute request.
+        final CronetEngine mCronetEngine;
+        // URL to request.
+        final String mUrl;
+        // Listener to receive progress callbacks.
+        final UrlRequestListener mListener;
+        // Executor to call listener on.
+        final Executor mExecutor;
+        // HTTP method (e.g. GET, POST etc).
+        String mMethod;
+        // List of request headers, stored as header field name and value pairs.
+        final ArrayList<Pair<String, String>> mRequestHeaders =
+                new ArrayList<Pair<String, String>>();
+        // Disable the cache for just this request.
+        boolean mDisableCache;
+        // Priority of request.
+        int mPriority = REQUEST_PRIORITY_MEDIUM;
+        // If request is an upload, this provides the request body data.
+        UploadDataProvider mUploadDataProvider;
+        // Executor to call upload data provider back on.
+        Executor mUploadDataProviderExecutor;
+
+        /**
+         * Creates a builder for {@link UrlRequest} objects. All callbacks for
+         * generated {@link UrlRequest} objects will be called on
+         * {@code executor}'s thread. {@code executor} must not run tasks on the
+         * current thread to prevent blocking networking operations and causing
+         * exceptions during shutdown.
+         *
+         * @param url {@link java.net.URL} for the generated requests.
+         * @param listener callback class that gets called on different events.
+         * @param executor {@link Executor} on which all callbacks will be called.
+         * @param cronetEngine {@link CronetEngine} used to execute this request.
+         */
+        public Builder(String url, UrlRequestListener listener, Executor executor,
+                CronetEngine cronetEngine) {
+            if (url == null) {
+                throw new NullPointerException("URL is required.");
+            }
+            if (listener == null) {
+                throw new NullPointerException("Listener is required.");
+            }
+            if (executor == null) {
+                throw new NullPointerException("Executor is required.");
+            }
+            if (cronetEngine == null) {
+                throw new NullPointerException("CronetEngine is required.");
+            }
+            mUrl = url;
+            mListener = listener;
+            mExecutor = executor;
+            mCronetEngine = cronetEngine;
+        }
+
+        /**
+         * Sets the HTTP method verb to use for this request.
+         *
+         * <p>The default when this method is not called is "GET" if the request has
+         * no body or "POST" if it does.
+         *
+         * @param method "GET", "HEAD", "DELETE", "POST" or "PUT".
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setHttpMethod(String method) {
+            if (method == null) {
+                throw new NullPointerException("Method is required.");
+            }
+            mMethod = method;
+            return this;
+        }
+
+        /**
+         * Adds a request header.
+         *
+         * @param header header name.
+         * @param value header value.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder addHeader(String header, String value) {
+            if (header == null) {
+                throw new NullPointerException("Invalid header name.");
+            }
+            if (value == null) {
+                throw new NullPointerException("Invalid header value.");
+            }
+            mRequestHeaders.add(Pair.create(header, value));
+            return this;
+        }
+
+        /**
+         * Disables cache for the request. If context is not set up to use cache,
+         * this call has no effect.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder disableCache() {
+            mDisableCache = true;
+            return this;
+        }
+
+        /**
+         * Lowest request priority. Passed to {@link #setPriority}.
+         */
+        public static final int REQUEST_PRIORITY_IDLE = 0;
+        /**
+         * Very low request priority. Passed to {@link #setPriority}.
+         */
+        public static final int REQUEST_PRIORITY_LOWEST = 1;
+        /**
+         * Low request priority. Passed to {@link #setPriority}.
+         */
+        public static final int REQUEST_PRIORITY_LOW = 2;
+        /**
+         * Medium request priority. Passed to {@link #setPriority}.
+         */
+        public static final int REQUEST_PRIORITY_MEDIUM = 3;
+        /**
+         * Highest request priority. Passed to {@link #setPriority}.
+         */
+        public static final int REQUEST_PRIORITY_HIGHEST = 4;
+
+        /**
+         * Sets priority of the request which should be one of the
+         * {@link #REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*} values.
+         * Defaults to {@link #REQUEST_PRIORITY_MEDIUM}
+         *
+         * @param priority priority of the request which should be one of the
+         *         {@link #REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*} values.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setPriority(int priority) {
+            mPriority = priority;
+            return this;
+        }
+
+        /**
+         * Sets upload data provider. Switches method to "POST" if not
+         * explicitly set. Starting the request will throw an exception if a
+         * Content-Type header is not set.
+         *
+         * @param uploadDataProvider responsible for providing the upload data.
+         * @param executor All {@code uploadDataProvider} methods will be called
+         *     using this {@code Executor}. May optionally be the same
+         *     {@code Executor} the request itself is using.
+         * @return the builder to facilitate chaining.
+         */
+        public Builder setUploadDataProvider(
+                UploadDataProvider uploadDataProvider, Executor executor) {
+            if (uploadDataProvider == null) {
+                throw new NullPointerException("Invalid UploadDataProvider.");
+            }
+            if (executor == null) {
+                throw new NullPointerException("Invalid UploadDataProvider Executor.");
+            }
+            if (mMethod == null) {
+                mMethod = "POST";
+            }
+            mUploadDataProvider = uploadDataProvider;
+            mUploadDataProviderExecutor = executor;
+            return this;
+        }
+
+        /**
+         * Creates a {@link UrlRequest} using configuration within this
+         * {@link Builder}. The returned {@code UrlRequest} can then be started
+         * by calling {@link UrlRequest#start}.
+         *
+         * @return constructed {@link UrlRequest} using configuration within
+         *         this {@link Builder}.
+         */
+        public UrlRequest build() {
+            final UrlRequest request =
+                    mCronetEngine.createRequest(mUrl, mListener, mExecutor, mPriority);
+            if (mMethod != null) {
+                request.setHttpMethod(mMethod);
+            }
+            if (mDisableCache) {
+                request.disableCache();
+            }
+            for (Pair<String, String> header : mRequestHeaders) {
+                request.addHeader(header.first, header.second);
+            }
+            if (mUploadDataProvider != null) {
+                request.setUploadDataProvider(mUploadDataProvider, mUploadDataProviderExecutor);
+            }
+            return request;
+        }
+    }
+
+    /**
      * Request status values returned by {@link #getStatus}.
      */
     public static class Status {
@@ -196,36 +395,6 @@
     }
 
     /**
-     * Lowest request priority. Passed to {@link UrlRequestContext#createRequest(String,
-     * UrlRequestListener, Executor, int) UrlRequestContext.createRequest()}.
-     */
-    public static final int REQUEST_PRIORITY_IDLE = 0;
-    /**
-     * Very low request priority. Passed to {@link UrlRequestContext#createRequest(String,
-     * UrlRequestListener, Executor, int) UrlRequestContext.createRequest()}.
-     */
-    public static final int REQUEST_PRIORITY_LOWEST = 1;
-    /**
-     * Low request priority. Passed to {@link UrlRequestContext#createRequest(String,
-     * UrlRequestListener, Executor, int) UrlRequestContext.createRequest()}.
-     */
-    public static final int REQUEST_PRIORITY_LOW = 2;
-    /**
-     * Medium request priority. Passed to {@link UrlRequestContext#createRequest(String,
-     * UrlRequestListener, Executor, int) UrlRequestContext.createRequest()}.
-     */
-    public static final int REQUEST_PRIORITY_MEDIUM = 3;
-    /**
-     * Highest request priority. Passed to {@link UrlRequestContext#createRequest(String,
-     * UrlRequestListener, Executor, int) UrlRequestContext.createRequest()}.
-     */
-    public static final int REQUEST_PRIORITY_HIGHEST = 4;
-
-    // More setters go here. They may only be called before start (Maybe
-    // also allow during redirects). Could optionally instead use arguments
-    // to URLRequestFactory when creating the request.
-
-    /**
      * Sets the HTTP method verb to use for this request. Must be done before
      * request has started.
      *
@@ -233,16 +402,18 @@
      * no body or "POST" if it does.
      *
      * @param method "GET", "HEAD", "DELETE", "POST" or "PUT".
+     * @deprecated Use {@link Builder#setHttpMethod}.
      */
-    public void setHttpMethod(String method);
+    @Deprecated public void setHttpMethod(String method);
 
     /**
      * Adds a request header. Must be done before request has started.
      *
      * @param header header name.
      * @param value header value.
+     * @deprecated Use {@link Builder#setPriority}.
      */
-    public void addHeader(String header, String value);
+    @Deprecated public void addHeader(String header, String value);
 
     /**
      * Sets upload data provider. Must be done before request has started. May only be
@@ -254,7 +425,9 @@
      * @param executor All {@code uploadDataProvider} methods will be called
      *     using this {@code Executor}. May optionally be the same
      *     {@code Executor} the request itself is using.
+     * @deprecated Use {@link Builder#setUploadDataProvider}.
      */
+    @Deprecated
     public void setUploadDataProvider(UploadDataProvider uploadDataProvider, Executor executor);
 
     /**
@@ -346,8 +519,9 @@
     /**
      * Disables cache for the request. If context is not set up to use cache,
      * this call has no effect.
+     * @deprecated Use {@link Builder#disableCache}.
      */
-    public void disableCache();
+    @Deprecated public void disableCache();
 
     /**
      * Queries the status of the request.
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java
deleted file mode 100644
index 30ff080..0000000
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java
+++ /dev/null
@@ -1,222 +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.
-
-package org.chromium.net;
-
-import android.content.Context;
-import android.util.Log;
-
-import java.lang.reflect.Constructor;
-import java.util.concurrent.Executor;
-
-/**
- * A context for {@link UrlRequest}'s, which uses the best HTTP stack
- * available on the current platform.
- */
-public abstract class UrlRequestContext {
-    private static final String TAG = "UrlRequestFactory";
-    private static final String CRONET_URL_REQUEST_CONTEXT =
-            "org.chromium.net.CronetUrlRequestContext";
-
-    /**
-     * Creates a {@link UrlRequest} object. All callbacks will
-     * be called on {@code executor}'s thread. {@code executor} must not run
-     * tasks on the current thread to prevent blocking networking operations
-     * and causing exceptions during shutdown. Request is given medium priority,
-     * see {@link UrlRequest#REQUEST_PRIORITY_MEDIUM}. To specify other
-     * priorities see {@link #createRequest(String, UrlRequestListener,
-     * Executor, int priority)}.
-     *
-     * @param url {@link java.net.URL} for the request.
-     * @param listener callback class that gets called on different events.
-     * @param executor {@link Executor} on which all callbacks will be called.
-     * @return new request.
-     */
-    public abstract UrlRequest createRequest(String url,
-            UrlRequestListener listener, Executor executor);
-
-    /**
-     * Creates a {@link UrlRequest} object. All callbacks will
-     * be called on {@code executor}'s thread. {@code executor} must not run
-     * tasks on the current thread to prevent blocking networking operations
-     * and causing exceptions during shutdown.
-     *
-     * @param url {@link java.net.URL} for the request.
-     * @param listener callback class that gets called on different events.
-     * @param executor {@link Executor} on which all callbacks will be called.
-     * @param priority priority of the request which should be one of the
-     *         {@link UrlRequest#REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*}
-     *         values.
-     * @return new request.
-     */
-    public abstract UrlRequest createRequest(String url,
-            UrlRequestListener listener, Executor executor, int priority);
-
-    /**
-     * @return {@code true} if the context is enabled.
-     */
-    abstract boolean isEnabled();
-
-    /**
-     * @return a human-readable version string of the context.
-     */
-    public abstract String getVersionString();
-
-    /**
-     * Shuts down the {@link UrlRequestContext} if there are no active requests,
-     * otherwise throws an exception.
-     *
-     * Cannot be called on network thread - the thread Cronet calls into
-     * Executor on (which is different from the thread the Executor invokes
-     * callbacks on). May block until all the {@code UrlRequestContext}'s
-     * resources have been cleaned up.
-     */
-    public abstract void shutdown();
-
-    /**
-     * Starts NetLog logging to a file. The NetLog is useful for debugging.
-     * The file can be viewed using a Chrome browser navigated to
-     * chrome://net-internals/#import
-     * @param fileName the complete file path. It must not be empty. If the file
-     *            exists, it is truncated before starting. If actively logging,
-     *            this method is ignored.
-     * @param logAll {@code true} to include basic events, user cookies,
-     *            credentials and all transferred bytes in the log.
-     *            {@code false} to just include basic events.
-     */
-    public abstract void startNetLogToFile(String fileName, boolean logAll);
-
-    /**
-     * Stops NetLog logging and flushes file to disk. If a logging session is
-     * not in progress, this call is ignored.
-     */
-    public abstract void stopNetLog();
-
-    /**
-     * Enables the network quality estimator, which collects and reports
-     * measurements of round trip time (RTT) and downstream throughput at
-     * various layers of the network stack. After enabling the estimator,
-     * listeners of RTT and throughput can be added with
-     * {@link #addRttListener} and {@link #addThroughputListener} and
-     * removed with {@link #removeRttListener} and
-     * {@link #removeThroughputListener}. The estimator uses memory and CPU
-     * only when enabled.
-     * @param executor an executor that will be used to notified all
-     *            added RTT and throughput listeners.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated public abstract void enableNetworkQualityEstimator(Executor executor);
-
-    /**
-     * Enables the network quality estimator for testing. This must be called
-     * before round trip time and throughput listeners are added. Set both
-     * boolean parameters to false for default behavior.
-     * @param useLocalHostRequests include requests to localhost in estimates.
-     * @param useSmallerResponses include small responses in throughput estimates.
-     * @param executor an {@link java.util.concurrent.Executor} on which all
-     *            listeners will be called.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated
-    abstract void enableNetworkQualityEstimatorForTesting(
-            boolean useLocalHostRequests, boolean useSmallerResponses, Executor executor);
-
-    /**
-     * Registers a listener that gets called whenever the network quality
-     * estimator witnesses a sample round trip time. This must be called
-     * after {@link #enableNetworkQualityEstimator}, and with throw an
-     * exception otherwise. Round trip times may be recorded at various layers
-     * of the network stack, including TCP, QUIC, and at the URL request layer.
-     * The listener is called on the {@link java.util.concurrent.Executor} that
-     * is passed to {@link #enableNetworkQualityEstimator}.
-     * @param listener the listener of round trip times.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated public abstract void addRttListener(NetworkQualityRttListener listener);
-
-    /**
-     * Removes a listener of round trip times if previously registered with
-     * {@link #addRttListener}. This should be called after a
-     * {@link NetworkQualityRttListener} is added in order to stop receiving
-     * observations.
-     * @param listener the listener of round trip times.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated public abstract void removeRttListener(NetworkQualityRttListener listener);
-
-    /**
-     * Registers a listener that gets called whenever the network quality
-     * estimator witnesses a sample throughput measurement. This must be called
-     * after {@link #enableNetworkQualityEstimator}. Throughput observations
-     * are computed by measuring bytes read over the active network interface
-     * at times when at least one URL response is being received. The listener
-     * is called on the {@link java.util.concurrent.Executor} that is passed to
-     * {@link #enableNetworkQualityEstimator}.
-     * @param listener the listener of throughput.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated
-    public abstract void addThroughputListener(NetworkQualityThroughputListener listener);
-
-    /**
-     * Removes a listener of throughput. This should be called after a
-     * {@link NetworkQualityThroughputListener} is added with
-     * {@link #addThroughputListener} in order to stop receiving observations.
-     * @param listener the listener of throughput.
-     * @deprecated not really deprecated but hidden for now as it's a prototype.
-     */
-    @Deprecated
-    public abstract void removeThroughputListener(NetworkQualityThroughputListener listener);
-
-    /**
-     * Creates a {@link UrlRequestContext} with the given
-     * {@link UrlRequestContextConfig}.
-     * @param context Android {@link Context}.
-     * @param config context configuration.
-     */
-    public static UrlRequestContext createContext(Context context,
-            UrlRequestContextConfig config) {
-        UrlRequestContext urlRequestContext = null;
-        if (config.userAgent().isEmpty()) {
-            config.setUserAgent(UserAgent.from(context));
-        }
-        if (!config.legacyMode()) {
-            urlRequestContext = createCronetContext(context, config);
-        }
-        if (urlRequestContext == null) {
-            // TODO(mef): Fallback to stub implementation. Once stub
-            // implementation is available merge with createCronetFactory.
-            urlRequestContext = createCronetContext(context, config);
-        }
-        Log.i(TAG, "Using network stack: "
-                + urlRequestContext.getVersionString());
-        return urlRequestContext;
-    }
-
-    private static UrlRequestContext createCronetContext(Context context,
-            UrlRequestContextConfig config) {
-        UrlRequestContext urlRequestContext = null;
-        try {
-            Class<? extends UrlRequestContext> contextClass =
-                    UrlRequestContext.class.getClassLoader()
-                            .loadClass(CRONET_URL_REQUEST_CONTEXT)
-                            .asSubclass(UrlRequestContext.class);
-            Constructor<? extends UrlRequestContext> constructor =
-                    contextClass.getConstructor(
-                            Context.class, UrlRequestContextConfig.class);
-            UrlRequestContext cronetContext =
-                    constructor.newInstance(context, config);
-            if (cronetContext.isEnabled()) {
-                urlRequestContext = cronetContext;
-            }
-        } catch (ClassNotFoundException e) {
-            // Leave as null.
-        } catch (Exception e) {
-            throw new IllegalStateException(
-                    "Cannot instantiate: " + CRONET_URL_REQUEST_CONTEXT,
-                    e);
-        }
-        return urlRequestContext;
-    }
-}
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java
index e240c5d..ff0c7779 100644
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java
+++ b/components/cronet/android/java/src/org/chromium/net/UrlRequestContextConfig.java
@@ -1,341 +1,26 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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.net;
 
-import org.json.JSONArray;
 import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
 
 /**
- * A config for UrlRequestContext, which allows runtime configuration of
- * UrlRequestContext.
+ * A config for CronetEngine, which allows runtime configuration of
+ * CronetEngine.
+ * @deprecated use {@link CronetEngine.Builder} instead.
  */
-public class UrlRequestContextConfig {
-    private final JSONObject mConfig;
-
-    /**
-     * Default config enables SPDY, disables QUIC, SDCH and http cache.
-     */
+public class UrlRequestContextConfig extends CronetEngine.Builder {
     public UrlRequestContextConfig() {
-        mConfig = new JSONObject();
-        enableLegacyMode(false);
-        enableQUIC(false);
-        enableHTTP2(true);
-        enableSDCH(false);
-        enableHttpCache(HTTP_CACHE_DISABLED, 0);
+        // Context will be passed in later when the ChromiumUrlRequestFactory
+        // or ChromiumUrlRequestContext is created.
+        super(null);
     }
 
-    /**
-     * Creates a config from a JSON string, which was serialized using
-     * {@link #toString}.
-     */
     public UrlRequestContextConfig(String json) throws JSONException {
-        mConfig = new JSONObject(json);
-    }
-
-    /**
-     * Overrides the user-agent header for all requests.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig setUserAgent(String userAgent) {
-        return putString(UrlRequestContextConfigList.USER_AGENT, userAgent);
-    }
-
-    String userAgent() {
-        return mConfig.optString(UrlRequestContextConfigList.USER_AGENT);
-    }
-
-    /**
-     * Sets directory for HTTP Cache and Cookie Storage. The directory must
-     * exist.
-     * @param value path to existing directory.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig setStoragePath(String value) {
-        if (!new File(value).isDirectory()) {
-            throw new IllegalArgumentException(
-                    "Storage path must be set to existing directory");
-        }
-
-        return putString(UrlRequestContextConfigList.STORAGE_PATH, value);
-    }
-
-    String storagePath() {
-        return mConfig.optString(UrlRequestContextConfigList.STORAGE_PATH);
-    }
-
-    /**
-     * Sets whether falling back to implementation based on system's
-     * {@link java.net.HttpURLConnection} implementation is enabled.
-     * Defaults to disabled.
-     * @return the config to facilitate chaining.
-     * @deprecated Not supported by the new API.
-     */
-    @Deprecated
-    public UrlRequestContextConfig enableLegacyMode(boolean value) {
-        return putBoolean(UrlRequestContextConfigList.ENABLE_LEGACY_MODE,
-                          value);
-    }
-
-    boolean legacyMode() {
-        return mConfig.optBoolean(
-                UrlRequestContextConfigList.ENABLE_LEGACY_MODE);
-    }
-
-    /**
-     * Overrides the name of the native library backing Cronet.
-     * @return the config to facilitate chaining.
-     */
-    UrlRequestContextConfig setLibraryName(String libName) {
-        return putString(UrlRequestContextConfigList.NATIVE_LIBRARY_NAME,
-                         libName);
-    }
-
-    String libraryName() {
-        return mConfig.optString(
-                UrlRequestContextConfigList.NATIVE_LIBRARY_NAME, "cronet");
-    }
-
-    /**
-     * Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protocol
-     * is enabled. Defaults to disabled.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig enableQUIC(boolean value) {
-        return putBoolean(UrlRequestContextConfigList.ENABLE_QUIC, value);
-    }
-
-    /**
-     * Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a>
-     * protocol is enabled. Defaults to enabled.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig enableHTTP2(boolean value) {
-        return putBoolean(UrlRequestContextConfigList.ENABLE_SPDY, value);
-    }
-
-    /**
-     * Sets whether
-     * <a
-     * href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/att-0441/Shared_Dictionary_Compression_over_HTTP.pdf">
-     * SDCH</a> compression is enabled. Defaults to disabled.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig enableSDCH(boolean value) {
-        return putBoolean(UrlRequestContextConfigList.ENABLE_SDCH, value);
-    }
-
-    /**
-     * Enables
-     * <a href="https://developer.chrome.com/multidevice/data-compression">Data
-     * Reduction Proxy</a>. Defaults to disabled.
-     * @param key key to use when authenticating with the proxy.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig enableDataReductionProxy(String key) {
-        return (putString(
-                UrlRequestContextConfigList.DATA_REDUCTION_PROXY_KEY, key));
-    }
-
-    /**
-     * Overrides
-     * <a href="https://developer.chrome.com/multidevice/data-compression">
-     * Data Reduction Proxy</a> configuration parameters with a primary
-     * proxy name, fallback proxy name, and a secure proxy check URL. Proxies
-     * are specified as [scheme://]host[:port]. Used for testing.
-     * @param primaryProxy the primary data reduction proxy to use.
-     * @param fallbackProxy a fallback data reduction proxy to use.
-     * @param secureProxyCheckUrl a URL to fetch to determine if using a secure
-     * proxy is allowed.
-     * @return the config to facilitate chaining.
-     * @hide
-     */
-    public UrlRequestContextConfig setDataReductionProxyOptions(
-            String primaryProxy,
-            String fallbackProxy,
-            String secureProxyCheckUrl) {
-        if (primaryProxy.isEmpty() || fallbackProxy.isEmpty()
-                || secureProxyCheckUrl.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Primary and fallback proxies and check url must be set");
-        }
-        putString(UrlRequestContextConfigList.DATA_REDUCTION_PRIMARY_PROXY,
-                primaryProxy);
-        putString(UrlRequestContextConfigList.DATA_REDUCTION_FALLBACK_PROXY,
-                fallbackProxy);
-        putString(UrlRequestContextConfigList
-                .DATA_REDUCTION_SECURE_PROXY_CHECK_URL, secureProxyCheckUrl);
-        return this;
-    }
-
-    /**
-     * Setting to disable HTTP cache. Some data may still be temporarily stored in memory.
-     * Passed to {@link #enableHttpCache}.
-     */
-    public static final int HTTP_CACHE_DISABLED = 0;
-
-    /**
-     * Setting to enable in-memory HTTP cache, including HTTP data.
-     * Passed to {@link #enableHttpCache}.
-     */
-    public static final int HTTP_CACHE_IN_MEMORY = 1;
-
-    /**
-     * Setting to enable on-disk cache, excluding HTTP data.
-     * {@link #setStoragePath} must be called prior to passing this constant to
-     * {@link #enableHttpCache}.
-     */
-    public static final int HTTP_CACHE_DISK_NO_HTTP = 2;
-
-    /**
-     * Setting to enable on-disk cache, including HTTP data.
-     * {@link #setStoragePath} must be called prior to passing this constant to
-     * {@link #enableHttpCache}.
-     */
-    public static final int HTTP_CACHE_DISK = 3;
-
-    /**
-     * Enables or disables caching of HTTP data and other information like QUIC
-     * server information.
-     * @param cacheMode control location and type of cached data.
-     * @param maxSize maximum size used to cache data (advisory and maybe
-     * exceeded at times).
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig enableHttpCache(int cacheMode, long maxSize) {
-        if (cacheMode == HTTP_CACHE_DISK || cacheMode == HTTP_CACHE_DISK_NO_HTTP) {
-            if (storagePath().isEmpty()) {
-                throw new IllegalArgumentException("Storage path must be set");
-            }
-        } else {
-            if (!storagePath().isEmpty()) {
-                throw new IllegalArgumentException(
-                        "Storage path must be empty");
-            }
-        }
-        putBoolean(UrlRequestContextConfigList.LOAD_DISABLE_CACHE,
-                cacheMode == HTTP_CACHE_DISABLED || cacheMode == HTTP_CACHE_DISK_NO_HTTP);
-        putLong(UrlRequestContextConfigList.HTTP_CACHE_MAX_SIZE, maxSize);
-
-        switch (cacheMode) {
-            case HTTP_CACHE_DISABLED:
-                return putString(UrlRequestContextConfigList.HTTP_CACHE,
-                        UrlRequestContextConfigList.HTTP_CACHE_DISABLED);
-            case HTTP_CACHE_DISK_NO_HTTP:
-            case HTTP_CACHE_DISK:
-                return putString(UrlRequestContextConfigList.HTTP_CACHE,
-                        UrlRequestContextConfigList.HTTP_CACHE_DISK);
-
-            case HTTP_CACHE_IN_MEMORY:
-                return putString(UrlRequestContextConfigList.HTTP_CACHE,
-                        UrlRequestContextConfigList.HTTP_CACHE_MEMORY);
-        }
-        return this;
-    }
-
-    /**
-     * Adds hint that {@code host} supports QUIC.
-     * Note that {@link #enableHttpCache enableHttpCache}
-     * ({@link HttpCache#DISK DISK}) is needed to take advantage of 0-RTT
-     * connection establishment between sessions.
-     *
-     * @param host hostname of the server that supports QUIC.
-     * @param port host of the server that supports QUIC.
-     * @param alternatePort alternate port to use for QUIC.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig addQuicHint(String host,
-            int port,
-            int alternatePort) {
-        if (host.contains("/")) {
-            throw new IllegalArgumentException("Illegal QUIC Hint Host: "
-                                               + host);
-        }
-        try {
-            JSONArray quicHints = mConfig.optJSONArray(
-                    UrlRequestContextConfigList.QUIC_HINTS);
-            if (quicHints == null) {
-                quicHints = new JSONArray();
-                mConfig.put(UrlRequestContextConfigList.QUIC_HINTS, quicHints);
-            }
-
-            JSONObject hint = new JSONObject();
-            hint.put(UrlRequestContextConfigList.QUIC_HINT_HOST, host);
-            hint.put(UrlRequestContextConfigList.QUIC_HINT_PORT, port);
-            hint.put(UrlRequestContextConfigList.QUIC_HINT_ALT_PORT,
-                     alternatePort);
-            quicHints.put(hint);
-        } catch (JSONException e) {
-            // Intentionally do nothing.
-        }
-        return this;
-    }
-
-    /**
-     * Sets experimental QUIC connection options, overwriting any pre-existing
-     * options. List of options is subject to change.
-     *
-     * @param quicConnectionOptions comma-separated QUIC options (for example
-     * "PACE,IW10") to use if QUIC is enabled.
-     * @return the config to facilitate chaining.
-     */
-    public UrlRequestContextConfig setExperimentalQuicConnectionOptions(
-            String quicConnectionOptions) {
-        return putString(UrlRequestContextConfigList.QUIC_OPTIONS,
-                         quicConnectionOptions);
-    }
-
-    /**
-     * Get JSON string representation of the config.
-     */
-    @Override
-    public String toString() {
-        return mConfig.toString();
-    }
-
-    /**
-     * Sets a boolean value in the config. Returns a reference to the same
-     * config object, so you can chain put calls together.
-     * @return the config to facilitate chaining.
-     */
-    private UrlRequestContextConfig putBoolean(String key, boolean value) {
-        try {
-            mConfig.put(key, value);
-        } catch (JSONException e) {
-            // Intentionally do nothing.
-        }
-        return this;
-    }
-
-    /**
-     * Sets a long value in the config. Returns a reference to the same
-     * config object, so you can chain put calls together.
-     * @return the config to facilitate chaining.
-     */
-    private UrlRequestContextConfig putLong(String key, long value)  {
-        try {
-            mConfig.put(key, value);
-        } catch (JSONException e) {
-            // Intentionally do nothing.
-        }
-        return this;
-    }
-
-    /**
-     * Sets a string value in the config. Returns a reference to the same
-     * config object, so you can chain put calls together.
-     * @return the config to facilitate chaining.
-     */
-    private UrlRequestContextConfig putString(String key, String value) {
-        try {
-            mConfig.put(key, value);
-        } catch (JSONException e) {
-            // Intentionally do nothing.
-        }
-        return this;
+        // Context will be passed in later when the ChromiumUrlRequestFactory
+        // or ChromiumUrlRequestContext is created.
+        super(null, json);
     }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestListener.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestListener.java
index 49d0a93a7..f3803f06 100644
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequestListener.java
+++ b/components/cronet/android/java/src/org/chromium/net/UrlRequestListener.java
@@ -9,8 +9,8 @@
 /**
  * Users of Cronet extend this class to receive callbacks indicating the
  * progress of a {@link UrlRequest} being processed. An instance of this class
- * is passed in when the {@code UrlRequest} is created by
- * {@link UrlRequestContext#createRequest}
+ * is passed in to {@link UrlRequest.Builder#UrlRequest.Builder UrlRequest.Builder()}
+ * when constructing the {@code UrlRequest}.
  * <p>
  * Note:  All methods will be called on the thread of the
  * {@link java.util.concurrent.Executor} used during construction of the
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java
index a7db527..e5a7090 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java
@@ -146,7 +146,8 @@
             // TODO(xunjieli): Think of a less fragile way, since getLength() can be
             // potentially called in other places in the future.
             if (mInitialContentLength == -1) {
-                return mBuffer.position();
+                // Account for the fact that setConnected() flip()s mBuffer.
+                return mConnected ? mBuffer.limit() : mBuffer.position();
             }
             return mInitialContentLength;
         }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
index 89bf7b690..8726e2a 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
@@ -7,10 +7,10 @@
 import android.util.Pair;
 
 import org.chromium.base.Log;
+import org.chromium.net.CronetEngine;
 import org.chromium.net.ExtendedResponseInfo;
 import org.chromium.net.ResponseInfo;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestContext;
 import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlRequestListener;
 
@@ -36,9 +36,9 @@
 class CronetHttpURLConnection extends HttpURLConnection {
     private static final String TAG = "cr.CronetHttpURLConn";
     private static final String CONTENT_LENGTH = "Content-Length";
-    private final UrlRequestContext mUrlRequestContext;
+    private final CronetEngine mCronetEngine;
     private final MessageLoop mMessageLoop;
-    private final UrlRequest mRequest;
+    private UrlRequest mRequest;
     private final List<Pair<String, String>> mRequestHeaders;
 
     private CronetInputStream mInputStream;
@@ -48,13 +48,10 @@
     private boolean mOnRedirectCalled = false;
     private boolean mHasResponse = false;
 
-    public CronetHttpURLConnection(URL url,
-            UrlRequestContext urlRequestContext) {
+    public CronetHttpURLConnection(URL url, CronetEngine cronetEngine) {
         super(url);
-        mUrlRequestContext = urlRequestContext;
+        mCronetEngine = cronetEngine;
         mMessageLoop = new MessageLoop();
-        mRequest = mUrlRequestContext.createRequest(url.toString(),
-                new CronetUrlRequestListener(), mMessageLoop);
         mInputStream = new CronetInputStream(this);
         mRequestHeaders = new ArrayList<Pair<String, String>>();
     }
@@ -255,9 +252,12 @@
         if (connected) {
             return;
         }
+        final UrlRequest.Builder requestBuilder = new UrlRequest.Builder(
+                getURL().toString(), new CronetUrlRequestListener(), mMessageLoop, mCronetEngine);
         if (doOutput) {
             if (mOutputStream != null) {
-                mRequest.setUploadDataProvider(mOutputStream.getUploadDataProvider(), mMessageLoop);
+                requestBuilder.setUploadDataProvider(
+                        mOutputStream.getUploadDataProvider(), mMessageLoop);
                 if (getRequestProperty(CONTENT_LENGTH) == null && !isChunkedUpload()) {
                     addRequestProperty(CONTENT_LENGTH,
                             Long.toString(mOutputStream.getUploadDataProvider().getLength()));
@@ -277,12 +277,13 @@
             }
         }
         for (Pair<String, String> requestHeader : mRequestHeaders) {
-            mRequest.addHeader(requestHeader.first, requestHeader.second);
+            requestBuilder.addHeader(requestHeader.first, requestHeader.second);
         }
         if (!getUseCaches()) {
-            mRequest.disableCache();
+            requestBuilder.disableCache();
         }
         connected = true;
+        mRequest = requestBuilder.build();
         // Start the request.
         mRequest.start();
     }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java
index 952fc87a..c962e74 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java
@@ -4,7 +4,7 @@
 
 package org.chromium.net.urlconnection;
 
-import org.chromium.net.UrlRequestContext;
+import org.chromium.net.CronetEngine;
 
 import java.io.IOException;
 import java.net.Proxy;
@@ -23,10 +23,10 @@
  * listed {@link CronetURLStreamHandlerFactory here}.
  */
 public class CronetHttpURLStreamHandler extends URLStreamHandler {
-    private final UrlRequestContext mUrlRequestContext;
+    private final CronetEngine mCronetEngine;
 
-    public CronetHttpURLStreamHandler(UrlRequestContext urlRequestContext) {
-        mUrlRequestContext = urlRequestContext;
+    public CronetHttpURLStreamHandler(CronetEngine cronetEngine) {
+        mCronetEngine = cronetEngine;
     }
 
     /**
@@ -37,7 +37,7 @@
     public URLConnection openConnection(URL url) throws IOException {
         String protocol = url.getProtocol();
         if ("http".equals(protocol) || "https".equals(protocol)) {
-            return new CronetHttpURLConnection(url, mUrlRequestContext);
+            return new CronetHttpURLConnection(url, mCronetEngine);
         }
         throw new UnsupportedOperationException(
                 "Unexpected protocol:" + protocol);
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java
index 2363a8f..aadcd7b 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java
@@ -4,10 +4,7 @@
 
 package org.chromium.net.urlconnection;
 
-import android.content.Context;
-
-import org.chromium.net.UrlRequestContext;
-import org.chromium.net.UrlRequestContextConfig;
+import org.chromium.net.CronetEngine;
 
 import java.net.URLStreamHandler;
 import java.net.URLStreamHandlerFactory;
@@ -42,21 +39,19 @@
  */
 public class CronetURLStreamHandlerFactory
         implements URLStreamHandlerFactory {
-    private final UrlRequestContext mRequestContext;
+    private final CronetEngine mCronetEngine;
 
     /**
      * Creates a {@link CronetURLStreamHandlerFactory} to handle HTTP and HTTPS
      * traffic.
-     * @param context application context.
-     * @param config the configuration to be used.
+     * @param cronetEngine the {@link CronetEngine} to be used.
      * @throws NullPointerException if config is null.
      */
-    public CronetURLStreamHandlerFactory(Context context,
-            UrlRequestContextConfig config) {
-        if (config == null) {
-            throw new NullPointerException("UrlRequestContextConfig is null.");
+    public CronetURLStreamHandlerFactory(CronetEngine cronetEngine) {
+        if (cronetEngine == null) {
+            throw new NullPointerException("CronetEngine is null.");
         }
-        mRequestContext = UrlRequestContext.createContext(context, config);
+        mCronetEngine = cronetEngine;
     }
 
     /**
@@ -66,7 +61,7 @@
     @Override
     public URLStreamHandler createURLStreamHandler(String protocol) {
         if ("http".equals(protocol) || "https".equals(protocol)) {
-            return new CronetHttpURLStreamHandler(mRequestContext);
+            return new CronetHttpURLStreamHandler(mCronetEngine);
         }
         return null;
     }
diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
index 6f450364c..2682771 100644
--- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
+++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
@@ -15,13 +15,12 @@
 import android.widget.TextView;
 
 import org.chromium.base.Log;
+import org.chromium.net.CronetEngine;
 import org.chromium.net.ExtendedResponseInfo;
 import org.chromium.net.ResponseInfo;
 import org.chromium.net.UploadDataProvider;
 import org.chromium.net.UploadDataSink;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestContext;
-import org.chromium.net.UrlRequestContextConfig;
 import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlRequestListener;
 
@@ -39,7 +38,7 @@
 public class CronetSampleActivity extends Activity {
     private static final String TAG = "cr.CronetSample";
 
-    private UrlRequestContext mRequestContext;
+    private CronetEngine mCronetEngine;
 
     private String mUrl;
     private boolean mLoading = false;
@@ -157,12 +156,12 @@
         mResultText = (TextView) findViewById(R.id.resultView);
         mReceiveDataText = (TextView) findViewById(R.id.dataView);
 
-        UrlRequestContextConfig myConfig = new UrlRequestContextConfig();
-        myConfig.enableHttpCache(UrlRequestContextConfig.HTTP_CACHE_IN_MEMORY, 100 * 1024)
+        CronetEngine.Builder myBuilder = new CronetEngine.Builder(this);
+        myBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
                 .enableHTTP2(true)
                 .enableQUIC(true);
 
-        mRequestContext = UrlRequestContext.createContext(this, myConfig);
+        mCronetEngine = myBuilder.build();
 
         String appUrl = (getIntent() != null ? getIntent().getDataString() : null);
         if (appUrl == null) {
@@ -193,13 +192,14 @@
         alert.show();
     }
 
-    private void applyPostDataToUrlRequest(UrlRequest request, Executor executor, String postData) {
+    private void applyPostDataToUrlRequestBuilder(
+            UrlRequest.Builder builder, Executor executor, String postData) {
         if (postData != null && postData.length() > 0) {
             UploadDataProvider uploadDataProvider =
                     new SimpleUploadDataProvider(postData.getBytes());
-            request.setHttpMethod("POST");
-            request.addHeader("Content-Type", "application/x-www-form-urlencoded");
-            request.setUploadDataProvider(uploadDataProvider, executor);
+            builder.setHttpMethod("POST");
+            builder.addHeader("Content-Type", "application/x-www-form-urlencoded");
+            builder.setUploadDataProvider(uploadDataProvider, executor);
         }
     }
 
@@ -214,9 +214,9 @@
 
         Executor executor = Executors.newSingleThreadExecutor();
         UrlRequestListener listener = new SimpleUrlRequestListener();
-        UrlRequest request = mRequestContext.createRequest(url, listener, executor);
-        applyPostDataToUrlRequest(request, executor, postData);
-        request.start();
+        UrlRequest.Builder builder = new UrlRequest.Builder(url, listener, executor, mCronetEngine);
+        applyPostDataToUrlRequestBuilder(builder, executor, postData);
+        builder.build().start();
     }
 
     /**
@@ -241,12 +241,12 @@
     }
 
     private void startNetLog() {
-        mRequestContext.startNetLogToFile(
+        mCronetEngine.startNetLogToFile(
                 Environment.getExternalStorageDirectory().getPath() + "/cronet_sample_netlog.json",
                 false);
     }
 
     private void stopNetLog() {
-        mRequestContext.stopNetLog();
+        mCronetEngine.stopNetLog();
     }
 }
diff --git a/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java b/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
index c9e1391..6d53f528 100644
--- a/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
+++ b/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
@@ -111,7 +111,7 @@
         private final Protocol mProtocol;
         private final URL mUrl;
         private final String mName;
-        private final UrlRequestContext mCronetContext;
+        private final CronetEngine mCronetEngine;
         // Size in bytes of content being uploaded or downloaded.
         private final int mLength;
         // How many requests to execute.
@@ -178,13 +178,14 @@
                 throw new IllegalArgumentException("Bad URL: " + getConfigString("HOST") + ":"
                         + port + "/" + resource);
             }
-            final UrlRequestContextConfig cronetConfig = new UrlRequestContextConfig();
+            final CronetEngine.Builder cronetEngineBuilder =
+                    new CronetEngine.Builder(CronetPerfTestActivity.this);
             if (mProtocol == Protocol.QUIC) {
-                cronetConfig.enableQUIC(true);
-                cronetConfig.addQuicHint(getConfigString("HOST"), getConfigInt("QUIC_PORT"),
+                cronetEngineBuilder.enableQUIC(true);
+                cronetEngineBuilder.addQuicHint(getConfigString("HOST"), getConfigInt("QUIC_PORT"),
                         getConfigInt("QUIC_PORT"));
             }
-            mCronetContext = new CronetUrlRequestContext(CronetPerfTestActivity.this, cronetConfig);
+            mCronetEngine = cronetEngineBuilder.build();
             mName = buildBenchmarkName(mode, direction, protocol, concurrency, mIterations);
             mConcurrency = concurrency;
             mResults = results;
@@ -216,8 +217,8 @@
         @SuppressLint("NewApi")
         private void startLogging() {
             if (getConfigBoolean("CAPTURE_NETLOG")) {
-                mCronetContext.startNetLogToFile(getFilesDir().getPath() + "/" + mName + ".json",
-                        false);
+                mCronetEngine.startNetLogToFile(
+                        getFilesDir().getPath() + "/" + mName + ".json", false);
             }
             if (getConfigBoolean("CAPTURE_TRACE")) {
                 Debug.startMethodTracing(getFilesDir().getPath() + "/" + mName + ".trace");
@@ -229,7 +230,7 @@
 
         private void stopLogging() {
             if (getConfigBoolean("CAPTURE_NETLOG")) {
-                mCronetContext.stopNetLog();
+                mCronetEngine.stopNetLog();
             }
             if (getConfigBoolean("CAPTURE_TRACE") || getConfigBoolean("CAPTURE_SAMPLED_TRACE")) {
                 Debug.stopMethodTracing();
@@ -343,13 +344,14 @@
                         initiateRequest(buffer);
                     }
                 };
-                final UrlRequest request = mCronetContext.createRequest(mUrl.toString(),
-                        new Listener(buffer, completionCallback), mWorkQueueExecutor);
+                final UrlRequest.Builder builder = new UrlRequest.Builder(mUrl.toString(),
+                        new Listener(buffer, completionCallback), mWorkQueueExecutor,
+                        mCronetEngine);
                 if (mDirection == Direction.UP) {
-                    request.setUploadDataProvider(new Uploader(buffer), mWorkQueueExecutor);
-                    request.addHeader("Content-Type", "application/octet-stream");
+                    builder.setUploadDataProvider(new Uploader(buffer), mWorkQueueExecutor);
+                    builder.addHeader("Content-Type", "application/octet-stream");
                 }
-                request.start();
+                builder.build().start();
             }
 
             private class Uploader extends UploadDataProvider {
@@ -479,7 +481,7 @@
                     break;
                 case CRONET_HUC: {
                     final CronetHttpURLStreamHandler cronetStreamHandler =
-                            new CronetHttpURLStreamHandler(mCronetContext);
+                            new CronetHttpURLStreamHandler(mCronetEngine);
                     for (int i = 0; i < mIterations; i++) {
                         tasks.add(new CronetHttpURLConnectionFetchTask(cronetStreamHandler));
                     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/ContextInitTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/ContextInitTest.java
index 439ea512..c8e108f 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/ContextInitTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/ContextInitTest.java
@@ -138,11 +138,11 @@
         // crashes like crbug.com/453845
         final CronetTestActivity activity = launchCronetTestApp();
         HttpUrlRequestFactory firstFactory =
-                HttpUrlRequestFactory.createFactory(activity, activity.getContextConfig());
+                HttpUrlRequestFactory.createFactory(activity, activity.getCronetEngineBuilder());
         HttpUrlRequestFactory secondFactory = HttpUrlRequestFactory.createFactory(
-                activity.getApplicationContext(), activity.getContextConfig());
+                activity.getApplicationContext(), activity.getCronetEngineBuilder());
         HttpUrlRequestFactory thirdFactory = HttpUrlRequestFactory.createFactory(
-                new ContextWrapper(activity), activity.getContextConfig());
+                new ContextWrapper(activity), activity.getCronetEngineBuilder());
         // Meager attempt to extend lifetimes to ensure they're concurrently
         // alive.
         firstFactory.getName();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 395a8b1..f8c347ac 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -23,7 +23,7 @@
 import java.util.concurrent.Executor;
 
 /**
- * Test CronetUrlRequestContext.
+ * Test CronetEngine.
  */
 public class CronetUrlRequestContextTest extends CronetTestBase {
     // URLs used for tests.
@@ -53,11 +53,11 @@
         @Override
         public void run() {
             mRunBlocker.block();
-            UrlRequestContext requestContext = mActivity.initRequestContext();
+            CronetEngine cronetEngine = mActivity.initCronetEngine();
             mListener = new TestUrlRequestListener();
-            UrlRequest urlRequest =
-                    requestContext.createRequest(mUrl, mListener, mListener.getExecutor());
-            urlRequest.start();
+            UrlRequest.Builder urlRequestBuilder =
+                    new UrlRequest.Builder(mUrl, mListener, mListener.getExecutor(), cronetEngine);
+            urlRequestBuilder.build().start();
             mListener.blockForDone();
         }
     }
@@ -70,7 +70,7 @@
         @Override
         public void onSucceeded(UrlRequest request, ExtendedResponseInfo info) {
             super.onSucceeded(request, info);
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
         }
 
         @Override
@@ -78,7 +78,7 @@
                 ResponseInfo info,
                 UrlRequestException error) {
             super.onFailed(request, info, error);
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
         }
     }
 
@@ -127,21 +127,19 @@
     public void testConfigUserAgent() throws Exception {
         String userAgentName = "User-Agent";
         String userAgentValue = "User-Agent-Value";
-        UrlRequestContextConfig config = new UrlRequestContextConfig();
-        config.setUserAgent(userAgentValue);
-        config.setLibraryName("cronet_tests");
-        String[] commandLineArgs = {
-                CronetTestActivity.CONFIG_KEY, config.toString()
-        };
+        CronetEngine.Builder cronetEngineBuilder = new CronetEngine.Builder(mActivity);
+        cronetEngineBuilder.setUserAgent(userAgentValue);
+        cronetEngineBuilder.setLibraryName("cronet_tests");
+        String[] commandLineArgs = {CronetTestActivity.CONFIG_KEY, cronetEngineBuilder.toString()};
         mActivity = launchCronetTestAppWithUrlAndCommandLineArgs(TEST_URL,
                 commandLineArgs);
         assertTrue(NativeTestServer.startNativeTestServer(
                 getInstrumentation().getTargetContext()));
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoHeaderURL(userAgentName), listener,
-                listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder =
+                new UrlRequest.Builder(NativeTestServer.getEchoHeaderURL(userAgentName), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         assertEquals(userAgentValue, listener.mResponseAsString);
     }
@@ -152,9 +150,9 @@
         mActivity = launchCronetTestAppAndSkipFactoryInit();
 
         // Ensure native code is loaded before trying to start test server.
-        UrlRequestContext.createContext(
-                getInstrumentation().getTargetContext(),
-                new UrlRequestContextConfig().setLibraryName("cronet_tests"))
+        new CronetEngine.Builder(getInstrumentation().getTargetContext())
+                .setLibraryName("cronet_tests")
+                .build()
                 .shutdown();
 
         assertTrue(NativeTestServer.startNativeTestServer(
@@ -167,23 +165,22 @@
         // Enable the Data Reduction Proxy and configure it to use the test
         // server as its primary proxy, and to check successfully that this
         // proxy is OK to use.
-        UrlRequestContextConfig config = new UrlRequestContextConfig();
-        config.enableDataReductionProxy("test-key");
-        config.setDataReductionProxyOptions(
-                serverHostPort, "unused.net:9999",
+        CronetEngine.Builder cronetEngineBuilder =
+                new CronetEngine.Builder(getInstrumentation().getTargetContext());
+        cronetEngineBuilder.enableDataReductionProxy("test-key");
+        cronetEngineBuilder.setDataReductionProxyOptions(serverHostPort, "unused.net:9999",
                 NativeTestServer.getFileURL("/secureproxychecksuccess.txt"));
-        config.setLibraryName("cronet_tests");
-        mActivity.mUrlRequestContext = UrlRequestContext.createContext(
-                getInstrumentation().getTargetContext(), config);
+        cronetEngineBuilder.setLibraryName("cronet_tests");
+        mActivity.mCronetEngine = cronetEngineBuilder.build();
         TestUrlRequestListener listener = new TestUrlRequestListener();
 
         // Construct and start a request that can only be returned by the test
         // server. This request will fail if the configuration logic for the
         // Data Reduction Proxy is not used.
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                "http://DomainThatDoesnt.Resolve/datareductionproxysuccess.txt",
-                listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                "http://DomainThatDoesnt.Resolve/datareductionproxysuccess.txt", listener,
+                listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
 
         // Verify that the request is successful and that the Data Reduction
@@ -201,23 +198,23 @@
         mActivity = launchCronetTestApp();
         TestNetworkQualityListener networkQualityListener = new TestNetworkQualityListener();
         try {
-            mActivity.mUrlRequestContext.addRttListener(networkQualityListener);
+            mActivity.mCronetEngine.addRttListener(networkQualityListener);
             fail("Should throw an exception.");
         } catch (IllegalStateException e) {
         }
         try {
-            mActivity.mUrlRequestContext.addThroughputListener(networkQualityListener);
+            mActivity.mCronetEngine.addThroughputListener(networkQualityListener);
             fail("Should throw an exception.");
         } catch (IllegalStateException e) {
         }
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest urlRequest =
+                mActivity.mCronetEngine.createRequest(TEST_URL, listener, listener.getExecutor());
         urlRequest.start();
         listener.blockForDone();
         assertEquals(0, networkQualityListener.rttObservationCount());
         assertEquals(0, networkQualityListener.throughputObservationCount());
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
     }
 
     @SmallTest
@@ -226,21 +223,20 @@
         mActivity = launchCronetTestApp();
         TestExecutor testExecutor = new TestExecutor();
         TestNetworkQualityListener networkQualityListener = new TestNetworkQualityListener();
-        mActivity.mUrlRequestContext.enableNetworkQualityEstimatorForTesting(
-                true, true, testExecutor);
-        mActivity.mUrlRequestContext.addRttListener(networkQualityListener);
-        mActivity.mUrlRequestContext.addThroughputListener(networkQualityListener);
-        mActivity.mUrlRequestContext.removeRttListener(networkQualityListener);
-        mActivity.mUrlRequestContext.removeThroughputListener(networkQualityListener);
+        mActivity.mCronetEngine.enableNetworkQualityEstimatorForTesting(true, true, testExecutor);
+        mActivity.mCronetEngine.addRttListener(networkQualityListener);
+        mActivity.mCronetEngine.addThroughputListener(networkQualityListener);
+        mActivity.mCronetEngine.removeRttListener(networkQualityListener);
+        mActivity.mCronetEngine.removeThroughputListener(networkQualityListener);
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest urlRequest =
+                mActivity.mCronetEngine.createRequest(TEST_URL, listener, listener.getExecutor());
         urlRequest.start();
         listener.blockForDone();
         testExecutor.runAllTasks();
         assertEquals(0, networkQualityListener.rttObservationCount());
         assertEquals(0, networkQualityListener.throughputObservationCount());
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
     }
 
     @SmallTest
@@ -249,19 +245,18 @@
         mActivity = launchCronetTestApp();
         TestExecutor testExecutor = new TestExecutor();
         TestNetworkQualityListener networkQualityListener = new TestNetworkQualityListener();
-        mActivity.mUrlRequestContext.enableNetworkQualityEstimatorForTesting(
-                true, true, testExecutor);
-        mActivity.mUrlRequestContext.addRttListener(networkQualityListener);
-        mActivity.mUrlRequestContext.addThroughputListener(networkQualityListener);
+        mActivity.mCronetEngine.enableNetworkQualityEstimatorForTesting(true, true, testExecutor);
+        mActivity.mCronetEngine.addRttListener(networkQualityListener);
+        mActivity.mCronetEngine.addThroughputListener(networkQualityListener);
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest urlRequest =
+                mActivity.mCronetEngine.createRequest(TEST_URL, listener, listener.getExecutor());
         urlRequest.start();
         listener.blockForDone();
         testExecutor.runAllTasks();
         assertTrue(networkQualityListener.rttObservationCount() > 0);
         assertTrue(networkQualityListener.throughputObservationCount() > 0);
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
     }
 
     @SmallTest
@@ -272,11 +267,12 @@
         // Block listener when response starts to verify that shutdown fails
         // if there are active requests.
         listener.setAutoAdvance(false);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = urlRequestBuilder.build();
         urlRequest.start();
         try {
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
             fail("Should throw an exception");
         } catch (Exception e) {
             assertEquals("Cannot shutdown with active requests.",
@@ -286,7 +282,7 @@
         listener.waitForNextStep();
         assertEquals(ResponseStep.ON_RESPONSE_STARTED, listener.mResponseStep);
         try {
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
             fail("Should throw an exception");
         } catch (Exception e) {
             assertEquals("Cannot shutdown with active requests.",
@@ -297,7 +293,7 @@
         listener.waitForNextStep();
         assertEquals(ResponseStep.ON_READ_COMPLETED, listener.mResponseStep);
         try {
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
             fail("Should throw an exception");
         } catch (Exception e) {
             assertEquals("Cannot shutdown with active requests.",
@@ -335,18 +331,18 @@
 
         // Create new request context, but its initialization on the main thread
         // will be stuck behind blockingTask.
-        final UrlRequestContext requestContext = activity.initRequestContext();
+        final CronetEngine cronetEngine = activity.initCronetEngine();
         // Unblock the main thread, so context gets initialized and shutdown on
         // it.
         block.open();
         // Shutdown will wait for init to complete on main thread.
-        requestContext.shutdown();
+        cronetEngine.shutdown();
         // Verify that context is shutdown.
         try {
-            requestContext.stopNetLog();
+            cronetEngine.stopNetLog();
             fail("Should throw an exception.");
         } catch (Exception e) {
-            assertEquals("Context is shut down.", e.getMessage());
+            assertEquals("Engine is shut down.", e.getMessage());
         }
     }
 
@@ -360,15 +356,15 @@
         Runnable blockingTask = new Runnable() {
             public void run() {
                 // Create new request context, loading the library.
-                final UrlRequestContext requestContext = activity.initRequestContext();
+                final CronetEngine cronetEngine = activity.initCronetEngine();
                 // Shutdown right after init.
-                requestContext.shutdown();
+                cronetEngine.shutdown();
                 // Verify that context is shutdown.
                 try {
-                    requestContext.stopNetLog();
+                    cronetEngine.stopNetLog();
                     fail("Should throw an exception.");
                 } catch (Exception e) {
-                    assertEquals("Context is shut down.", e.getMessage());
+                    assertEquals("Engine is shut down.", e.getMessage());
                 }
                 block.open();
             }
@@ -383,12 +379,11 @@
     public void testMultipleShutdown() throws Exception {
         mActivity = launchCronetTestApp();
         try {
-            mActivity.mUrlRequestContext.shutdown();
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
+            mActivity.mCronetEngine.shutdown();
             fail("Should throw an exception");
         } catch (Exception e) {
-            assertEquals("Context is shut down.",
-                         e.getMessage());
+            assertEquals("Engine is shut down.", e.getMessage());
         }
     }
 
@@ -397,9 +392,9 @@
     public void testShutdownAfterError() throws Exception {
         mActivity = launchCronetTestApp();
         TestUrlRequestListener listener = new ShutdownTestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                MOCK_CRONET_TEST_FAILED_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(MOCK_CRONET_TEST_FAILED_URL,
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         assertTrue(listener.mOnErrorCalled);
     }
@@ -412,11 +407,12 @@
         // Block listener when response starts to verify that shutdown fails
         // if there are active requests.
         listener.setAutoAdvance(false);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = urlRequestBuilder.build();
         urlRequest.start();
         try {
-            mActivity.mUrlRequestContext.shutdown();
+            mActivity.mCronetEngine.shutdown();
             fail("Should throw an exception");
         } catch (Exception e) {
             assertEquals("Cannot shutdown with active requests.",
@@ -425,7 +421,7 @@
         listener.waitForNextStep();
         assertEquals(ResponseStep.ON_RESPONSE_STARTED, listener.mResponseStep);
         urlRequest.cancel();
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
     }
 
     @SmallTest
@@ -434,21 +430,20 @@
         Context context = getInstrumentation().getTargetContext();
         File directory = new File(PathUtils.getDataDirectory(context));
         File file = File.createTempFile("cronet", "json", directory);
-        CronetUrlRequestContext requestContext = new CronetUrlRequestContext(
-                context,
-                new UrlRequestContextConfig().setLibraryName("cronet_tests"));
+        CronetEngine cronetEngine = new CronetUrlRequestContext(
+                new CronetEngine.Builder(context).setLibraryName("cronet_tests"));
         // Start NetLog immediately after the request context is created to make
         // sure that the call won't crash the app even when the native request
         // context is not fully initialized. See crbug.com/470196.
-        requestContext.startNetLogToFile(file.getPath(), false);
+        cronetEngine.startNetLogToFile(file.getPath(), false);
 
         // Start a request.
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest request = requestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        request.start();
+        UrlRequest.Builder urlRequestBuilder =
+                new UrlRequest.Builder(TEST_URL, listener, listener.getExecutor(), cronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
-        requestContext.stopNetLog();
+        cronetEngine.stopNetLog();
         assertTrue(file.exists());
         assertTrue(file.length() != 0);
         assertFalse(hasBytesInNetLog(file));
@@ -461,21 +456,20 @@
     public void testNetLogAfterShutdown() throws Exception {
         mActivity = launchCronetTestApp();
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
 
         File directory = new File(PathUtils.getDataDirectory(
                 getInstrumentation().getTargetContext()));
         File file = File.createTempFile("cronet", "json", directory);
         try {
-            mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(),
-                    false);
+            mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
             fail("Should throw an exception.");
         } catch (Exception e) {
-            assertEquals("Context is shut down.", e.getMessage());
+            assertEquals("Engine is shut down.", e.getMessage());
         }
         assertFalse(hasBytesInNetLog(file));
         assertTrue(file.delete());
@@ -490,17 +484,17 @@
                 getInstrumentation().getTargetContext()));
         File file = File.createTempFile("cronet", "json", directory);
         // Start NetLog multiple times.
-        mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(), false);
-        mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(), false);
-        mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(), false);
-        mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(), false);
+        mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
+        mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
+        mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
+        mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
         // Start a request.
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
-        mActivity.mUrlRequestContext.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
         assertTrue(file.exists());
         assertTrue(file.length() != 0);
         assertFalse(hasBytesInNetLog(file));
@@ -515,19 +509,19 @@
         File directory = new File(PathUtils.getDataDirectory(
                 getInstrumentation().getTargetContext()));
         File file = File.createTempFile("cronet", "json", directory);
-        mActivity.mUrlRequestContext.startNetLogToFile(file.getPath(), false);
+        mActivity.mCronetEngine.startNetLogToFile(file.getPath(), false);
         // Start a request.
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         // Stop NetLog multiple times.
-        mActivity.mUrlRequestContext.stopNetLog();
-        mActivity.mUrlRequestContext.stopNetLog();
-        mActivity.mUrlRequestContext.stopNetLog();
-        mActivity.mUrlRequestContext.stopNetLog();
-        mActivity.mUrlRequestContext.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
+        mActivity.mCronetEngine.stopNetLog();
         assertTrue(file.exists());
         assertTrue(file.length() != 0);
         assertFalse(hasBytesInNetLog(file));
@@ -541,18 +535,17 @@
         Context context = getInstrumentation().getTargetContext();
         File directory = new File(PathUtils.getDataDirectory(context));
         File file = File.createTempFile("cronet", "json", directory);
-        CronetUrlRequestContext requestContext = new CronetUrlRequestContext(
-                context,
-                new UrlRequestContextConfig().setLibraryName("cronet_tests"));
+        CronetEngine cronetEngine = new CronetUrlRequestContext(
+                new CronetEngine.Builder(context).setLibraryName("cronet_tests"));
         // Start NetLog with logAll as true.
-        requestContext.startNetLogToFile(file.getPath(), true);
+        cronetEngine.startNetLogToFile(file.getPath(), true);
         // Start a request.
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest request = requestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        request.start();
+        UrlRequest.Builder urlRequestBuilder =
+                new UrlRequest.Builder(TEST_URL, listener, listener.getExecutor(), cronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
-        requestContext.stopNetLog();
+        cronetEngine.stopNetLog();
         assertTrue(file.exists());
         assertTrue(file.length() != 0);
         assertTrue(hasBytesInNetLog(file));
@@ -577,11 +570,11 @@
 
     private void enableCache(int cacheType) throws Exception {
         String cacheTypeString = "";
-        if (cacheType == UrlRequestContextConfig.HTTP_CACHE_DISK) {
+        if (cacheType == CronetEngine.Builder.HTTP_CACHE_DISK) {
             cacheTypeString = CronetTestActivity.CACHE_DISK;
-        } else if (cacheType == UrlRequestContextConfig.HTTP_CACHE_DISK_NO_HTTP) {
+        } else if (cacheType == CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP) {
             cacheTypeString = CronetTestActivity.CACHE_DISK_NO_HTTP;
-        } else if (cacheType == UrlRequestContextConfig.HTTP_CACHE_IN_MEMORY) {
+        } else if (cacheType == CronetEngine.Builder.HTTP_CACHE_IN_MEMORY) {
             cacheTypeString = CronetTestActivity.CACHE_IN_MEMORY;
         }
         String[] commandLineArgs = {CronetTestActivity.CACHE_KEY, cacheTypeString};
@@ -598,12 +591,12 @@
     private void checkRequestCaching(String url, boolean expectCached,
             boolean disableCache) {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                url, listener, listener.getExecutor());
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                url, listener, listener.getExecutor(), mActivity.mCronetEngine);
         if (disableCache) {
-            urlRequest.disableCache();
+            urlRequestBuilder.disableCache();
         }
-        urlRequest.start();
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         assertEquals(expectCached, listener.mResponseInfo.wasCached());
     }
@@ -611,7 +604,7 @@
     @SmallTest
     @Feature({"Cronet"})
     public void testEnableHttpCacheDisabled() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_DISABLED);
+        enableCache(CronetEngine.Builder.HTTP_CACHE_DISABLED);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
         checkRequestCaching(url, false);
         checkRequestCaching(url, false);
@@ -621,7 +614,7 @@
     @SmallTest
     @Feature({"Cronet"})
     public void testEnableHttpCacheInMemory() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_IN_MEMORY);
+        enableCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
         checkRequestCaching(url, false);
         checkRequestCaching(url, true);
@@ -632,7 +625,7 @@
     @SmallTest
     @Feature({"Cronet"})
     public void testEnableHttpCacheDisk() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_DISK);
+        enableCache(CronetEngine.Builder.HTTP_CACHE_DISK);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
         checkRequestCaching(url, false);
         checkRequestCaching(url, true);
@@ -643,7 +636,7 @@
     @SmallTest
     @Feature({"Cronet"})
     public void testEnableHttpCacheDiskNoHttp() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_DISABLED);
+        enableCache(CronetEngine.Builder.HTTP_CACHE_DISABLED);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
         checkRequestCaching(url, false);
         checkRequestCaching(url, false);
@@ -653,7 +646,7 @@
     @SmallTest
     @Feature({"Cronet"})
     public void testDisableCache() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_DISK);
+        enableCache(CronetEngine.Builder.HTTP_CACHE_DISK);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
 
         // When cache is disabled, making a request does not write to the cache.
@@ -670,10 +663,10 @@
 
         // Cache is disabled after server is shut down, request should fail.
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                url, listener, listener.getExecutor());
-        urlRequest.disableCache();
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder = new UrlRequest.Builder(
+                url, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        urlRequestBuilder.disableCache();
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         assertNotNull(listener.mError);
         assertEquals("Exception in CronetUrlRequest: net::ERR_CONNECTION_REFUSED",
@@ -682,8 +675,8 @@
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testEnableHttpCacheDiskNewContext() throws Exception {
-        enableCache(UrlRequestContextConfig.HTTP_CACHE_DISK);
+    public void testEnableHttpCacheDiskNewEngine() throws Exception {
+        enableCache(CronetEngine.Builder.HTTP_CACHE_DISK);
         String url = NativeTestServer.getFileURL("/cacheable.txt");
         checkRequestCaching(url, false);
         checkRequestCaching(url, true);
@@ -691,41 +684,40 @@
         checkRequestCaching(url, true);
 
         // Shutdown original context and create another that uses the same cache.
-        mActivity.mUrlRequestContext.shutdown();
-        mActivity.mUrlRequestContext = UrlRequestContext.createContext(
-                getInstrumentation().getTargetContext(), mActivity.getContextConfig());
+        mActivity.mCronetEngine.shutdown();
+        mActivity.mCronetEngine = mActivity.getCronetEngineBuilder().build();
         checkRequestCaching(url, true);
     }
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testInitContextAndStartRequest() {
+    public void testInitEngineAndStartRequest() {
         CronetTestActivity activity = launchCronetTestAppAndSkipFactoryInit();
 
-        // Immediately make a request after initializing the context.
-        UrlRequestContext requestContext = activity.initRequestContext();
+        // Immediately make a request after initializing the engine.
+        CronetEngine cronetEngine = activity.initCronetEngine();
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest =
-                requestContext.createRequest(TEST_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder urlRequestBuilder =
+                new UrlRequest.Builder(TEST_URL, listener, listener.getExecutor(), cronetEngine);
+        urlRequestBuilder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
     }
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testInitContextStartTwoRequests() throws Exception {
+    public void testInitEngineStartTwoRequests() throws Exception {
         CronetTestActivity activity = launchCronetTestAppAndSkipFactoryInit();
 
         // Make two requests after initializing the context.
-        UrlRequestContext requestContext = activity.initRequestContext();
+        CronetEngine cronetEngine = activity.initCronetEngine();
         int[] statusCodes = {0, 0};
         String[] urls = {TEST_URL, URL_404};
         for (int i = 0; i < 2; i++) {
             TestUrlRequestListener listener = new TestUrlRequestListener();
-            UrlRequest urlRequest =
-                    requestContext.createRequest(urls[i], listener, listener.getExecutor());
-            urlRequest.start();
+            UrlRequest.Builder urlRequestBuilder =
+                    new UrlRequest.Builder(urls[i], listener, listener.getExecutor(), cronetEngine);
+            urlRequestBuilder.build().start();
             listener.blockForDone();
             statusCodes[i] = listener.mResponseInfo.getHttpStatusCode();
         }
@@ -735,7 +727,7 @@
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testInitTwoContextsSimultaneously() throws Exception {
+    public void testInitTwoEnginesSimultaneously() throws Exception {
         final CronetTestActivity activity = launchCronetTestAppAndSkipFactoryInit();
 
         // Threads will block on runBlocker to ensure simultaneous execution.
@@ -754,7 +746,7 @@
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testInitTwoContextsInSequence() throws Exception {
+    public void testInitTwoEnginesInSequence() throws Exception {
         final CronetTestActivity activity = launchCronetTestAppAndSkipFactoryInit();
 
         ConditionVariable runBlocker = new ConditionVariable(true);
@@ -771,19 +763,19 @@
 
     @SmallTest
     @Feature({"Cronet"})
-    public void testInitDifferentContexts() throws Exception {
+    public void testInitDifferentEngines() throws Exception {
         // Test that concurrently instantiating Cronet context's upon various
         // different versions of the same Android Context does not cause crashes
         // like crbug.com/453845
         mActivity = launchCronetTestApp();
-        CronetUrlRequestContext firstContext =
-                new CronetUrlRequestContext(mActivity, mActivity.getContextConfig());
-        CronetUrlRequestContext secondContext = new CronetUrlRequestContext(
-                mActivity.getApplicationContext(), mActivity.getContextConfig());
-        CronetUrlRequestContext thirdContext = new CronetUrlRequestContext(
-                new ContextWrapper(mActivity), mActivity.getContextConfig());
-        firstContext.shutdown();
-        secondContext.shutdown();
-        thirdContext.shutdown();
+        CronetEngine firstEngine =
+                new CronetUrlRequestContext(mActivity.createCronetEngineBuilder(mActivity));
+        CronetEngine secondEngine = new CronetUrlRequestContext(
+                mActivity.createCronetEngineBuilder(mActivity.getApplicationContext()));
+        CronetEngine thirdEngine = new CronetUrlRequestContext(
+                mActivity.createCronetEngineBuilder(new ContextWrapper(mActivity)));
+        firstEngine.shutdown();
+        secondEngine.shutdown();
+        thirdEngine.shutdown();
     }
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index 6eae9ae..8c33d76 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -44,7 +44,7 @@
     @Override
     protected void tearDown() throws Exception {
         NativeTestServer.shutdownNativeTestServer();
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
         super.tearDown();
     }
 
@@ -52,8 +52,9 @@
             throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         // Create request.
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                url, listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                url, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.blockForDone();
         assertTrue(urlRequest.isDone());
@@ -82,6 +83,42 @@
 
     @SmallTest
     @Feature({"Cronet"})
+    public void testBuilderChecks() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        try {
+            new UrlRequest.Builder(null, listener, listener.getExecutor(), mActivity.mCronetEngine);
+            fail("URL not null-checked");
+        } catch (NullPointerException e) {
+            assertEquals("URL is required.", e.getMessage());
+        }
+        try {
+            new UrlRequest.Builder(NativeTestServer.getRedirectURL(), null, listener.getExecutor(),
+                    mActivity.mCronetEngine);
+            fail("Listener not null-checked");
+        } catch (NullPointerException e) {
+            assertEquals("Listener is required.", e.getMessage());
+        }
+        try {
+            new UrlRequest.Builder(
+                    NativeTestServer.getRedirectURL(), listener, null, mActivity.mCronetEngine);
+            fail("Executor not null-checked");
+        } catch (NullPointerException e) {
+            assertEquals("Executor is required.", e.getMessage());
+        }
+        try {
+            new UrlRequest.Builder(
+                    NativeTestServer.getRedirectURL(), listener, listener.getExecutor(), null);
+            fail("CronetEngine not null-checked");
+        } catch (NullPointerException e) {
+            assertEquals("CronetEngine is required.", e.getMessage());
+        }
+        // Verify successful creation doesn't throw.
+        new UrlRequest.Builder(NativeTestServer.getRedirectURL(), listener, listener.getExecutor(),
+                mActivity.mCronetEngine);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
     public void testSimpleGet() throws Exception {
         TestUrlRequestListener listener = startAndWaitForComplete(
                 NativeTestServer.getEchoMethodURL());
@@ -107,8 +144,9 @@
         // Start the request and wait to see the redirect.
         TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setAutoAdvance(false);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getRedirectURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.waitForNextStep();
 
@@ -217,25 +255,18 @@
     public void testSetHttpMethod() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         String methodName = "HEAD";
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(), listener,
-                listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
         // Try to set 'null' method.
         try {
-            urlRequest.setHttpMethod(null);
+            builder.setHttpMethod(null);
             fail("Exception not thrown");
         } catch (NullPointerException e) {
             assertEquals("Method is required.", e.getMessage());
         }
 
-        urlRequest.setHttpMethod(methodName);
-        urlRequest.start();
-        try {
-            urlRequest.setHttpMethod("toolate");
-            fail("Exception not thrown");
-        } catch (IllegalStateException e) {
-            assertEquals("Request is already started.", e.getMessage());
-        }
+        builder.setHttpMethod(methodName);
+        builder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         assertEquals(0, listener.mHttpResponseDataLength);
@@ -245,11 +276,11 @@
     @Feature({"Cronet"})
     public void testBadMethod() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
         try {
-            urlRequest.setHttpMethod("bad:method!");
-            urlRequest.start();
+            builder.setHttpMethod("bad:method!");
+            builder.build().start();
             fail("IllegalArgumentException not thrown.");
         } catch (IllegalArgumentException e) {
             assertEquals("Invalid http method bad:method!",
@@ -261,11 +292,11 @@
     @Feature({"Cronet"})
     public void testBadHeaderName() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
         try {
-            urlRequest.addHeader("header:name", "headervalue");
-            urlRequest.start();
+            builder.addHeader("header:name", "headervalue");
+            builder.build().start();
             fail("IllegalArgumentException not thrown.");
         } catch (IllegalArgumentException e) {
             assertEquals("Invalid header header:name=headervalue",
@@ -277,11 +308,11 @@
     @Feature({"Cronet"})
     public void testBadHeaderValue() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
         try {
-            urlRequest.addHeader("headername", "bad header\r\nvalue");
-            urlRequest.start();
+            builder.addHeader("headername", "bad header\r\nvalue");
+            builder.build().start();
             fail("IllegalArgumentException not thrown.");
         } catch (IllegalArgumentException e) {
             assertEquals("Invalid header headername=bad header\r\nvalue",
@@ -295,18 +326,12 @@
         TestUrlRequestListener listener = new TestUrlRequestListener();
         String headerName = "header-name";
         String headerValue = "header-value";
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoHeaderURL(headerName), listener,
-                listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getEchoHeaderURL(headerName), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
-        urlRequest.addHeader(headerName, headerValue);
-        urlRequest.start();
-        try {
-            urlRequest.addHeader("header2", "value");
-            fail("Exception not thrown");
-        } catch (IllegalStateException e) {
-            assertEquals("Request is already started.", e.getMessage());
-        }
+        builder.addHeader(headerName, headerValue);
+        builder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         assertEquals(headerValue, listener.mResponseAsString);
@@ -319,12 +344,11 @@
         String headerName = "header-name";
         String headerValue1 = "header-value1";
         String headerValue2 = "header-value2";
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoAllHeadersURL(), listener,
-                listener.getExecutor());
-        urlRequest.addHeader(headerName, headerValue1);
-        urlRequest.addHeader(headerName, headerValue2);
-        urlRequest.start();
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoAllHeadersURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        builder.addHeader(headerName, headerValue1);
+        builder.addHeader(headerName, headerValue2);
+        builder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         String headers = listener.mResponseAsString;
@@ -344,11 +368,11 @@
         TestUrlRequestListener listener = new TestUrlRequestListener();
         String userAgentName = "User-Agent";
         String userAgentValue = "User-Agent-Value";
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoHeaderURL(userAgentName), listener,
-                listener.getExecutor());
-        urlRequest.addHeader(userAgentName, userAgentValue);
-        urlRequest.start();
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getEchoHeaderURL(userAgentName), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
+        builder.addHeader(userAgentName, userAgentValue);
+        builder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         assertEquals(userAgentValue, listener.mResponseAsString);
@@ -359,10 +383,10 @@
     public void testDefaultUserAgent() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         String headerName = "User-Agent";
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoHeaderURL(headerName), listener,
-                listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getEchoHeaderURL(headerName), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
+        builder.build().start();
         listener.blockForDone();
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         assertTrue("Default User-Agent should contain Cronet/n.n.n.n but is "
@@ -540,9 +564,9 @@
         listener.setAutoAdvance(false);
         // Since the default method is "GET", the expected response body is also
         // "GET".
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(),
-                listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.waitForNextStep();
 
@@ -627,8 +651,9 @@
         listener.setAutoAdvance(false);
         // Since the default method is "GET", the expected response body is also
         // "GET".
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.waitForNextStep();
 
@@ -708,9 +733,9 @@
     public void testBadBuffers() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setAutoAdvance(false);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(), listener,
-                listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.waitForNextStep();
 
@@ -749,8 +774,10 @@
     public void testUnexpectedReads() throws Exception {
         final TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setAutoAdvance(false);
-        final UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectURL(), listener, listener.getExecutor());
+        final UrlRequest urlRequest =
+                new UrlRequest.Builder(NativeTestServer.getRedirectURL(), listener,
+                                      listener.getExecutor(), mActivity.mCronetEngine)
+                        .build();
 
         // Try to read before starting request.
         try {
@@ -832,8 +859,10 @@
     public void testUnexpectedFollowRedirects() throws Exception {
         final TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setAutoAdvance(false);
-        final UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectURL(), listener, listener.getExecutor());
+        final UrlRequest urlRequest =
+                new UrlRequest.Builder(NativeTestServer.getRedirectURL(), listener,
+                                      listener.getExecutor(), mActivity.mCronetEngine)
+                        .build();
 
         // Try to follow a redirect before starting the request.
         try {
@@ -912,11 +941,11 @@
     @Feature({"Cronet"})
     public void testUploadSetDataProvider() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         try {
-            urlRequest.setUploadDataProvider(null, listener.getExecutor());
+            builder.setUploadDataProvider(null, listener.getExecutor());
             fail("Exception not thrown");
         } catch (NullPointerException e) {
             assertEquals("Invalid UploadDataProvider.", e.getMessage());
@@ -924,9 +953,9 @@
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
         try {
-            urlRequest.start();
+            builder.build().start();
             fail("Exception not thrown");
         } catch (IllegalArgumentException e) {
             assertEquals("Requests with upload data must have a Content-Type.", e.getMessage());
@@ -937,14 +966,14 @@
     @Feature({"Cronet"})
     public void testUploadEmptyBodySync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(0, dataProvider.getLength());
@@ -959,15 +988,15 @@
     @Feature({"Cronet"})
     public void testUploadSync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(4, dataProvider.getLength());
@@ -982,8 +1011,8 @@
     @Feature({"Cronet"})
     public void testUploadMultiplePiecesSync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
@@ -992,9 +1021,9 @@
         dataProvider.addRead("another ".getBytes());
         dataProvider.addRead("test".getBytes());
 
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(16, dataProvider.getLength());
@@ -1009,8 +1038,8 @@
     @Feature({"Cronet"})
     public void testUploadMultiplePiecesAsync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
@@ -1019,9 +1048,9 @@
         dataProvider.addRead("another ".getBytes());
         dataProvider.addRead("test".getBytes());
 
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(16, dataProvider.getLength());
@@ -1036,15 +1065,15 @@
     @Feature({"Cronet"})
     public void testUploadChangesDefaultMethod() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
@@ -1055,18 +1084,18 @@
     @Feature({"Cronet"})
     public void testUploadWithSetMethod() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoMethodURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoMethodURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         final String method = "PUT";
-        urlRequest.setHttpMethod(method);
+        builder.setHttpMethod(method);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
@@ -1077,15 +1106,16 @@
     @Feature({"Cronet"})
     public void testUploadRedirectSync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         // 1 read call before the rewind, 1 after.
@@ -1100,15 +1130,16 @@
     @Feature({"Cronet"})
     public void testUploadRedirectAsync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         // 1 read call before the rewind, 1 after.
@@ -1123,8 +1154,8 @@
     @Feature({"Cronet"})
     public void testUploadReadFailSync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
@@ -1132,9 +1163,9 @@
         // This will never be read, but if the length is 0, read may never be
         // called.
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1149,8 +1180,8 @@
     @Feature({"Cronet"})
     public void testUploadReadFailAsync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
@@ -1158,9 +1189,9 @@
         // This will never be read, but if the length is 0, read may never be
         // called.
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1175,8 +1206,8 @@
     @Feature({"Cronet"})
     public void testUploadReadFailThrown() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
@@ -1184,9 +1215,9 @@
         // This will never be read, but if the length is 0, read may never be
         // called.
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1201,16 +1232,17 @@
     @Feature({"Cronet"})
     public void testUploadRewindFailSync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_SYNC);
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1225,16 +1257,17 @@
     @Feature({"Cronet"})
     public void testUploadRewindFailAsync() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_ASYNC);
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1249,16 +1282,17 @@
     @Feature({"Cronet"})
     public void testUploadRewindFailThrown() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), listener,
+                        listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.THROWN);
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertEquals(1, dataProvider.getNumReadCalls());
@@ -1273,19 +1307,19 @@
     @Feature({"Cronet"})
     public void testUploadChunked() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test hello".getBytes());
         dataProvider.setChunked(true);
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
 
         assertEquals(-1, dataProvider.getLength());
 
-        urlRequest.start();
+        builder.build().start();
         listener.blockForDone();
 
         // 1 read call for one data chunk.
@@ -1297,8 +1331,8 @@
     @Feature({"Cronet"})
     public void testUploadChunkedLastReadZeroLengthBody() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
@@ -1307,12 +1341,12 @@
         dataProvider.addRead("!".getBytes());
         dataProvider.addRead("".getBytes());
         dataProvider.setChunked(true);
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
 
         assertEquals(-1, dataProvider.getLength());
 
-        urlRequest.start();
+        builder.build().start();
         listener.blockForDone();
 
         // 2 read call for the first two data chunks, and 1 for final chunk.
@@ -1326,8 +1360,8 @@
     @Feature({"Cronet"})
     public void testUploadFailsWithoutInitializingStream() throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
         // Shut down the test server, so connecting to it fails. Note that
         // calling shutdown again during teardown is safe.
         NativeTestServer.shutdownNativeTestServer();
@@ -1335,9 +1369,9 @@
         TestUploadDataProvider dataProvider = new TestUploadDataProvider(
                 TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
         dataProvider.addRead("test".getBytes());
-        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
-        urlRequest.addHeader("Content-Type", "useless/string");
-        urlRequest.start();
+        builder.setUploadDataProvider(dataProvider, listener.getExecutor());
+        builder.addHeader("Content-Type", "useless/string");
+        builder.build().start();
         listener.blockForDone();
 
         assertNull(listener.mResponseInfo);
@@ -1349,8 +1383,9 @@
             boolean expectResponseInfo, boolean expectError) {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setFailure(failureType, failureStep);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getRedirectURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.blockForDone();
         assertEquals(1, listener.mRedirectCount);
@@ -1397,8 +1432,9 @@
     public void testThrowON_SUCCEEDED() {
         TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_SUCCEEDED);
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getRedirectURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getRedirectURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         listener.blockForDone();
         assertEquals(1, listener.mRedirectCount);
@@ -1415,9 +1451,9 @@
         TestUrlRequestListener listener = new TestUrlRequestListener();
 
         listener.setAutoAdvance(false);
-        CronetUrlRequest urlRequest =
-                (CronetUrlRequest) mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
+        CronetUrlRequest urlRequest = (CronetUrlRequest) builder.build();
         urlRequest.start();
         listener.waitForNextStep();
         assertFalse(listener.isDone());
@@ -1470,13 +1506,14 @@
         }
 
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.getEchoBodyURL(),
+                listener, listener.getExecutor(), mActivity.mCronetEngine);
 
         ExecutorService uploadExecutor = Executors.newSingleThreadExecutor();
         HangingUploadDataProvider dataProvider = new HangingUploadDataProvider();
-        urlRequest.setUploadDataProvider(dataProvider, uploadExecutor);
-        urlRequest.addHeader("Content-Type", "useless/string");
+        builder.setUploadDataProvider(dataProvider, uploadExecutor);
+        builder.addHeader("Content-Type", "useless/string");
+        UrlRequest urlRequest = builder.build();
         urlRequest.start();
         // Wait for read to be called on executor.
         dataProvider.mReadCalled.block();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/GetStatusTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/GetStatusTest.java
index 8291ce0c..f146695 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/GetStatusTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/GetStatusTest.java
@@ -45,7 +45,7 @@
     @Override
     protected void tearDown() throws Exception {
         NativeTestServer.shutdownNativeTestServer();
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
         super.tearDown();
     }
 
@@ -55,8 +55,9 @@
         String url = NativeTestServer.getEchoMethodURL();
         TestUrlRequestListener listener = new TestUrlRequestListener();
         listener.setAutoAdvance(false);
-        UrlRequest urlRequest =
-                mActivity.mUrlRequestContext.createRequest(url, listener, listener.getExecutor());
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                url, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        UrlRequest urlRequest = builder.build();
         // Calling before request is started should give Status.INVALID,
         // since the native adapter is not created.
         TestStatusListener statusListener0 = new TestStatusListener();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/HistogramManagerTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/HistogramManagerTest.java
index 9922a12..30f8147 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/HistogramManagerTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/HistogramManagerTest.java
@@ -26,9 +26,9 @@
         byte delta1[] = mActivity.mHistogramManager.getHistogramDeltas();
 
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
-                TEST_URL, listener, listener.getExecutor());
-        urlRequest.start();
+        UrlRequest.Builder builder = new UrlRequest.Builder(
+                TEST_URL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        builder.build().start();
         listener.blockForDone();
         byte delta2[] = mActivity.mHistogramManager.getHistogramDeltas();
         assertTrue(delta2.length != 0);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
index 7e66627..000d7cd 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
@@ -30,13 +30,13 @@
         // Load library first, since we need the Quic test server's URL.
         System.loadLibrary("cronet_tests");
         QuicTestServer.startQuicTestServer(getInstrumentation().getTargetContext());
-        UrlRequestContextConfig config = new UrlRequestContextConfig();
-        config.enableQUIC(true);
-        config.addQuicHint(QuicTestServer.getServerHost(), QuicTestServer.getServerPort(),
+        CronetEngine.Builder builder = new CronetEngine.Builder(mActivity);
+        builder.enableQUIC(true);
+        builder.addQuicHint(QuicTestServer.getServerHost(), QuicTestServer.getServerPort(),
                 QuicTestServer.getServerPort());
-        config.setExperimentalQuicConnectionOptions("PACE,IW10,FOO,DEADBEEF");
+        builder.setExperimentalQuicConnectionOptions("PACE,IW10,FOO,DEADBEEF");
 
-        String[] commandLineArgs = {CronetTestActivity.CONFIG_KEY, config.toString(),
+        String[] commandLineArgs = {CronetTestActivity.CONFIG_KEY, builder.toString(),
                 CronetTestActivity.CACHE_KEY, CronetTestActivity.CACHE_DISK_NO_HTTP};
         mActivity = launchCronetTestAppWithUrlAndCommandLineArgs(null, commandLineArgs);
     }
@@ -80,9 +80,9 @@
         // since there is no http server running on the corresponding TCP port,
         // QUIC will always succeed with a 200 (see
         // net::HttpStreamFactoryImpl::Request::OnStreamFailed).
-        UrlRequest request = mActivity.mUrlRequestContext.createRequest(
-                quicURL, listener, listener.getExecutor());
-        request.start();
+        UrlRequest.Builder requestBuilder = new UrlRequest.Builder(
+                quicURL, listener, listener.getExecutor(), mActivity.mCronetEngine);
+        requestBuilder.build().start();
         listener.blockForDone();
 
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
@@ -109,18 +109,19 @@
         }
         assertTrue(fileContainsString("local_prefs.json",
                 QuicTestServer.getServerHost() + ":" + QuicTestServer.getServerPort()));
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
 
         // Make another request using a new context but with no QUIC hints.
-        UrlRequestContextConfig config = new UrlRequestContextConfig();
-        config.setStoragePath(mActivity.getTestStorage());
-        config.enableHttpCache(UrlRequestContextConfig.HTTP_CACHE_DISK, 1000 * 1024);
-        config.enableQUIC(true);
-        CronetUrlRequestContext newContext =
-                new CronetUrlRequestContext(getInstrumentation().getTargetContext(), config);
+        CronetEngine.Builder builder =
+                new CronetEngine.Builder(getInstrumentation().getTargetContext());
+        builder.setStoragePath(mActivity.getTestStorage());
+        builder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK, 1000 * 1024);
+        builder.enableQUIC(true);
+        CronetEngine newEngine = new CronetUrlRequestContext(builder);
         TestUrlRequestListener listener2 = new TestUrlRequestListener();
-        UrlRequest request2 = newContext.createRequest(quicURL, listener2, listener2.getExecutor());
-        request2.start();
+        requestBuilder =
+                new UrlRequest.Builder(quicURL, listener2, listener2.getExecutor(), newEngine);
+        requestBuilder.build().start();
         listener2.blockForDone();
         assertEquals(200, listener2.mResponseInfo.getHttpStatusCode());
         assertEquals(expectedContent, listener2.mResponseAsString);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
index ca4a426c..4b5a689a 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
@@ -48,7 +48,7 @@
                 launchCronetTestAppWithUrlAndCommandLineArgs(null, commandLineArgs.toArray(args));
         long urlRequestContextAdapter = (api == Api.LEGACY)
                 ? getContextAdapter((ChromiumUrlRequestFactory) mActivity.mRequestFactory)
-                : getContextAdapter((CronetUrlRequestContext) mActivity.mUrlRequestContext);
+                : getContextAdapter((CronetUrlRequestContext) mActivity.mCronetEngine);
         NativeTestServer.registerHostResolverProc(urlRequestContextAdapter, api == Api.LEGACY);
         // Start NativeTestServer.
         assertTrue(NativeTestServer.startNativeTestServer(getInstrumentation().getTargetContext()));
@@ -128,14 +128,13 @@
     public void testSdchEnabled() throws Exception {
         setUp(Sdch.ENABLED, Api.ASYNC);
         String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test";
-        long contextAdapter =
-                getContextAdapter((CronetUrlRequestContext) mActivity.mUrlRequestContext);
+        long contextAdapter = getContextAdapter((CronetUrlRequestContext) mActivity.mCronetEngine);
         DictionaryAddedObserver observer =
                 new DictionaryAddedObserver(targetUrl, contextAdapter, false /** Legacy Api */);
 
         // Make a request to /sdch which advertises the dictionary.
-        TestUrlRequestListener listener1 = startAndWaitForComplete(mActivity.mUrlRequestContext,
-                NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
+        TestUrlRequestListener listener1 = startAndWaitForComplete(
+                mActivity.mCronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
         assertEquals(200, listener1.mResponseInfo.getHttpStatusCode());
         assertEquals("This is an index page.\n", listener1.mResponseAsString);
         assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"),
@@ -145,15 +144,15 @@
 
         // Make a request to fetch encoded response at /sdch/test.
         TestUrlRequestListener listener2 =
-                startAndWaitForComplete(mActivity.mUrlRequestContext, targetUrl);
+                startAndWaitForComplete(mActivity.mCronetEngine, targetUrl);
         assertEquals(200, listener2.mResponseInfo.getHttpStatusCode());
         assertEquals("The quick brown fox jumps over the lazy dog.\n", listener2.mResponseAsString);
 
         // Wait for a bit until SimpleCache finished closing entries before
-        // calling shutdown on the UrlRequestContext.
+        // calling shutdown on the CronetEngine.
         // TODO(xunjieli): Remove once crbug.com/486120 is fixed.
         Thread.sleep(5000);
-        mActivity.mUrlRequestContext.shutdown();
+        mActivity.mCronetEngine.shutdown();
 
         // Shutting down the context will make JsonPrefStore to flush pending
         // writes to disk.
@@ -161,8 +160,8 @@
         assertTrue(fileContainsString("local_prefs.json", dictUrl));
 
         // Test persistence.
-        CronetUrlRequestContext newContext = new CronetUrlRequestContext(
-                getInstrumentation().getTargetContext(), mActivity.getContextConfig());
+        CronetUrlRequestContext newContext =
+                new CronetUrlRequestContext(mActivity.getCronetEngineBuilder());
 
         long newContextAdapter = getContextAdapter(newContext);
         NativeTestServer.registerHostResolverProc(newContextAdapter, false);
@@ -182,8 +181,8 @@
         setUp(Sdch.DISABLED, Api.ASYNC);
         // Make a request to /sdch.
         // Since Sdch is not enabled, no dictionary should be advertised.
-        TestUrlRequestListener listener = startAndWaitForComplete(mActivity.mUrlRequestContext,
-                NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
+        TestUrlRequestListener listener = startAndWaitForComplete(
+                mActivity.mCronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
         assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
         assertEquals("This is an index page.\n", listener.mResponseAsString);
         assertEquals(null, listener.mResponseInfo.getAllHeaders().get("Get-Dictionary"));
@@ -195,8 +194,8 @@
         setUp(Sdch.ENABLED, Api.ASYNC);
         // Make a request to /sdch/index which advertises a bad dictionary that
         // does not exist.
-        TestUrlRequestListener listener1 = startAndWaitForComplete(mActivity.mUrlRequestContext,
-                NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound");
+        TestUrlRequestListener listener1 = startAndWaitForComplete(
+                mActivity.mCronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound");
         assertEquals(200, listener1.mResponseInfo.getHttpStatusCode());
         assertEquals("This is an index page.\n", listener1.mResponseAsString);
         assertEquals(Arrays.asList("/sdch/dict/NotFound"),
@@ -204,7 +203,7 @@
 
         // Make a request to fetch /sdch/test, and make sure Sdch encoding is not used.
         TestUrlRequestListener listener2 = startAndWaitForComplete(
-                mActivity.mUrlRequestContext, NativeTestServer.getSdchURL() + "/sdch/test");
+                mActivity.mCronetEngine, NativeTestServer.getSdchURL() + "/sdch/test");
         assertEquals(200, listener2.mResponseInfo.getHttpStatusCode());
         assertEquals("Sdch is not used.\n", listener2.mResponseAsString);
     }
@@ -248,11 +247,12 @@
         return listener;
     }
 
-    private TestUrlRequestListener startAndWaitForComplete(
-            UrlRequestContext requestContext, String url) throws Exception {
+    private TestUrlRequestListener startAndWaitForComplete(CronetEngine cronetEngine, String url)
+            throws Exception {
         TestUrlRequestListener listener = new TestUrlRequestListener();
-        UrlRequest request = requestContext.createRequest(url, listener, listener.getExecutor());
-        request.start();
+        UrlRequest.Builder builder =
+                new UrlRequest.Builder(url, listener, listener.getExecutor(), cronetEngine);
+        builder.build().start();
         listener.blockForDone();
         return listener;
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
index 7d9bbf9..19b81d5 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
@@ -18,6 +18,9 @@
 import java.net.Proxy;
 import java.net.URL;
 
+/**
+ * Tests for CronetHttpURLStreamHandler class.
+ */
 public class CronetHttpURLStreamHandlerTest extends CronetTestBase {
     private CronetTestActivity mActivity;
 
@@ -40,7 +43,7 @@
     public void testOpenConnectionHttp() throws Exception {
         URL url = new URL(NativeTestServer.getEchoMethodURL());
         CronetHttpURLStreamHandler streamHandler =
-                new CronetHttpURLStreamHandler(mActivity.mUrlRequestContext);
+                new CronetHttpURLStreamHandler(mActivity.mCronetEngine);
         HttpURLConnection connection =
                 (HttpURLConnection) streamHandler.openConnection(url);
         assertEquals(200, connection.getResponseCode());
@@ -54,7 +57,7 @@
     public void testOpenConnectionHttps() throws Exception {
         URL url = new URL("https://example.com");
         CronetHttpURLStreamHandler streamHandler =
-                new CronetHttpURLStreamHandler(mActivity.mUrlRequestContext);
+                new CronetHttpURLStreamHandler(mActivity.mCronetEngine);
         HttpURLConnection connection =
                 (HttpURLConnection) streamHandler.openConnection(url);
         assertNotNull(connection);
@@ -65,7 +68,7 @@
     public void testOpenConnectionProtocolNotSupported() throws Exception {
         URL url = new URL("ftp://example.com");
         CronetHttpURLStreamHandler streamHandler =
-                new CronetHttpURLStreamHandler(mActivity.mUrlRequestContext);
+                new CronetHttpURLStreamHandler(mActivity.mCronetEngine);
         try {
             streamHandler.openConnection(url);
             fail();
@@ -79,7 +82,7 @@
     public void testOpenConnectionWithProxy() throws Exception {
         URL url = new URL(NativeTestServer.getEchoMethodURL());
         CronetHttpURLStreamHandler streamHandler =
-                new CronetHttpURLStreamHandler(mActivity.mUrlRequestContext);
+                new CronetHttpURLStreamHandler(mActivity.mCronetEngine);
         Proxy proxy = new Proxy(Proxy.Type.HTTP,
                 new InetSocketAddress("127.0.0.1", 8080));
         try {
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java
index 110bcf7f..23f4675 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java
@@ -7,18 +7,20 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.base.test.util.Feature;
-import org.chromium.net.CronetTestActivity;
 import org.chromium.net.CronetTestBase;
 
+/**
+ * Test for CronetURLStreamHandlerFactory.
+ */
 public class CronetURLStreamHandlerFactoryTest extends CronetTestBase {
     @SmallTest
     @Feature({"Cronet"})
     public void testRequireConfig() throws Exception {
-        CronetTestActivity activity = launchCronetTestApp();
+        launchCronetTestApp();
         try {
-            new CronetURLStreamHandlerFactory(activity, null);
+            new CronetURLStreamHandlerFactory(null);
         } catch (NullPointerException e) {
-            assertEquals("UrlRequestContextConfig is null.", e.getMessage());
+            assertEquals("CronetEngine is null.", e.getMessage());
         }
     }
 }
diff --git a/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java b/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java
index 956cbac..0a3cdadc 100644
--- a/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java
+++ b/components/cronet/android/test/src/org/chromium/net/CronetTestActivity.java
@@ -5,6 +5,7 @@
 package org.chromium.net;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Environment;
@@ -67,7 +68,7 @@
     public static final String LIBRARY_INIT_WRAPPER = "wrapperOnly";
 
     public CronetURLStreamHandlerFactory mStreamHandlerFactory;
-    public UrlRequestContext mUrlRequestContext;
+    public CronetEngine mCronetEngine;
     HttpUrlRequestFactory mRequestFactory;
     @SuppressFBWarnings("URF_UNREAD_FIELD")
     HistogramManager mHistogramManager;
@@ -78,8 +79,8 @@
 
     int mHttpStatusCode = 0;
 
-    // UrlRequestContextConfig used for this activity.
-    private UrlRequestContextConfig mConfig;
+    // CronetEngine.Builder used for this activity.
+    private CronetEngine.Builder mCronetEngineBuilder;
 
     class TestHttpUrlRequestListener implements HttpUrlRequestListener {
         public TestHttpUrlRequestListener() {
@@ -116,21 +117,21 @@
             }
         }
 
-        // Initializes UrlRequestContextConfig from commandLine args.
-        mConfig = initializeContextConfig();
-        Log.i(TAG, "Using Config: " + mConfig.toString());
+        // Initializes CronetEngine.Builder from commandLine args.
+        mCronetEngineBuilder = initializeCronetEngineBuilder();
+        Log.i(TAG, "Using Config: " + mCronetEngineBuilder.toString());
 
         String initString = getCommandLineArg(LIBRARY_INIT_KEY);
         if (LIBRARY_INIT_SKIP.equals(initString)) {
             return;
         }
 
+        mCronetEngine = initCronetEngine();
+
         if (LIBRARY_INIT_WRAPPER.equals(initString)) {
-            mStreamHandlerFactory =
-                new CronetURLStreamHandlerFactory(this, mConfig);
+            mStreamHandlerFactory = new CronetURLStreamHandlerFactory(mCronetEngine);
         }
 
-        mUrlRequestContext = initRequestContext();
         mHistogramManager = HistogramManager.createHistogramManager();
 
         if (LIBRARY_INIT_CRONET_ONLY.equals(initString)) {
@@ -171,19 +172,23 @@
         return path.delete();
     }
 
-    UrlRequestContextConfig getContextConfig() {
-        return mConfig;
+    CronetEngine.Builder getCronetEngineBuilder() {
+        return mCronetEngineBuilder;
     }
 
-    private UrlRequestContextConfig initializeContextConfig() {
-        UrlRequestContextConfig config = new UrlRequestContextConfig();
-        config.enableHTTP2(true).enableQUIC(true);
+    private CronetEngine.Builder initializeCronetEngineBuilder() {
+        return createCronetEngineBuilder(this);
+    }
+
+    CronetEngine.Builder createCronetEngineBuilder(Context context) {
+        CronetEngine.Builder cronetEngineBuilder = new CronetEngine.Builder(context);
+        cronetEngineBuilder.enableHTTP2(true).enableQUIC(true);
 
         // Override config if it is passed from the launcher.
         String configString = getCommandLineArg(CONFIG_KEY);
         if (configString != null) {
             try {
-                config = new UrlRequestContextConfig(configString);
+                cronetEngineBuilder = new CronetEngine.Builder(this, configString);
             } catch (org.json.JSONException e) {
                 Log.e(TAG, "Invalid Config.", e);
                 finish();
@@ -193,33 +198,35 @@
 
         String cacheString = getCommandLineArg(CACHE_KEY);
         if (CACHE_DISK.equals(cacheString)) {
-            config.setStoragePath(getTestStorage());
-            config.enableHttpCache(UrlRequestContextConfig.HTTP_CACHE_DISK, 1000 * 1024);
+            cronetEngineBuilder.setStoragePath(getTestStorage());
+            cronetEngineBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK, 1000 * 1024);
         } else if (CACHE_DISK_NO_HTTP.equals(cacheString)) {
-            config.setStoragePath(getTestStorage());
-            config.enableHttpCache(UrlRequestContextConfig.HTTP_CACHE_DISK_NO_HTTP, 1000 * 1024);
+            cronetEngineBuilder.setStoragePath(getTestStorage());
+            cronetEngineBuilder.enableHttpCache(
+                    CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 1000 * 1024);
         } else if (CACHE_IN_MEMORY.equals(cacheString)) {
-            config.enableHttpCache(UrlRequestContextConfig.HTTP_CACHE_IN_MEMORY, 100 * 1024);
+            cronetEngineBuilder.enableHttpCache(
+                    CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024);
         }
 
         String sdchString = getCommandLineArg(SDCH_KEY);
         if (SDCH_ENABLE.equals(sdchString)) {
-            config.enableSDCH(true);
+            cronetEngineBuilder.enableSDCH(true);
         }
 
         // Setting this here so it isn't overridden on the command line
-        config.setLibraryName("cronet_tests");
-        return config;
+        cronetEngineBuilder.setLibraryName("cronet_tests");
+        return cronetEngineBuilder;
     }
 
-    // Helper function to initialize request context. Also used in testing.
-    public UrlRequestContext initRequestContext() {
-        return UrlRequestContext.createContext(this, mConfig);
+    // Helper function to initialize Cronet engine. Also used in testing.
+    public CronetEngine initCronetEngine() {
+        return mCronetEngineBuilder.build();
     }
 
     // Helper function to initialize request factory. Also used in testing.
     public HttpUrlRequestFactory initRequestFactory() {
-        return HttpUrlRequestFactory.createFactory(this, mConfig);
+        return HttpUrlRequestFactory.createFactory(this, mCronetEngineBuilder);
     }
 
     private static String getUrlFromIntent(Intent intent) {
@@ -285,9 +292,9 @@
                     + "/cronet_sample_netlog_old_api.json",
                     false);
         }
-        if (mUrlRequestContext != null) {
-            mUrlRequestContext.startNetLogToFile(Environment.getExternalStorageDirectory().getPath()
-                    + "/cronet_sample_netlog_new_api.json",
+        if (mCronetEngine != null) {
+            mCronetEngine.startNetLogToFile(Environment.getExternalStorageDirectory().getPath()
+                            + "/cronet_sample_netlog_new_api.json",
                     false);
         }
     }
@@ -296,8 +303,8 @@
         if (mRequestFactory != null) {
             mRequestFactory.stopNetLog();
         }
-        if (mUrlRequestContext != null) {
-            mUrlRequestContext.stopNetLog();
+        if (mCronetEngine != null) {
+            mCronetEngine.stopNetLog();
         }
     }
 }
diff --git a/components/cronet/cronet_static.gypi b/components/cronet/cronet_static.gypi
index 08601ce..f2dce0d1 100644
--- a/components/cronet/cronet_static.gypi
+++ b/components/cronet/cronet_static.gypi
@@ -8,7 +8,7 @@
     '../base/base.gyp:base',
     '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
     'cronet_jni_headers',
-    'cronet_url_request_context_config_list',
+    'cronet_engine_builder_list',
     'cronet_url_request_java',
     'cronet_version',
     'cronet_version_header',
diff --git a/components/html_viewer/web_test_delegate_impl.cc b/components/html_viewer/web_test_delegate_impl.cc
index 4e958fd..b2dceec 100644
--- a/components/html_viewer/web_test_delegate_impl.cc
+++ b/components/html_viewer/web_test_delegate_impl.cc
@@ -310,6 +310,12 @@
   NOTIMPLEMENTED();
 }
 
+bool WebTestDelegateImpl::AddMediaStreamSourceAndTrack(
+    blink::WebMediaStream* stream) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 cc::SharedBitmapManager* WebTestDelegateImpl::GetSharedBitmapManager() {
   NOTIMPLEMENTED();
   return nullptr;
diff --git a/components/html_viewer/web_test_delegate_impl.h b/components/html_viewer/web_test_delegate_impl.h
index b3ae0e8..e13be3b 100644
--- a/components/html_viewer/web_test_delegate_impl.h
+++ b/components/html_viewer/web_test_delegate_impl.h
@@ -108,6 +108,7 @@
                      const GURL& origin,
                      const GURL& embedding_origin) override;
   void ResetPermissions() override;
+  bool AddMediaStreamSourceAndTrack(blink::WebMediaStream* stream) override;
   cc::SharedBitmapManager* GetSharedBitmapManager() override;
   void DispatchBeforeInstallPromptEvent(
       int request_id,
diff --git a/components/mus/gles2/command_buffer_driver.cc b/components/mus/gles2/command_buffer_driver.cc
index a03a357..acffd14 100644
--- a/components/mus/gles2/command_buffer_driver.cc
+++ b/components/mus/gles2/command_buffer_driver.cc
@@ -12,7 +12,6 @@
 #include "components/mus/gles2/gpu_memory_tracker.h"
 #include "components/mus/gles2/gpu_state.h"
 #include "components/mus/gles2/mojo_buffer_backing.h"
-#include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/common/value_state.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/context_group.h"
@@ -117,6 +116,10 @@
       base::Bind(&CommandBufferDriver::OnResize, base::Unretained(this)));
   decoder_->SetWaitSyncPointCallback(base::Bind(
       &CommandBufferDriver::OnWaitSyncPoint, base::Unretained(this)));
+  decoder_->SetFenceSyncReleaseCallback(base::Bind(
+      &CommandBufferDriver::OnFenceSyncRelease, base::Unretained(this)));
+  decoder_->SetWaitFenceSyncCallback(base::Bind(
+      &CommandBufferDriver::OnWaitFenceSync, base::Unretained(this)));
 
   gpu::gles2::DisallowedFeatures disallowed_features;
 
@@ -293,6 +296,45 @@
   return scheduler_->scheduled();
 }
 
+void CommandBufferDriver::OnFenceSyncRelease(uint64_t release) {
+  // TODO(dyen): Implement once CommandBufferID has been figured out and
+  // we have a SyncPointClient. It would probably look like what is commented
+  // out below:
+  // if (!sync_point_client_->client_state()->IsFenceSyncReleased(release))
+  //   sync_point_client_->ReleaseFenceSync(release);
+  NOTIMPLEMENTED();
+}
+
+bool CommandBufferDriver::OnWaitFenceSync(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint64_t release) {
+  gpu::SyncPointManager* sync_point_manager = gpu_state_->sync_point_manager();
+  DCHECK(sync_point_manager);
+
+  scoped_refptr<gpu::SyncPointClientState> release_state =
+      sync_point_manager->GetSyncPointClientState(namespace_id,
+                                                  command_buffer_id);
+
+  if (!release_state)
+    return true;
+
+  if (release_state->IsFenceSyncReleased(release))
+    return true;
+
+  // TODO(dyen): Implement once CommandBufferID has been figured out and
+  // we have a SyncPointClient. It would probably look like what is commented
+  // out below:
+  // scheduler_->SetScheduled(false);
+  // sync_point_client_->Wait(
+  //     release_state.get(),
+  //     release,
+  //     base::Bind(&CommandBufferDriver::OnSyncPointRetired,
+  //                weak_factory_.GetWeakPtr()));
+  NOTIMPLEMENTED();
+  return scheduler_->scheduled();
+}
+
 void CommandBufferDriver::OnSyncPointRetired() {
   scheduler_->SetScheduled(true);
 }
diff --git a/components/mus/gles2/command_buffer_driver.h b/components/mus/gles2/command_buffer_driver.h
index 97ede0c..9029d575 100644
--- a/components/mus/gles2/command_buffer_driver.h
+++ b/components/mus/gles2/command_buffer_driver.h
@@ -12,6 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "components/mus/public/interfaces/command_buffer.mojom.h"
+#include "gpu/command_buffer/common/constants.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace gpu {
@@ -73,6 +74,10 @@
                     mojo::Array<int32_t> attribs);
   void OnResize(gfx::Size size, float scale_factor);
   bool OnWaitSyncPoint(uint32_t sync_point);
+  void OnFenceSyncRelease(uint64_t release);
+  bool OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+                       uint64_t command_buffer_id,
+                       uint64_t release);
   void OnSyncPointRetired();
   void OnParseError();
   void OnContextLost(uint32_t reason);
diff --git a/components/mus/gles2/command_buffer_local.cc b/components/mus/gles2/command_buffer_local.cc
index 27442cb3..bb6f854 100644
--- a/components/mus/gles2/command_buffer_local.cc
+++ b/components/mus/gles2/command_buffer_local.cc
@@ -32,6 +32,7 @@
     : widget_(widget),
       gpu_state_(gpu_state),
       client_(client),
+      next_fence_sync_release_(1),
       weak_factory_(this) {}
 
 CommandBufferLocal::~CommandBufferLocal() {
@@ -92,6 +93,10 @@
       base::Bind(&CommandBufferLocal::OnResize, base::Unretained(this)));
   decoder_->SetWaitSyncPointCallback(
       base::Bind(&CommandBufferLocal::OnWaitSyncPoint, base::Unretained(this)));
+  decoder_->SetFenceSyncReleaseCallback(base::Bind(
+      &CommandBufferLocal::OnFenceSyncRelease, base::Unretained(this)));
+  decoder_->SetWaitFenceSyncCallback(
+      base::Bind(&CommandBufferLocal::OnWaitFenceSync, base::Unretained(this)));
 
   gpu::gles2::DisallowedFeatures disallowed_features;
 
@@ -220,6 +225,18 @@
   return 0;
 }
 
+uint64_t CommandBufferLocal::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool CommandBufferLocal::IsFenceSyncRelease(uint64_t release) {
+  return release > 0 && release < next_fence_sync_release_;
+}
+
+bool CommandBufferLocal::IsFenceSyncFlushed(uint64_t release) {
+  return IsFenceSyncRelease(release);
+}
+
 void CommandBufferLocal::PumpCommands() {
   if (!decoder_->MakeCurrent()) {
     command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
@@ -253,6 +270,45 @@
   return scheduler_->scheduled();
 }
 
+void CommandBufferLocal::OnFenceSyncRelease(uint64_t release) {
+  // TODO(dyen): Implement once CommandBufferID has been figured out and
+  // we have a SyncPointClient. It would probably look like what is commented
+  // out below:
+  // if (!sync_point_client_->client_state()->IsFenceSyncReleased(release))
+  //   sync_point_client_->ReleaseFenceSync(release);
+  NOTIMPLEMENTED();
+}
+
+bool CommandBufferLocal::OnWaitFenceSync(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint64_t release) {
+  gpu::SyncPointManager* sync_point_manager = gpu_state_->sync_point_manager();
+  DCHECK(sync_point_manager);
+
+  scoped_refptr<gpu::SyncPointClientState> release_state =
+      sync_point_manager->GetSyncPointClientState(namespace_id,
+                                                  command_buffer_id);
+
+  if (!release_state)
+    return true;
+
+  if (release_state->IsFenceSyncReleased(release))
+    return true;
+
+  // TODO(dyen): Implement once CommandBufferID has been figured out and
+  // we have a SyncPointClient. It would probably look like what is commented
+  // out below:
+  // scheduler_->SetScheduled(false);
+  // sync_point_client_->Wait(
+  //     release_state.get(),
+  //     release,
+  //     base::Bind(&CommandBufferLocal::OnSyncPointRetired,
+  //                weak_factory_.GetWeakPtr()));
+  NOTIMPLEMENTED();
+  return scheduler_->scheduled();
+}
+
 void CommandBufferLocal::OnParseError() {
   gpu::CommandBuffer::State state = command_buffer_->GetLastState();
   OnContextLost(state.context_lost_reason);
diff --git a/components/mus/gles2/command_buffer_local.h b/components/mus/gles2/command_buffer_local.h
index 92a79661..75c7bc4 100644
--- a/components/mus/gles2/command_buffer_local.h
+++ b/components/mus/gles2/command_buffer_local.h
@@ -73,6 +73,9 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
  private:
   void PumpCommands();
@@ -81,6 +84,10 @@
   void OnUpdateVSyncParameters(const base::TimeTicks timebase,
                                const base::TimeDelta interval);
   bool OnWaitSyncPoint(uint32_t sync_point);
+  void OnFenceSyncRelease(uint64_t release);
+  bool OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+                       uint64_t command_buffer_id,
+                       uint64_t release);
   void OnParseError();
   void OnContextLost(uint32_t reason);
   void OnSyncPointRetired();
@@ -94,6 +101,8 @@
   scoped_refptr<gfx::GLSurface> surface_;
   CommandBufferLocalClient* client_;
 
+  uint64_t next_fence_sync_release_;
+
   base::WeakPtrFactory<CommandBufferLocal> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CommandBufferLocal);
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 788b4f4..30199b4d 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -455,21 +455,27 @@
   const int original_version = meta_table_.GetVersionNumber();
   switch (original_version) {
     case 1:
-      if (!db_.Execute("ALTER TABLE logins "
-                       "ADD COLUMN password_type INTEGER") ||
+      // Column could exist because of https://crbug.com/295851
+      if (!db_.DoesColumnExist("logins", "password_type") &&
+          !db_.Execute("ALTER TABLE logins "
+                       "ADD COLUMN password_type INTEGER")) {
+        return false;
+      }
+      if (!db_.DoesColumnExist("logins", "possible_usernames") &&
           !db_.Execute("ALTER TABLE logins "
                        "ADD COLUMN possible_usernames BLOB")) {
         return false;
       }
     // Fall through.
     case 2:
-      if (!db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) {
+      // Column could exist because of https://crbug.com/295851
+      if (!db_.DoesColumnExist("logins", "times_used") &&
+          !db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) {
         return false;
       }
     // Fall through.
     case 3:
-      // We need to check if the column exists because of
-      // https://crbug.com/295851
+      // Column could exist because of https://crbug.com/295851
       if (!db_.DoesColumnExist("logins", "form_data") &&
           !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) {
         return false;
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 7faa7f6fd..00a1e06 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -1511,11 +1511,22 @@
   MigrationToVCurrent("login_db_v9_without_use_additional_auth_field.sql");
 }
 
+class LoginDatabaseMigrationTestBroken : public LoginDatabaseMigrationTest {};
+
+// Test migrating certain databases with incorrect version.
+// http://crbug.com/295851
+TEST_P(LoginDatabaseMigrationTestBroken, Broken) {
+  MigrationToVCurrent(base::StringPrintf("login_db_v%d_broken.sql", version()));
+}
+
 INSTANTIATE_TEST_CASE_P(MigrationToVCurrent,
                         LoginDatabaseMigrationTest,
                         testing::Range(1, kCurrentVersionNumber));
 INSTANTIATE_TEST_CASE_P(MigrationToVCurrent,
                         LoginDatabaseMigrationTestV9,
                         testing::Values(9));
+INSTANTIATE_TEST_CASE_P(MigrationToVCurrent,
+                        LoginDatabaseMigrationTestBroken,
+                        testing::Range(1, 4));
 
 }  // namespace password_manager
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index dcdd6a7..0bdcae6 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -260,6 +260,18 @@
     ]
   }
 
+  if (is_android || is_ios) {
+    sources -= [
+      "cloud/component_cloud_policy_service_unittest.cc",
+      "cloud/component_cloud_policy_store_unittest.cc",
+      "cloud/component_cloud_policy_updater_unittest.cc",
+      "cloud/external_policy_data_fetcher_unittest.cc",
+      "cloud/external_policy_data_updater_unittest.cc",
+      "cloud/resource_cache_unittest.cc",
+      "config_dir_policy_loader_unittest.cc",
+    ]
+  }
+
   deps = [
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/test/data/password_manager/login_db_v1_broken.sql b/components/test/data/password_manager/login_db_v1_broken.sql
new file mode 100644
index 0000000..3db20259
--- /dev/null
+++ b/components/test/data/password_manager/login_db_v1_broken.sql
@@ -0,0 +1,66 @@
+-- Version 4 schema with meta.version==1.  Tests http://crbug.com/295851
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('last_compatible_version','1');
+INSERT INTO "meta" VALUES('version','1');
+CREATE TABLE logins (
+origin_url VARCHAR NOT NULL,
+action_url VARCHAR,
+username_element VARCHAR,
+username_value VARCHAR,
+password_element VARCHAR,
+password_value BLOB,
+submit_element VARCHAR,
+signon_realm VARCHAR NOT NULL,
+ssl_valid INTEGER NOT NULL,
+preferred INTEGER NOT NULL,
+date_created INTEGER NOT NULL,
+blacklisted_by_user INTEGER NOT NULL,
+scheme INTEGER NOT NULL,
+password_type INTEGER,
+possible_usernames BLOB,
+times_used INTEGER,
+form_data BLOB,
+UNIQUE (origin_url, username_element, username_value, password_element, submit_element, signon_realm));
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402955745, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen2', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402950000, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+CREATE INDEX logins_signon ON logins (signon_realm);
+COMMIT;
+
diff --git a/components/test/data/password_manager/login_db_v2_broken.sql b/components/test/data/password_manager/login_db_v2_broken.sql
new file mode 100644
index 0000000..10da38b3
--- /dev/null
+++ b/components/test/data/password_manager/login_db_v2_broken.sql
@@ -0,0 +1,66 @@
+-- Version 4 schema with meta.version==2.  Tests http://crbug.com/295851
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('last_compatible_version','1');
+INSERT INTO "meta" VALUES('version','2');
+CREATE TABLE logins (
+origin_url VARCHAR NOT NULL,
+action_url VARCHAR,
+username_element VARCHAR,
+username_value VARCHAR,
+password_element VARCHAR,
+password_value BLOB,
+submit_element VARCHAR,
+signon_realm VARCHAR NOT NULL,
+ssl_valid INTEGER NOT NULL,
+preferred INTEGER NOT NULL,
+date_created INTEGER NOT NULL,
+blacklisted_by_user INTEGER NOT NULL,
+scheme INTEGER NOT NULL,
+password_type INTEGER,
+possible_usernames BLOB,
+times_used INTEGER,
+form_data BLOB,
+UNIQUE (origin_url, username_element, username_value, password_element, submit_element, signon_realm));
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402955745, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen2', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402950000, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+CREATE INDEX logins_signon ON logins (signon_realm);
+COMMIT;
+
diff --git a/components/test/data/password_manager/login_db_v3_broken.sql b/components/test/data/password_manager/login_db_v3_broken.sql
new file mode 100644
index 0000000..41262ff
--- /dev/null
+++ b/components/test/data/password_manager/login_db_v3_broken.sql
@@ -0,0 +1,66 @@
+-- Version 4 schema with meta.version==3.  Tests http://crbug.com/295851
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('last_compatible_version','1');
+INSERT INTO "meta" VALUES('version','3');
+CREATE TABLE logins (
+origin_url VARCHAR NOT NULL,
+action_url VARCHAR,
+username_element VARCHAR,
+username_value VARCHAR,
+password_element VARCHAR,
+password_value BLOB,
+submit_element VARCHAR,
+signon_realm VARCHAR NOT NULL,
+ssl_valid INTEGER NOT NULL,
+preferred INTEGER NOT NULL,
+date_created INTEGER NOT NULL,
+blacklisted_by_user INTEGER NOT NULL,
+scheme INTEGER NOT NULL,
+password_type INTEGER,
+possible_usernames BLOB,
+times_used INTEGER,
+form_data BLOB,
+UNIQUE (origin_url, username_element, username_value, password_element, submit_element, signon_realm));
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402955745, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+INSERT INTO "logins" VALUES(
+'https://accounts.google.com/ServiceLogin', /* origin_url */
+'https://accounts.google.com/ServiceLoginAuth', /* action_url */
+'Email', /* username_element */
+'theerikchen2', /* username_value */
+'Passwd', /* password_element */
+X'', /* password_value */
+'', /* submit_element */
+'https://accounts.google.com/', /* signon_realm */
+1, /* ssl_valid */
+1, /* preferred */
+1402950000, /* date_created */
+0, /* blacklisted_by_user */
+0, /* scheme */
+0, /* password_type */
+X'00000000', /* possible_usernames */
+1, /* times_used */
+X'18000000020000000000000000000000000000000000000000000000' /* form_data */
+);
+CREATE INDEX logins_signon ON logins (signon_realm);
+COMMIT;
+
diff --git a/components/test_runner/mock_web_user_media_client.cc b/components/test_runner/mock_web_user_media_client.cc
index f30fac9..7cff4c3 100644
--- a/components/test_runner/mock_web_user_media_client.cc
+++ b/components/test_runner/mock_web_user_media_client.cc
@@ -18,7 +18,6 @@
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebMediaDevicesRequest.h"
-#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"
 #include "third_party/WebKit/public/web/WebUserMediaRequest.h"
 
 using blink::WebMediaConstraints;
@@ -161,34 +160,34 @@
       return;
     }
 
-    const size_t zero = 0;
-    const size_t one = 1;
-    WebVector<WebMediaStreamTrack> audio_tracks(request.audio() ? one : zero);
-    WebVector<WebMediaStreamTrack> video_tracks(request.video() ? one : zero);
+    WebMediaStream stream;
+    stream.initialize(WebVector<WebMediaStreamTrack>(),
+                      WebVector<WebMediaStreamTrack>());
+    stream.setExtraData(new MockExtraData());
 
     if (request.audio()) {
       WebMediaStreamSource source;
       source.initialize("MockAudioDevice#1",
                         WebMediaStreamSource::TypeAudio,
                         "Mock audio device",
-                        false /* remote */, true /* readonly */);
-      audio_tracks[0].initialize(source);
+                        false /* remote */,
+                        true /* readonly */);
+      WebMediaStreamTrack web_track;
+      web_track.initialize(source);
+      stream.addTrack(web_track);
     }
 
-    if (request.video()) {
+    if (request.video() && !delegate_->AddMediaStreamSourceAndTrack(&stream)) {
       WebMediaStreamSource source;
       source.initialize("MockVideoDevice#1",
                         WebMediaStreamSource::TypeVideo,
                         "Mock video device",
                         false /* remote */, true /* readonly */);
-      video_tracks[0].initialize(source);
+      WebMediaStreamTrack web_track;
+      web_track.initialize(source);
+      stream.addTrack(web_track);
     }
 
-    WebMediaStream stream;
-    stream.initialize(audio_tracks, video_tracks);
-
-    stream.setExtraData(new MockExtraData());
-
     delegate_->PostTask(new UserMediaRequestTask(this, request, stream));
 }
 
diff --git a/components/test_runner/mock_web_user_media_client.h b/components/test_runner/mock_web_user_media_client.h
index dda0d4df..33b55d5 100644
--- a/components/test_runner/mock_web_user_media_client.h
+++ b/components/test_runner/mock_web_user_media_client.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_TEST_RUNNER_MOCK_WEB_USER_MEDIA_CLIENT_H_
 #define COMPONENTS_TEST_RUNNER_MOCK_WEB_USER_MEDIA_CLIENT_H_
 
-#include "base/macros.h"
 #include "components/test_runner/web_task.h"
 #include "third_party/WebKit/public/web/WebUserMediaClient.h"
 
diff --git a/components/test_runner/test_runner.h b/components/test_runner/test_runner.h
index 45ee51a..b227a7ee 100644
--- a/components/test_runner/test_runner.h
+++ b/components/test_runner/test_runner.h
@@ -23,6 +23,7 @@
 namespace blink {
 class WebContentSettingsClient;
 class WebFrame;
+class WebMediaStream;
 class WebString;
 class WebView;
 class WebURLResponse;
diff --git a/components/test_runner/web_test_delegate.h b/components/test_runner/web_test_delegate.h
index 26b135a6..d53852e 100644
--- a/components/test_runner/web_test_delegate.h
+++ b/components/test_runner/web_test_delegate.h
@@ -27,13 +27,14 @@
 class WebHistoryItem;
 class WebLayer;
 class WebLocalFrame;
+class WebMediaStream;
 class WebPlugin;
 struct WebPluginParams;
-class WebURLResponse;
-class WebView;
 struct WebRect;
 struct WebSize;
 struct WebURLError;
+class WebURLResponse;
+class WebView;
 }
 
 namespace cc {
@@ -244,6 +245,9 @@
   // Clear all the permissions set via SetPermission().
   virtual void ResetPermissions() = 0;
 
+  // Add content MediaStream classes to the Blink MediaStream ones.
+  virtual bool AddMediaStreamSourceAndTrack(blink::WebMediaStream* stream) = 0;
+
   virtual cc::SharedBitmapManager* GetSharedBitmapManager() = 0;
 
   // Causes the beforeinstallprompt event to be sent to the renderer with a
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index f893d6a..c2dda31 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -453,7 +453,7 @@
     // See note at the initialization of ExitManager, below; basically,
     // only Android builds have the ctor/dtor handlers set up to use
     // TRACE_EVENT right away.
-    TRACE_EVENT0("startup", "ContentMainRunnerImpl::Initialize");
+    TRACE_EVENT0("startup,benchmark", "ContentMainRunnerImpl::Initialize");
 #endif  // OS_ANDROID
 
     // NOTE(willchan): One might ask why these TCMalloc-related calls are done
@@ -638,7 +638,7 @@
     // Android tracing started at the beginning of the method.
     // Other OSes have to wait till we get here in order for all the memory
     // management setup to be completed.
-    TRACE_EVENT0("startup", "ContentMainRunnerImpl::Initialize");
+    TRACE_EVENT0("startup,benchmark", "ContentMainRunnerImpl::Initialize");
 #endif // !OS_ANDROID
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 150d0f4..8df6ad27 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -640,6 +640,29 @@
           GetRole() == ui::AX_ROLE_ROW_HEADER);
 }
 
+bool BrowserAccessibility::HasCaret() const {
+  if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) &&
+      HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) &&
+      HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) {
+    return true;
+  }
+
+  BrowserAccessibility* root = manager()->GetRoot();
+  // The caret is always at the focus of the selection.
+  int32 focus_id;
+  if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id))
+    return false;
+
+  BrowserAccessibility* focus_object = manager()->GetFromID(focus_id);
+  if (!focus_object)
+    return false;
+
+  if (!focus_object->IsDescendantOf(this))
+    return false;
+
+  return true;
+}
+
 bool BrowserAccessibility::IsEditableText() const {
   return HasState(ui::AX_STATE_EDITABLE);
 }
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 9e61c4af..10bde83 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -265,6 +265,9 @@
   // Returns true if this node is an cell or an table header.
   bool IsCellOrTableHeaderRole() const;
 
+  // Returns true if the caret is active on this object.
+  bool HasCaret() const;
+
   // Returns true if this node is an editable text field of any kind.
   bool IsEditableText() const;
 
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index 8501ba61..2704b423 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -2013,9 +2013,12 @@
   if (!offset)
     return E_INVALIDARG;
 
+  if (!HasCaret())
+    return S_FALSE;
+
   int selection_start, selection_end;
   GetSelectionOffsets(&selection_start, &selection_end);
-  // The caret is always at the end of the selection, if a selection exists.
+  // The caret is always at the end of the selection.
   *offset = selection_end;
   if (*offset < 0)
     return S_FALSE;
@@ -3847,27 +3850,22 @@
   if (*selection_start < 0 || *selection_end < 0)
     return;
 
-  // If the selection is collapsed or if it only spans one character, return the
-  // selection offsets only if the caret is active on this object or any of its
-  // children.
-  // The focus object indicates the caret position.
-  if (*selection_start == *selection_end) {
-    BrowserAccessibility* root = manager()->GetRoot();
-    int32 focus_id;
-    if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id))
-      return;
-
-    BrowserAccessibilityWin* focus_object =
-        manager()->GetFromID(focus_id)->ToBrowserAccessibilityWin();
-    if (!focus_object)
-      return;
-
-    if (!focus_object->IsDescendantOf(this) &&
-        !(IsTextOnlyObject() && GetParent() == focus_object)) {
-      *selection_start = -1;
-      *selection_end = -1;
-      return;
-    }
+  // There are three cases when a selection would start and end on the same
+  // character:
+  // 1. Anchor and focus are both in a subtree that is to the right of this
+  // object.
+  // 2. Anchor and focus are both in a subtree that is to the left of this
+  // object.
+  // 3. Anchor and focus are in a subtree represented by a single embedded
+  // object character.
+  // Only case 3 refers to a valid selection because cases 1 and 2 fall
+  // outside this object in their entirety.
+  // Selections that span more than one character are by definition inside this
+  // object, so checking them is not necessary.
+  if (*selection_start == *selection_end && !HasCaret()) {
+    *selection_start = -1;
+    *selection_end = -1;
+    return;
   }
 
   // The IA2 Spec says that if the largest of the two offsets falls on an
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index c1d99b2..008dcc70 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -951,9 +951,9 @@
   link_text.SetName("here");
 
   // Place the caret between 'h' and 'e'.
-  root.AddIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, 4);
+  root.AddIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, 5);
   root.AddIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, 1);
-  root.AddIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, 4);
+  root.AddIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, 5);
   root.AddIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, 1);
 
   root.child_ids.push_back(2);
@@ -1065,7 +1065,7 @@
   link_text.state = (1 << ui::AX_STATE_FOCUSABLE) | (1 << ui::AX_STATE_LINKED);
   link_text.SetName("here");
 
-  // Select the part of the text "lick here".
+  // Select the following part of the text: "lick here".
   root.AddIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, 3);
   root.AddIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, 1);
   root.AddIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, 5);
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index a066cd6..ee1bb84 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -36,17 +36,8 @@
 // registrations across all registered service workers for a profile.
 // Registrations are stored along with their associated Service Worker
 // registration in ServiceWorkerStorage. If the ServiceWorker is unregistered,
-// the sync registrations are removed. This class expects to be run on the IO
+// the sync registrations are removed. This class must be run on the IO
 // thread. The asynchronous methods are executed sequentially.
-
-// TODO(jkarlin): Check permissions when registering, scheduling, and firing
-// background sync. In the meantime, --enable-service-worker-sync is required to
-// fire a sync event.
-// TODO(jkarlin): Unregister syncs when permission is revoked.
-// TODO(jkarlin): Create a background sync scheduler to actually run the
-// registered events.
-// TODO(jkarlin): Keep the browser alive if "Let Google Chrome Run in the
-// Background" is true and a sync is registered.
 class CONTENT_EXPORT BackgroundSyncManager
     : NON_EXPORTED_BASE(public ServiceWorkerContextObserver) {
  public:
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 4d8c198c..a14cfb5 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -414,4 +414,46 @@
   observer.Wait();
 }
 
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+                       InspectDuringLocalToRemoteFrameSwap) {
+  host_resolver()->AddRule("*", "127.0.0.1");
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  content::SetupCrossSiteRedirector(embedded_test_server());
+
+  GURL test_url1 =
+      embedded_test_server()->GetURL("A.com", "/devtools/navigation.html");
+  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url1, 1);
+
+  ShellAddedObserver new_shell_observer;
+  EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
+                            "window.open('about:blank','foo');"));
+  Shell* new_shell = new_shell_observer.GetShell();
+  EXPECT_TRUE(new_shell->web_contents()->HasOpener());
+
+  agent_host_ = DevToolsAgentHost::GetOrCreateFor(new_shell->web_contents());
+  agent_host_->AttachClient(this);
+
+  GURL test_url2 =
+      embedded_test_server()->GetURL("B.com", "/devtools/navigation.html");
+
+  // After this navigation, if the bug exists, the process will crash.
+  NavigateToURLBlockUntilNavigationsComplete(new_shell, test_url2, 1);
+
+  // Ensure that the A.com process is still alive by executing a script in the
+  // original tab.
+  //
+  // TODO(alexmos, nasko):  A better way to do this is to navigate the original
+  // tab to another site, watch for process exit, and check whether there was a
+  // crash. However, currently there's no way to wait for process exit
+  // regardless of whether it's a crash or not.  RenderProcessHostWatcher
+  // should be fixed to support waiting on both WATCH_FOR_PROCESS_EXIT and
+  // WATCH_FOR_HOST_DESTRUCTION, and then used here.
+  bool success = false;
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(),
+                                          "window.domAutomationController.send("
+                                          "    !!window.open('', 'foo'));",
+                                          &success));
+  EXPECT_TRUE(success);
+}
+
 }  // namespace content
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
index 777f508..4f637605 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
@@ -17,7 +17,7 @@
 #include "content/common/generic_shared_memory_id_generator.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
-#include "content/common/gpu/gpu_memory_buffer_factory_shared_memory.h"
+#include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -47,31 +47,26 @@
       FROM_HERE, base::Bind(destruction_callback, sync_point));
 }
 
-bool IsGpuMemoryBufferFactoryConfigurationSupported(
-    gfx::GpuMemoryBufferType type,
-    const GpuMemoryBufferFactory::Configuration& configuration) {
-  switch (type) {
+bool IsNativeGpuMemoryBufferFactoryConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  switch (GpuMemoryBufferFactory::GetNativeType()) {
     case gfx::SHARED_MEMORY_BUFFER:
-      return GpuMemoryBufferFactorySharedMemory::
-          IsGpuMemoryBufferConfigurationSupported(configuration.format,
-                                                  configuration.usage);
+      return false;
 #if defined(OS_MACOSX)
     case gfx::IO_SURFACE_BUFFER:
       return GpuMemoryBufferFactoryIOSurface::
-          IsGpuMemoryBufferConfigurationSupported(configuration.format,
-                                                  configuration.usage);
+          IsGpuMemoryBufferConfigurationSupported(format, usage);
 #endif
 #if defined(OS_ANDROID)
     case gfx::SURFACE_TEXTURE_BUFFER:
       return GpuMemoryBufferFactorySurfaceTexture::
-          IsGpuMemoryBufferConfigurationSupported(configuration.format,
-                                                  configuration.usage);
+          IsGpuMemoryBufferConfigurationSupported(format, usage);
 #endif
 #if defined(USE_OZONE)
     case gfx::OZONE_NATIVE_PIXMAP:
       return GpuMemoryBufferFactoryOzoneNativePixmap::
-          IsGpuMemoryBufferConfigurationSupported(configuration.format,
-                                                  configuration.usage);
+          IsGpuMemoryBufferConfigurationSupported(format, usage);
 #endif
     default:
       NOTREACHED();
@@ -79,18 +74,8 @@
   }
 }
 
-gfx::GpuMemoryBufferType GetGpuMemoryBufferFactoryType() {
-  std::vector<gfx::GpuMemoryBufferType> supported_types;
-  GpuMemoryBufferFactory::GetSupportedTypes(&supported_types);
-  DCHECK(!supported_types.empty());
-
-  // The GPU service will always use the preferred type.
-  return supported_types[0];
-}
-
-std::vector<GpuMemoryBufferFactory::Configuration>
-GetSupportedGpuMemoryBufferConfigurations(gfx::GpuMemoryBufferType type) {
-  std::vector<GpuMemoryBufferFactory::Configuration> configurations;
+GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() {
+  GpuMemoryBufferConfigurationSet configurations;
 #if defined(OS_MACOSX)
   bool enable_native_gpu_memory_buffers =
       !base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -101,45 +86,46 @@
           switches::kEnableNativeGpuMemoryBuffers);
 #endif
 
+#if defined(USE_OZONE) || defined(OS_MACOSX)
+  bool force_native_scanout_formats = true;
+#else
+  bool force_native_scanout_formats = false;
+#endif
+
   // Disable native buffers when using Mesa.
   if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kUseGL) == gfx::kGLImplementationOSMesaName) {
     enable_native_gpu_memory_buffers = false;
+    force_native_scanout_formats = false;
   }
 
   if (enable_native_gpu_memory_buffers) {
-    const GpuMemoryBufferFactory::Configuration kNativeConfigurations[] = {
-        {gfx::BufferFormat::R_8, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::R_8, gfx::BufferUsage::PERSISTENT_MAP},
-        {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::PERSISTENT_MAP},
-        {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::PERSISTENT_MAP},
-        {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::PERSISTENT_MAP},
-        {gfx::BufferFormat::UYVY_422, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::UYVY_422, gfx::BufferUsage::PERSISTENT_MAP},
-        {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::MAP},
-        {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::PERSISTENT_MAP},
-    };
-    for (auto& configuration : kNativeConfigurations) {
-      if (IsGpuMemoryBufferFactoryConfigurationSupported(type, configuration))
-        configurations.push_back(configuration);
+    const gfx::BufferFormat kNativeFormats[] = {
+        gfx::BufferFormat::R_8,       gfx::BufferFormat::RGBA_4444,
+        gfx::BufferFormat::RGBA_8888, gfx::BufferFormat::BGRA_8888,
+        gfx::BufferFormat::UYVY_422,  gfx::BufferFormat::YUV_420_BIPLANAR};
+    const gfx::BufferUsage kNativeUsages[] = {gfx::BufferUsage::MAP,
+                                              gfx::BufferUsage::PERSISTENT_MAP};
+    for (auto& format : kNativeFormats) {
+      for (auto& usage : kNativeUsages) {
+        if (IsNativeGpuMemoryBufferFactoryConfigurationSupported(format, usage))
+          configurations.insert(std::make_pair(format, usage));
+      }
     }
   }
 
-#if defined(USE_OZONE) || defined(OS_MACOSX)
-  const GpuMemoryBufferFactory::Configuration kScanoutConfigurations[] = {
-      {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT},
-      {gfx::BufferFormat::BGRX_8888, gfx::BufferUsage::SCANOUT},
-      {gfx::BufferFormat::UYVY_422, gfx::BufferUsage::SCANOUT},
-      {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::SCANOUT},
-  };
-  for (auto& configuration : kScanoutConfigurations) {
-    if (IsGpuMemoryBufferFactoryConfigurationSupported(type, configuration))
-      configurations.push_back(configuration);
+  if (force_native_scanout_formats) {
+    const gfx::BufferFormat kScanoutFormats[] = {
+        gfx::BufferFormat::BGRA_8888, gfx::BufferFormat::BGRX_8888,
+        gfx::BufferFormat::UYVY_422, gfx::BufferFormat::YUV_420_BIPLANAR};
+    for (auto& format : kScanoutFormats) {
+      if (IsNativeGpuMemoryBufferFactoryConfigurationSupported(
+              format, gfx::BufferUsage::SCANOUT)) {
+        configurations.insert(
+            std::make_pair(format, gfx::BufferUsage::SCANOUT));
+      }
+    }
   }
-#endif
 
   return configurations;
 }
@@ -173,9 +159,7 @@
 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
     int gpu_client_id,
     uint64_t gpu_client_tracing_id)
-    : factory_type_(GetGpuMemoryBufferFactoryType()),
-      supported_configurations_(
-          GetSupportedGpuMemoryBufferConfigurations(factory_type_)),
+    : native_configurations_(GetNativeGpuMemoryBufferConfigurations()),
       gpu_client_id_(gpu_client_id),
       gpu_client_tracing_id_(gpu_client_tracing_id),
       gpu_host_id_(0) {
@@ -196,25 +180,30 @@
 uint32 BrowserGpuMemoryBufferManager::GetImageTextureTarget(
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
-  gfx::GpuMemoryBufferType type = GetGpuMemoryBufferFactoryType();
-  for (auto& configuration : GetSupportedGpuMemoryBufferConfigurations(type)) {
-    if (configuration.format != format || configuration.usage != usage)
-      continue;
-
-    switch (type) {
-      case gfx::SURFACE_TEXTURE_BUFFER:
-      case gfx::OZONE_NATIVE_PIXMAP:
-        // GPU memory buffers that are shared with the GL using EGLImages
-        // require TEXTURE_EXTERNAL_OES.
-        return GL_TEXTURE_EXTERNAL_OES;
-      case gfx::IO_SURFACE_BUFFER:
-        // IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB.
-        return GL_TEXTURE_RECTANGLE_ARB;
-      default:
-        return GL_TEXTURE_2D;
-    }
+  GpuMemoryBufferConfigurationSet native_configurations =
+      GetNativeGpuMemoryBufferConfigurations();
+  if (native_configurations.find(std::make_pair(format, usage)) ==
+      native_configurations.end()) {
+    return GL_TEXTURE_2D;
   }
 
+  switch (GpuMemoryBufferFactory::GetNativeType()) {
+    case gfx::SURFACE_TEXTURE_BUFFER:
+    case gfx::OZONE_NATIVE_PIXMAP:
+      // GPU memory buffers that are shared with the GL using EGLImages
+      // require TEXTURE_EXTERNAL_OES.
+      return GL_TEXTURE_EXTERNAL_OES;
+    case gfx::IO_SURFACE_BUFFER:
+      // IOSurface backed images require GL_TEXTURE_RECTANGLE_ARB.
+      return GL_TEXTURE_RECTANGLE_ARB;
+    case gfx::SHARED_MEMORY_BUFFER:
+      return GL_TEXTURE_2D;
+    case gfx::EMPTY_BUFFER:
+      NOTREACHED();
+      return GL_TEXTURE_2D;
+  }
+
+  NOTREACHED();
   return GL_TEXTURE_2D;
 }
 
@@ -245,8 +234,8 @@
     const AllocationCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // Use service side allocation if this is a supported configuration.
-  if (IsGpuMemoryBufferConfigurationSupported(format, usage)) {
+  // Use service side allocation for native configurations.
+  if (IsNativeGpuMemoryBufferConfiguration(format, usage)) {
     AllocateGpuMemoryBufferOnIO(id, size, format, usage, child_client_id, 0,
                                 false, callback);
     return;
@@ -391,14 +380,11 @@
   return request.result.Pass();
 }
 
-bool BrowserGpuMemoryBufferManager::IsGpuMemoryBufferConfigurationSupported(
+bool BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBufferConfiguration(
     gfx::BufferFormat format,
     gfx::BufferUsage usage) const {
-  for (auto& configuration : supported_configurations_) {
-    if (configuration.format == format && configuration.usage == usage)
-      return true;
-  }
-  return false;
+  return native_configurations_.find(std::make_pair(format, usage)) !=
+         native_configurations_.end();
 }
 
 void BrowserGpuMemoryBufferManager::AllocateGpuMemoryBufferForSurfaceOnIO(
@@ -407,9 +393,8 @@
 
   gfx::GpuMemoryBufferId new_id = content::GetNextGenericSharedMemoryId();
 
-  // Use service side allocation if this is a supported configuration.
-  if (IsGpuMemoryBufferConfigurationSupported(request->format,
-                                              request->usage)) {
+  // Use service side allocation for native configurations.
+  if (IsNativeGpuMemoryBufferConfiguration(request->format, request->usage)) {
     // Note: Unretained is safe as this is only used for synchronous allocation
     // from a non-IO thread.
     AllocateGpuMemoryBufferOnIO(
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.h b/content/browser/gpu/browser_gpu_memory_buffer_manager.h
index 12fd082..4c27505 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.h
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.h
@@ -5,16 +5,37 @@
 #ifndef CONTENT_BROWSER_GPU_BROWSER_GPU_MEMORY_BUFFER_MANAGER_H_
 #define CONTENT_BROWSER_GPU_BROWSER_GPU_MEMORY_BUFFER_MANAGER_H_
 
-#include <vector>
+#include <utility>
 
 #include "base/callback.h"
+#include "base/containers/hash_tables.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "content/common/content_export.h"
-#include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 
 namespace content {
 
+using GpuMemoryBufferConfigurationKey =
+    std::pair<gfx::BufferFormat, gfx::BufferUsage>;
+using GpuMemoryBufferConfigurationSet =
+    base::hash_set<GpuMemoryBufferConfigurationKey>;
+
+}  // content
+
+namespace BASE_HASH_NAMESPACE {
+
+template <>
+struct hash<content::GpuMemoryBufferConfigurationKey> {
+  size_t operator()(const content::GpuMemoryBufferConfigurationKey& key) const {
+    return base::HashPair(static_cast<int>(key.first),
+                          static_cast<int>(key.second));
+  }
+};
+
+}  // namespace BASE_HASH_NAMESPACE
+
+namespace content {
+
 class CONTENT_EXPORT BrowserGpuMemoryBufferManager
     : public gpu::GpuMemoryBufferManager,
       public base::trace_event::MemoryDumpProvider {
@@ -97,8 +118,8 @@
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
       int32 surface_id);
-  bool IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
-                                               gfx::BufferUsage usage) const;
+  bool IsNativeGpuMemoryBufferConfiguration(gfx::BufferFormat format,
+                                            gfx::BufferUsage usage) const;
   void AllocateGpuMemoryBufferForSurfaceOnIO(
       AllocateGpuMemoryBufferRequest* request);
   void GpuMemoryBufferAllocatedForSurfaceOnIO(
@@ -125,9 +146,7 @@
 
   uint64_t ClientIdToTracingProcessId(int client_id) const;
 
-  const gfx::GpuMemoryBufferType factory_type_;
-  const std::vector<GpuMemoryBufferFactory::Configuration>
-      supported_configurations_;
+  const GpuMemoryBufferConfigurationSet native_configurations_;
   const int gpu_client_id_;
   const uint64_t gpu_client_tracing_id_;
 
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index e0db04b..97ac7b5 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -1090,8 +1090,10 @@
     }
 
     LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
-    leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
-    if (!db) {
+    *status =
+        leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
+    if (!status->ok()) {
+      DCHECK(!db);
       LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
                           origin_url);
@@ -1111,11 +1113,14 @@
              task_runner,
              status);
 
-  if (clean_journal && backing_store.get() &&
-      !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
-    HistogramOpenStatus(
-        INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
-    return scoped_refptr<IndexedDBBackingStore>();
+  if (clean_journal && backing_store.get()) {
+    *status = backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode());
+    if (!status->ok()) {
+      HistogramOpenStatus(
+          INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
+          origin_url);
+      return scoped_refptr<IndexedDBBackingStore>();
+    }
   }
   return backing_store;
 }
diff --git a/content/browser/loader/cross_site_resource_handler.cc b/content/browser/loader/cross_site_resource_handler.cc
index 0b9e6a77..45397357 100644
--- a/content/browser/loader/cross_site_resource_handler.cc
+++ b/content/browser/loader/cross_site_resource_handler.cc
@@ -96,8 +96,10 @@
   // Without a valid RFH against which to check, we must cancel the request,
   // to prevent the resource at |url| from being delivered to a potentially
   // unsuitable renderer process.
+  // TODO(nick): Switch this back to NavigationDecision::CANCEL once we fix
+  // existing transfer unittests that don't specify a valid rfh ID.
   if (!rfh)
-    return CrossSiteResourceHandler::NavigationDecision::CANCEL_REQUEST;
+    return CrossSiteResourceHandler::NavigationDecision::USE_EXISTING_RENDERER;
 
   RenderFrameHostManager* manager = rfh->frame_tree_node()->render_manager();
   if (manager->IsRendererTransferNeededForNavigation(rfh, real_url))
diff --git a/content/browser/loader/cross_site_resource_handler_browsertest.cc b/content/browser/loader/cross_site_resource_handler_browsertest.cc
index 4d66c1a..ba0f3cc 100644
--- a/content/browser/loader/cross_site_resource_handler_browsertest.cc
+++ b/content/browser/loader/cross_site_resource_handler_browsertest.cc
@@ -239,8 +239,10 @@
 
 // Regression test for https://crbug.com/538784 -- ensures that one can't
 // sidestep CrossSiteResourceHandler by detaching a frame mid-request.
+//
+// TODO(nick): Disabled until we re-land the fix for https://crbug.com/538784.
 IN_PROC_BROWSER_TEST_F(CrossSiteResourceHandlerTest,
-                       NoDeliveryToDetachedFrame) {
+                       DISABLED_NoDeliveryToDetachedFrame) {
   GURL attacker_page = embedded_test_server()->GetURL(
       "evil.com", "/cross_site_iframe_factory.html?evil(evil)");
   EXPECT_TRUE(NavigateToURL(shell(), attacker_page));
diff --git a/content/browser/renderer_host/input/web_input_event_builders_mac.mm b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
index 00d34f8..1e116e84 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_mac.mm
+++ b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
@@ -502,7 +502,7 @@
   result->globalX = screen_local.x;
   // Flip y.
   NSScreen* primary_screen = ([[NSScreen screens] count] > 0)
-                                 ? [[NSScreen screens] objectAtIndex:0]
+                                 ? [[NSScreen screens] firstObject]
                                  : nil;
   if (primary_screen)
     result->globalY = [primary_screen frame].size.height - screen_local.y;
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index dc03b79..0f7b2378 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1226,6 +1226,7 @@
     switches::kDisableAcceleratedVideoDecode,
     switches::kDisableBlinkFeatures,
     switches::kDisableBreakpad,
+    switches::kDisableCompositorAnimationTimelines,
     switches::kDisablePreferCompositingToLCDText,
     switches::kDisableDatabases,
     switches::kDisableDelayAgnosticAec,
@@ -1264,7 +1265,6 @@
     switches::kDomAutomationController,
     switches::kEnableBlinkFeatures,
     switches::kEnableBrowserSideNavigation,
-    switches::kEnableCompositorAnimationTimelines,
     switches::kEnableCredentialManagerAPI,
     switches::kEnableDisplayList2dCanvas,
     switches::kEnableDistanceFieldText,
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 8db1f59b..00c46f2 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -354,7 +354,7 @@
   if (!g_screen_info_up_to_date) {
     if ([[NSScreen screens] count] > 0) {
       screen_zero_height =
-          [[[NSScreen screens] objectAtIndex:0] frame].size.height;
+          [[[NSScreen screens] firstObject] frame].size.height;
       g_screen_info_up_to_date = true;
     } else {
       return y;
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 9e1f792..5588f1f 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -122,7 +122,7 @@
   bounds.origin = [window convertBaseToScreen:bounds.origin];
 
   // Flip y to account for screen flip.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   bounds.origin.y = [screen frame].size.height - bounds.origin.y
       - bounds.size.height;
   *out = gfx::Rect(NSRectToCGRect(bounds));
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.cc b/content/common/gpu/client/command_buffer_proxy_impl.cc
index fc0cb83..cb509a83 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.cc
+++ b/content/common/gpu/client/command_buffer_proxy_impl.cc
@@ -46,6 +46,9 @@
       flush_count_(0),
       last_put_offset_(-1),
       last_barrier_put_offset_(-1),
+      next_fence_sync_release_(1),
+      flushed_fence_sync_release_(0),
+      verified_fence_sync_release_(0),
       next_signal_id_(0) {
   DCHECK(channel);
 }
@@ -212,8 +215,18 @@
   last_barrier_put_offset_ = put_offset;
 
   if (channel_) {
-    channel_->OrderingBarrier(route_id_, stream_id_, put_offset, ++flush_count_,
-                              latency_info_, put_offset_changed, true);
+    const uint32_t flush_id = channel_->OrderingBarrier(
+        route_id_, stream_id_, put_offset, ++flush_count_, latency_info_,
+        put_offset_changed, true);
+    if (put_offset_changed) {
+      DCHECK(flush_id);
+      const uint64_t fence_sync_release = next_fence_sync_release_ - 1;
+      if (fence_sync_release > flushed_fence_sync_release_) {
+        flushed_fence_sync_release_ = fence_sync_release;
+        flushed_release_flush_id_.push(
+            std::make_pair(fence_sync_release, flush_id));
+      }
+    }
   }
 
   if (put_offset_changed)
@@ -231,8 +244,18 @@
   last_barrier_put_offset_ = put_offset;
 
   if (channel_) {
-    channel_->OrderingBarrier(route_id_, stream_id_, put_offset, ++flush_count_,
-                              latency_info_, put_offset_changed, false);
+    const uint32_t flush_id = channel_->OrderingBarrier(
+        route_id_, stream_id_, put_offset, ++flush_count_, latency_info_,
+        put_offset_changed, false);
+    if (put_offset_changed) {
+      DCHECK(flush_id);
+      const uint64_t fence_sync_release = next_fence_sync_release_ - 1;
+      if (fence_sync_release > flushed_fence_sync_release_) {
+        flushed_fence_sync_release_ = fence_sync_release;
+        flushed_release_flush_id_.push(
+            std::make_pair(fence_sync_release, flush_id));
+      }
+    }
   }
 
   if (put_offset_changed)
@@ -473,6 +496,38 @@
   return command_buffer_id_;
 }
 
+uint64_t CommandBufferProxyImpl::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool CommandBufferProxyImpl::IsFenceSyncRelease(uint64_t release) {
+  return release != 0 && release < next_fence_sync_release_;
+}
+
+bool CommandBufferProxyImpl::IsFenceSyncFlushed(uint64_t release) {
+  CheckLock();
+  if (last_state_.error != gpu::error::kNoError)
+    return false;
+
+  if (release <= verified_fence_sync_release_)
+    return true;
+
+  // Check if we have actually flushed the fence sync release.
+  if (release <= flushed_fence_sync_release_) {
+    DCHECK(!flushed_release_flush_id_.empty());
+    // Check if it has already been validated by another context.
+    UpdateVerifiedReleases(channel_->GetHighestValidatedFlushID(stream_id_));
+    if (release <= verified_fence_sync_release_)
+      return true;
+
+    // Has not been validated, validate it now.
+    UpdateVerifiedReleases(channel_->ValidateFlushIDReachedServer(stream_id_));
+    return release <= verified_fence_sync_release_;
+  }
+
+  return false;
+}
+
 uint32 CommandBufferProxyImpl::InsertSyncPoint() {
   CheckLock();
   if (last_state_.error != gpu::error::kNoError)
@@ -619,6 +674,17 @@
     shared_state()->Read(&last_state_);
 }
 
+void CommandBufferProxyImpl::UpdateVerifiedReleases(uint32_t verified_flush) {
+  while (!flushed_release_flush_id_.empty()) {
+    const std::pair<uint64_t, uint32_t>& front_item =
+        flushed_release_flush_id_.front();
+    if (front_item.second > verified_flush)
+      break;
+    verified_fence_sync_release_ = front_item.first;
+    flushed_release_flush_id_.pop();
+  }
+}
+
 gpu::CommandBufferSharedState* CommandBufferProxyImpl::shared_state() const {
   return reinterpret_cast<gpu::CommandBufferSharedState*>(
       shared_state_shm_->memory());
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.h b/content/common/gpu/client/command_buffer_proxy_impl.h
index 98f8ed7..61ba775 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.h
+++ b/content/common/gpu/client/command_buffer_proxy_impl.h
@@ -123,6 +123,9 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
   bool ProduceFrontBuffer(const gpu::Mailbox& mailbox);
   void SetContextLostCallback(const base::Closure& callback);
@@ -191,6 +194,9 @@
   // Try to read an updated copy of the state from shared memory.
   void TryUpdateState();
 
+  // Updates the highest verified release fence sync.
+  void UpdateVerifiedReleases(uint32_t verified_flush);
+
   // The shared memory area used to update state.
   gpu::CommandBufferSharedState* shared_state() const;
 
@@ -215,6 +221,18 @@
   int32 last_put_offset_;
   int32 last_barrier_put_offset_;
 
+  // Next generated fence sync.
+  uint64_t next_fence_sync_release_;
+
+  // Unverified flushed fence syncs with their corresponding flush id.
+  std::queue<std::pair<uint64_t, uint32_t>> flushed_release_flush_id_;
+
+  // Last flushed fence sync release, same as last item in queue if not empty.
+  uint64_t flushed_fence_sync_release_;
+
+  // Last verified fence sync.
+  uint64_t verified_fence_sync_release_;
+
   base::Closure context_lost_callback_;
 
   GpuConsoleMessageCallback console_message_callback_;
diff --git a/content/common/gpu/client/gpu_channel_host.cc b/content/common/gpu/client/gpu_channel_host.cc
index a87051a..b637624 100644
--- a/content/common/gpu/client/gpu_channel_host.cc
+++ b/content/common/gpu/client/gpu_channel_host.cc
@@ -35,10 +35,14 @@
 }  // namespace
 
 GpuChannelHost::StreamFlushInfo::StreamFlushInfo()
-    : flush_pending(false),
+    : next_stream_flush_id(1),
+      flushed_stream_flush_id(0),
+      verified_stream_flush_id(0),
+      flush_pending(false),
       route_id(MSG_ROUTING_NONE),
       put_offset(0),
-      flush_count(0) {}
+      flush_count(0),
+      flush_id(0) {}
 
 GpuChannelHost::StreamFlushInfo::~StreamFlushInfo() {}
 
@@ -127,7 +131,7 @@
   return result;
 }
 
-void GpuChannelHost::OrderingBarrier(
+uint32_t GpuChannelHost::OrderingBarrier(
     int32 route_id,
     int32 stream_id,
     int32 put_offset,
@@ -138,30 +142,38 @@
   AutoLock lock(context_lock_);
   StreamFlushInfo& flush_info = stream_flush_info_[stream_id];
   if (flush_info.flush_pending && flush_info.route_id != route_id)
-    InternalFlush(stream_id);
+    InternalFlush(&flush_info);
 
   if (put_offset_changed) {
+    const uint32_t flush_id = flush_info.next_stream_flush_id++;
     flush_info.flush_pending = true;
     flush_info.route_id = route_id;
     flush_info.put_offset = put_offset;
     flush_info.flush_count = flush_count;
+    flush_info.flush_id = flush_id;
     flush_info.latency_info.insert(flush_info.latency_info.end(),
                                    latency_info.begin(), latency_info.end());
 
     if (do_flush)
-      InternalFlush(stream_id);
+      InternalFlush(&flush_info);
+
+    return flush_id;
   }
+  return 0;
 }
 
-void GpuChannelHost::InternalFlush(int32 stream_id) {
+void GpuChannelHost::InternalFlush(StreamFlushInfo* flush_info) {
   context_lock_.AssertAcquired();
-  StreamFlushInfo& flush_info = stream_flush_info_[stream_id];
-  DCHECK(flush_info.flush_pending);
+  DCHECK(flush_info);
+  DCHECK(flush_info->flush_pending);
+  DCHECK_LT(flush_info->flushed_stream_flush_id, flush_info->flush_id);
   Send(new GpuCommandBufferMsg_AsyncFlush(
-      flush_info.route_id, flush_info.put_offset, flush_info.flush_count,
-      flush_info.latency_info));
-  flush_info.latency_info.clear();
-  flush_info.flush_pending = false;
+      flush_info->route_id, flush_info->put_offset, flush_info->flush_count,
+      flush_info->latency_info));
+  flush_info->latency_info.clear();
+  flush_info->flush_pending = false;
+
+  flush_info->flushed_stream_flush_id = flush_info->flush_id;
 }
 
 scoped_ptr<CommandBufferProxyImpl> GpuChannelHost::CreateViewCommandBuffer(
@@ -290,8 +302,9 @@
   RemoveRoute(route_id);
 
   AutoLock lock(context_lock_);
-  if (stream_flush_info_[stream_id].route_id == route_id)
-    stream_flush_info_.erase(stream_id);
+  StreamFlushInfo& flush_info = stream_flush_info_[stream_id];
+  if (flush_info.flush_pending && flush_info.route_id == route_id)
+    flush_info.flush_pending = false;
 }
 
 void GpuChannelHost::DestroyChannel() {
@@ -384,6 +397,62 @@
   return next_stream_id_.GetNext();
 }
 
+uint32_t GpuChannelHost::ValidateFlushIDReachedServer(int32 stream_id) {
+  // Store what flush ids we will be validating for all streams.
+  base::hash_map<int32, uint32_t> validate_flushes;
+  uint32_t flushed_stream_flush_id = 0;
+  uint32_t verified_stream_flush_id = 0;
+  {
+    AutoLock lock(context_lock_);
+    for (const auto& iter : stream_flush_info_) {
+      const int32 iter_stream_id = iter.first;
+      const StreamFlushInfo& flush_info = iter.second;
+      if (iter_stream_id == stream_id) {
+        flushed_stream_flush_id = flush_info.flushed_stream_flush_id;
+        verified_stream_flush_id = flush_info.verified_stream_flush_id;
+      }
+
+      if (flush_info.flushed_stream_flush_id >
+          flush_info.verified_stream_flush_id) {
+        validate_flushes.insert(
+            std::make_pair(iter_stream_id, flush_info.flushed_stream_flush_id));
+      }
+    }
+  }
+
+  if (flushed_stream_flush_id == verified_stream_flush_id) {
+    // Current stream has no unverified flushes.
+    return verified_stream_flush_id;
+  }
+
+  if (Send(new GpuChannelMsg_Nop())) {
+    // Update verified flush id for all streams.
+    uint32_t highest_flush_id = 0;
+    AutoLock lock(context_lock_);
+    for (const auto& iter : validate_flushes) {
+      const int32 validated_stream_id = iter.first;
+      const uint32_t validated_flush_id = iter.second;
+      StreamFlushInfo& flush_info = stream_flush_info_[validated_stream_id];
+      if (flush_info.verified_stream_flush_id < validated_flush_id) {
+        flush_info.verified_stream_flush_id = validated_flush_id;
+      }
+
+      if (validated_stream_id == stream_id)
+        highest_flush_id = flush_info.verified_stream_flush_id;
+    }
+
+    return highest_flush_id;
+  }
+
+  return 0;
+}
+
+uint32_t GpuChannelHost::GetHighestValidatedFlushID(int32 stream_id) {
+  AutoLock lock(context_lock_);
+  StreamFlushInfo& flush_info = stream_flush_info_[stream_id];
+  return flush_info.verified_stream_flush_id;
+}
+
 GpuChannelHost::~GpuChannelHost() {
 #if DCHECK_IS_ON()
   AutoLock lock(context_lock_);
diff --git a/content/common/gpu/client/gpu_channel_host.h b/content/common/gpu/client/gpu_channel_host.h
index 46dd9343..a161a78 100644
--- a/content/common/gpu/client/gpu_channel_host.h
+++ b/content/common/gpu/client/gpu_channel_host.h
@@ -107,13 +107,14 @@
 
   // Set an ordering barrier.  AsyncFlushes any pending barriers on other
   // routes. Combines multiple OrderingBarriers into a single AsyncFlush.
-  void OrderingBarrier(int32 route_id,
-                       int32 stream_id,
-                       int32 put_offset,
-                       uint32 flush_count,
-                       const std::vector<ui::LatencyInfo>& latency_info,
-                       bool put_offset_changed,
-                       bool do_flush);
+  // Returns the flush ID for the stream or 0 if put offset was not changed.
+  uint32_t OrderingBarrier(int32 route_id,
+                           int32 stream_id,
+                           int32 put_offset,
+                           uint32 flush_count,
+                           const std::vector<ui::LatencyInfo>& latency_info,
+                           bool put_offset_changed,
+                           bool do_flush);
 
   // Create and connect to a command buffer in the GPU process.
   scoped_ptr<CommandBufferProxyImpl> CreateViewCommandBuffer(
@@ -181,6 +182,19 @@
   // Generate a stream ID guaranteed to be unique for this channel.
   int32 GenerateStreamID();
 
+  // Sends a synchronous nop to the server which validate that all previous IPC
+  // messages have been received. Once the synchronous nop has been sent to the
+  // server all previous flushes will all be marked as validated, including
+  // flushes for other streams on the same channel. Once a validation has been
+  // sent, it will return the highest validated flush id for the stream.
+  // If the validation fails (which can only happen upon context lost), the
+  // highest validated flush id will not change. If no flush ID were ever
+  // validated then it will return 0 (Note the lowest valid flush ID is 1).
+  uint32_t ValidateFlushIDReachedServer(int32 stream_id);
+
+  // Returns the highest validated flush ID for a given stream.
+  uint32_t GetHighestValidatedFlushID(int32 stream_id);
+
  private:
   friend class base::RefCountedThreadSafe<GpuChannelHost>;
 
@@ -234,10 +248,17 @@
     StreamFlushInfo();
     ~StreamFlushInfo();
 
+    // These are global per stream.
+    uint32_t next_stream_flush_id;
+    uint32_t flushed_stream_flush_id;
+    uint32_t verified_stream_flush_id;
+
+    // These are local per context.
     bool flush_pending;
     int32 route_id;
     int32 put_offset;
     uint32 flush_count;
+    uint32_t flush_id;
     std::vector<ui::LatencyInfo> latency_info;
   };
 
@@ -249,7 +270,7 @@
   void Connect(const IPC::ChannelHandle& channel_handle,
                base::WaitableEvent* shutdown_event);
   bool InternalSend(IPC::Message* msg);
-  void InternalFlush(int32 stream_id);
+  void InternalFlush(StreamFlushInfo* flush_info);
 
   // Threading notes: all fields are constant during the lifetime of |this|
   // except:
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl.cc b/content/common/gpu/client/gpu_memory_buffer_impl.cc
index 44c44a25..38107a5 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl.cc
@@ -5,10 +5,7 @@
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 
 #include "base/logging.h"
-#include "base/numerics/safe_math.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
-#include "ui/gfx/buffer_format_util.h"
-#include "ui/gl/gl_bindings.h"
 
 #if defined(OS_MACOSX)
 #include "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h"
@@ -50,7 +47,7 @@
   switch (handle.type) {
     case gfx::SHARED_MEMORY_BUFFER:
       return GpuMemoryBufferImplSharedMemory::CreateFromHandle(
-          handle, size, format, callback);
+          handle, size, format, usage, callback);
 #if defined(OS_MACOSX)
     case gfx::IO_SURFACE_BUFFER:
       return GpuMemoryBufferImplIOSurface::CreateFromHandle(
@@ -59,7 +56,7 @@
 #if defined(OS_ANDROID)
     case gfx::SURFACE_TEXTURE_BUFFER:
       return GpuMemoryBufferImplSurfaceTexture::CreateFromHandle(
-          handle, size, format, callback);
+          handle, size, format, usage, callback);
 #endif
 #if defined(USE_OZONE)
     case gfx::OZONE_NATIVE_PIXMAP:
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
index 7a3f044..13d2cc7 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
@@ -5,6 +5,7 @@
 #include "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h"
 
 #include "base/logging.h"
+#include "content/common/gpu/gpu_memory_buffer_factory_io_surface.h"
 #include "content/common/mac/io_surface_manager.h"
 #include "ui/gfx/buffer_format_util.h"
 
@@ -24,6 +25,10 @@
   return 0;
 }
 
+void FreeIOSurfaceForTesting(gfx::GpuMemoryBufferId id) {
+  IOSurfaceManager::GetInstance()->UnregisterIOSurface(id, 0);
+}
+
 }  // namespace
 
 GpuMemoryBufferImplIOSurface::GpuMemoryBufferImplIOSurface(
@@ -41,7 +46,8 @@
 }
 
 // static
-scoped_ptr<GpuMemoryBufferImpl> GpuMemoryBufferImplIOSurface::CreateFromHandle(
+scoped_ptr<GpuMemoryBufferImplIOSurface>
+GpuMemoryBufferImplIOSurface::CreateFromHandle(
     const gfx::GpuMemoryBufferHandle& handle,
     const gfx::Size& size,
     gfx::BufferFormat format,
@@ -52,11 +58,37 @@
   if (!io_surface)
     return nullptr;
 
-  return make_scoped_ptr<GpuMemoryBufferImpl>(
+  return make_scoped_ptr(
       new GpuMemoryBufferImplIOSurface(handle.id, size, format, callback,
                                        io_surface.release(), LockFlags(usage)));
 }
 
+// static
+bool GpuMemoryBufferImplIOSurface::IsConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  return GpuMemoryBufferFactoryIOSurface::
+      IsGpuMemoryBufferConfigurationSupported(format, usage);
+}
+
+// static
+base::Closure GpuMemoryBufferImplIOSurface::AllocateForTesting(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gfx::GpuMemoryBufferHandle* handle) {
+  base::ScopedCFTypeRef<IOSurfaceRef> io_surface(
+      GpuMemoryBufferFactoryIOSurface::CreateIOSurface(size, format));
+  DCHECK(io_surface);
+  gfx::GpuMemoryBufferId kBufferId(1);
+  bool rv = IOSurfaceManager::GetInstance()->RegisterIOSurface(kBufferId, 0,
+                                                               io_surface);
+  DCHECK(rv);
+  handle->type = gfx::IO_SURFACE_BUFFER;
+  handle->id = kBufferId;
+  return base::Bind(&FreeIOSurfaceForTesting, kBufferId);
+}
+
 bool GpuMemoryBufferImplIOSurface::Map(void** data) {
   DCHECK(!mapped_);
   IOReturn status = IOSurfaceLock(io_surface_, lock_flags_, NULL);
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
index d224938..d75c9d1e 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
@@ -8,20 +8,31 @@
 #include <IOSurface/IOSurface.h>
 
 #include "base/mac/scoped_cftyperef.h"
+#include "content/common/content_export.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 
 namespace content {
 
 // Implementation of GPU memory buffer based on IO surfaces.
-class GpuMemoryBufferImplIOSurface : public GpuMemoryBufferImpl {
+class CONTENT_EXPORT GpuMemoryBufferImplIOSurface : public GpuMemoryBufferImpl {
  public:
-  static scoped_ptr<GpuMemoryBufferImpl> CreateFromHandle(
+  ~GpuMemoryBufferImplIOSurface() override;
+
+  static scoped_ptr<GpuMemoryBufferImplIOSurface> CreateFromHandle(
       const gfx::GpuMemoryBufferHandle& handle,
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
       const DestructionCallback& callback);
 
+  static bool IsConfigurationSupported(gfx::BufferFormat format,
+                                       gfx::BufferUsage usage);
+
+  static base::Closure AllocateForTesting(const gfx::Size& size,
+                                          gfx::BufferFormat format,
+                                          gfx::BufferUsage usage,
+                                          gfx::GpuMemoryBufferHandle* handle);
+
   // Overridden from gfx::GpuMemoryBuffer:
   bool Map(void** data) override;
   void Unmap() override;
@@ -35,7 +46,6 @@
                                const DestructionCallback& callback,
                                IOSurfaceRef io_surface,
                                uint32_t lock_flags);
-  ~GpuMemoryBufferImplIOSurface() override;
 
   base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
   uint32_t lock_flags_;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc
new file mode 100644
index 0000000..83edce6
--- /dev/null
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h"
+#include "content/test/gpu_memory_buffer_impl_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplIOSurface,
+                              GpuMemoryBufferImplTest,
+                              GpuMemoryBufferImplIOSurface);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
index afeeb2ca4..8ebd1f8 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
@@ -4,10 +4,21 @@
 
 #include "content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h"
 
+#include "content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h"
 #include "ui/ozone/public/client_native_pixmap_factory.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 
 namespace content {
+namespace {
+
+void FreeNativePixmapForTesting(scoped_refptr<ui::NativePixmap> native_pixmap) {
+  // Nothing to do here. |native_pixmap| will be freed when this function
+  // returns and reference count drops to 0.
+}
+
+}  // namespace
 
 GpuMemoryBufferImplOzoneNativePixmap::GpuMemoryBufferImplOzoneNativePixmap(
     gfx::GpuMemoryBufferId id,
@@ -20,7 +31,7 @@
 GpuMemoryBufferImplOzoneNativePixmap::~GpuMemoryBufferImplOzoneNativePixmap() {}
 
 // static
-scoped_ptr<GpuMemoryBufferImpl>
+scoped_ptr<GpuMemoryBufferImplOzoneNativePixmap>
 GpuMemoryBufferImplOzoneNativePixmap::CreateFromHandle(
     const gfx::GpuMemoryBufferHandle& handle,
     const gfx::Size& size,
@@ -35,6 +46,29 @@
       handle.id, size, format, callback, native_pixmap.Pass()));
 }
 
+// static
+bool GpuMemoryBufferImplOzoneNativePixmap::IsConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  return GpuMemoryBufferFactoryOzoneNativePixmap::
+      IsGpuMemoryBufferConfigurationSupported(format, usage);
+}
+
+// static
+base::Closure GpuMemoryBufferImplOzoneNativePixmap::AllocateForTesting(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gfx::GpuMemoryBufferHandle* handle) {
+  scoped_refptr<ui::NativePixmap> pixmap =
+      ui::OzonePlatform::GetInstance()
+          ->GetSurfaceFactoryOzone()
+          ->CreateNativePixmap(gfx::kNullPluginWindow, size, format, usage);
+  handle->type = gfx::OZONE_NATIVE_PIXMAP;
+  handle->native_pixmap_handle = pixmap->ExportHandle();
+  return base::Bind(&FreeNativePixmapForTesting, pixmap);
+}
+
 bool GpuMemoryBufferImplOzoneNativePixmap::Map(void** data) {
   *data = pixmap_->Map();
   mapped_ = true;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h
index 74f1bfe..f129ddd 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_OZONE_NATIVE_PIXMAP_H_
 #define CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_OZONE_NATIVE_PIXMAP_H_
 
+#include "content/common/content_export.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 
 namespace ui {
@@ -14,17 +15,26 @@
 namespace content {
 
 // Implementation of GPU memory buffer based on Ozone native pixmap.
-class GpuMemoryBufferImplOzoneNativePixmap : public GpuMemoryBufferImpl {
+class CONTENT_EXPORT GpuMemoryBufferImplOzoneNativePixmap
+    : public GpuMemoryBufferImpl {
  public:
   ~GpuMemoryBufferImplOzoneNativePixmap() override;
 
-  static scoped_ptr<GpuMemoryBufferImpl> CreateFromHandle(
+  static scoped_ptr<GpuMemoryBufferImplOzoneNativePixmap> CreateFromHandle(
       const gfx::GpuMemoryBufferHandle& handle,
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
       const DestructionCallback& callback);
 
+  static bool IsConfigurationSupported(gfx::BufferFormat format,
+                                       gfx::BufferUsage usage);
+
+  static base::Closure AllocateForTesting(const gfx::Size& size,
+                                          gfx::BufferFormat format,
+                                          gfx::BufferUsage usage,
+                                          gfx::GpuMemoryBufferHandle* handle);
+
   // Overridden from gfx::GpuMemoryBuffer:
   bool Map(void** data) override;
   void Unmap() override;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc
new file mode 100644
index 0000000..08295c51
--- /dev/null
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h"
+#include "content/test/gpu_memory_buffer_impl_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplOzoneNativePixmap,
+                              GpuMemoryBufferImplTest,
+                              GpuMemoryBufferImplOzoneNativePixmap);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
index efa641b7..02437ff 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
@@ -11,6 +11,11 @@
 #include "ui/gl/gl_bindings.h"
 
 namespace content {
+namespace {
+
+void Noop() {}
+
+}  // namespace
 
 GpuMemoryBufferImplSharedMemory::GpuMemoryBufferImplSharedMemory(
     gfx::GpuMemoryBufferId id,
@@ -28,11 +33,11 @@
 }
 
 // static
-scoped_ptr<GpuMemoryBufferImpl> GpuMemoryBufferImplSharedMemory::Create(
-    gfx::GpuMemoryBufferId id,
-    const gfx::Size& size,
-    gfx::BufferFormat format,
-    const DestructionCallback& callback) {
+scoped_ptr<GpuMemoryBufferImplSharedMemory>
+GpuMemoryBufferImplSharedMemory::Create(gfx::GpuMemoryBufferId id,
+                                        const gfx::Size& size,
+                                        gfx::BufferFormat format,
+                                        const DestructionCallback& callback) {
   size_t buffer_size = 0u;
   if (!gfx::BufferSizeForBufferFormatChecked(size, format, &buffer_size))
     return nullptr;
@@ -68,11 +73,12 @@
 }
 
 // static
-scoped_ptr<GpuMemoryBufferImpl>
+scoped_ptr<GpuMemoryBufferImplSharedMemory>
 GpuMemoryBufferImplSharedMemory::CreateFromHandle(
     const gfx::GpuMemoryBufferHandle& handle,
     const gfx::Size& size,
     gfx::BufferFormat format,
+    gfx::BufferUsage usage,
     const DestructionCallback& callback) {
   if (!base::SharedMemory::IsHandleValid(handle.handle))
     return nullptr;
@@ -83,13 +89,8 @@
   if (!shared_memory->Map(buffer_size))
     base::TerminateBecauseOutOfMemory(buffer_size);
 
-  return make_scoped_ptr<GpuMemoryBufferImpl>(
-      new GpuMemoryBufferImplSharedMemory(
-          handle.id,
-          size,
-          format,
-          callback,
-          shared_memory.Pass()));
+  return make_scoped_ptr(new GpuMemoryBufferImplSharedMemory(
+      handle.id, size, format, callback, shared_memory.Pass()));
 }
 
 // static
@@ -131,6 +132,13 @@
 }
 
 // static
+bool GpuMemoryBufferImplSharedMemory::IsConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  return IsFormatSupported(format) && IsUsageSupported(usage);
+}
+
+// static
 bool GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(
     const gfx::Size& size,
     gfx::BufferFormat format) {
@@ -167,6 +175,21 @@
   return false;
 }
 
+// static
+base::Closure GpuMemoryBufferImplSharedMemory::AllocateForTesting(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gfx::GpuMemoryBufferHandle* handle) {
+  base::SharedMemory shared_memory;
+  bool rv = shared_memory.CreateAnonymous(
+      gfx::BufferSizeForBufferFormat(size, format));
+  DCHECK(rv);
+  handle->type = gfx::SHARED_MEMORY_BUFFER;
+  handle->handle = base::SharedMemory::DuplicateHandle(shared_memory.handle());
+  return base::Bind(&Noop);
+}
+
 bool GpuMemoryBufferImplSharedMemory::Map(void** data) {
   DCHECK(!mapped_);
   size_t offset = 0;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
index fedeae7..c3c39b9 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
@@ -5,16 +5,18 @@
 #ifndef CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_SHARED_MEMORY_H_
 #define CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_SHARED_MEMORY_H_
 
+#include "content/common/content_export.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 
 namespace content {
 
 // Implementation of GPU memory buffer based on shared memory.
-class GpuMemoryBufferImplSharedMemory : public GpuMemoryBufferImpl {
+class CONTENT_EXPORT GpuMemoryBufferImplSharedMemory
+    : public GpuMemoryBufferImpl {
  public:
   ~GpuMemoryBufferImplSharedMemory() override;
 
-  static scoped_ptr<GpuMemoryBufferImpl> Create(
+  static scoped_ptr<GpuMemoryBufferImplSharedMemory> Create(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
       gfx::BufferFormat format,
@@ -26,17 +28,25 @@
       gfx::BufferFormat format,
       base::ProcessHandle child_process);
 
-  static scoped_ptr<GpuMemoryBufferImpl> CreateFromHandle(
+  static scoped_ptr<GpuMemoryBufferImplSharedMemory> CreateFromHandle(
       const gfx::GpuMemoryBufferHandle& handle,
       const gfx::Size& size,
       gfx::BufferFormat format,
+      gfx::BufferUsage usage,
       const DestructionCallback& callback);
 
   static bool IsFormatSupported(gfx::BufferFormat format);
   static bool IsUsageSupported(gfx::BufferUsage usage);
+  static bool IsConfigurationSupported(gfx::BufferFormat format,
+                                       gfx::BufferUsage usage);
   static bool IsSizeValidForFormat(const gfx::Size& size,
                                    gfx::BufferFormat format);
 
+  static base::Closure AllocateForTesting(const gfx::Size& size,
+                                          gfx::BufferFormat format,
+                                          gfx::BufferUsage usage,
+                                          gfx::GpuMemoryBufferHandle* handle);
+
   // Overridden from gfx::GpuMemoryBuffer:
   bool Map(void** data) override;
   void Unmap() override;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
new file mode 100644
index 0000000..06262ce
--- /dev/null
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
@@ -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.
+
+#include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
+#include "content/test/gpu_memory_buffer_impl_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplSharedMemory,
+                              GpuMemoryBufferImplTest,
+                              GpuMemoryBufferImplSharedMemory);
+
+void BufferDestroyed(bool* destroyed, uint32 sync_point) {
+  *destroyed = true;
+}
+
+TEST(GpuMemoryBufferImplSharedMemoryTest, Create) {
+  const gfx::GpuMemoryBufferId kBufferId(1);
+
+  gfx::Size buffer_size(8, 8);
+
+  for (auto format : gfx::GetBufferFormats()) {
+    if (!GpuMemoryBufferImplSharedMemory::IsFormatSupported(format))
+      continue;
+
+    bool destroyed = false;
+    scoped_ptr<GpuMemoryBufferImplSharedMemory> buffer(
+        GpuMemoryBufferImplSharedMemory::Create(
+            kBufferId, buffer_size, format,
+            base::Bind(&BufferDestroyed, base::Unretained(&destroyed))));
+    ASSERT_TRUE(buffer);
+    EXPECT_EQ(buffer->GetFormat(), format);
+
+    // Check if destruction callback is executed when deleting the buffer.
+    buffer.reset();
+    ASSERT_TRUE(destroyed);
+  }
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
index b3471d1..6449f99e 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
@@ -7,7 +7,9 @@
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "content/common/android/surface_texture_manager.h"
+#include "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h"
 #include "ui/gfx/buffer_format_util.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace content {
@@ -37,6 +39,12 @@
   return 0;
 }
 
+void FreeSurfaceTextureForTesting(
+    scoped_refptr<gfx::SurfaceTexture> surface_texture,
+    gfx::GpuMemoryBufferId id) {
+  SurfaceTextureManager::GetInstance()->UnregisterSurfaceTexture(id.id, 0);
+}
+
 }  // namespace
 
 GpuMemoryBufferImplSurfaceTexture::GpuMemoryBufferImplSurfaceTexture(
@@ -54,24 +62,49 @@
 }
 
 // static
-scoped_ptr<GpuMemoryBufferImpl>
+scoped_ptr<GpuMemoryBufferImplSurfaceTexture>
 GpuMemoryBufferImplSurfaceTexture::CreateFromHandle(
     const gfx::GpuMemoryBufferHandle& handle,
     const gfx::Size& size,
     gfx::BufferFormat format,
+    gfx::BufferUsage usage,
     const DestructionCallback& callback) {
   ANativeWindow* native_window =
       SurfaceTextureManager::GetInstance()
           ->AcquireNativeWidgetForSurfaceTexture(handle.id.id);
   if (!native_window)
-    return scoped_ptr<GpuMemoryBufferImpl>();
+    return nullptr;
 
   ANativeWindow_setBuffersGeometry(
       native_window, size.width(), size.height(), WindowFormat(format));
 
-  return make_scoped_ptr<GpuMemoryBufferImpl>(
-      new GpuMemoryBufferImplSurfaceTexture(
-          handle.id, size, format, callback, native_window));
+  return make_scoped_ptr(new GpuMemoryBufferImplSurfaceTexture(
+      handle.id, size, format, callback, native_window));
+}
+
+// static
+bool GpuMemoryBufferImplSurfaceTexture::IsConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  return GpuMemoryBufferFactorySurfaceTexture::
+      IsGpuMemoryBufferConfigurationSupported(format, usage);
+}
+
+// static
+base::Closure GpuMemoryBufferImplSurfaceTexture::AllocateForTesting(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gfx::GpuMemoryBufferHandle* handle) {
+  scoped_refptr<gfx::SurfaceTexture> surface_texture =
+      gfx::SurfaceTexture::Create(0);
+  DCHECK(surface_texture);
+  gfx::GpuMemoryBufferId kBufferId(1);
+  SurfaceTextureManager::GetInstance()->RegisterSurfaceTexture(
+      kBufferId.id, 0, surface_texture.get());
+  handle->type = gfx::SURFACE_TEXTURE_BUFFER;
+  handle->id = kBufferId;
+  return base::Bind(&FreeSurfaceTextureForTesting, surface_texture, kBufferId);
 }
 
 bool GpuMemoryBufferImplSurfaceTexture::Map(void** data) {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
index 244d160b..959976f 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_SURFACE_TEXTURE_H_
 #define CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_SURFACE_TEXTURE_H_
 
+#include "content/common/content_export.h"
 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
 
 struct ANativeWindow;
@@ -12,14 +13,26 @@
 namespace content {
 
 // Implementation of GPU memory buffer based on SurfaceTextures.
-class GpuMemoryBufferImplSurfaceTexture : public GpuMemoryBufferImpl {
+class CONTENT_EXPORT GpuMemoryBufferImplSurfaceTexture
+    : public GpuMemoryBufferImpl {
  public:
-  static scoped_ptr<GpuMemoryBufferImpl> CreateFromHandle(
+  ~GpuMemoryBufferImplSurfaceTexture() override;
+
+  static scoped_ptr<GpuMemoryBufferImplSurfaceTexture> CreateFromHandle(
       const gfx::GpuMemoryBufferHandle& handle,
       const gfx::Size& size,
       gfx::BufferFormat format,
+      gfx::BufferUsage usage,
       const DestructionCallback& callback);
 
+  static bool IsConfigurationSupported(gfx::BufferFormat format,
+                                       gfx::BufferUsage usage);
+
+  static base::Closure AllocateForTesting(const gfx::Size& size,
+                                          gfx::BufferFormat format,
+                                          gfx::BufferUsage usage,
+                                          gfx::GpuMemoryBufferHandle* handle);
+
   // Overridden from gfx::GpuMemoryBuffer:
   bool Map(void** data) override;
   void Unmap() override;
@@ -32,7 +45,6 @@
                                     gfx::BufferFormat format,
                                     const DestructionCallback& callback,
                                     ANativeWindow* native_window);
-  ~GpuMemoryBufferImplSurfaceTexture() override;
 
   ANativeWindow* native_window_;
   size_t stride_;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc
new file mode 100644
index 0000000..1cd513d
--- /dev/null
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h"
+#include "content/test/gpu_memory_buffer_impl_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplSurfaceTexture,
+                              GpuMemoryBufferImplTest,
+                              GpuMemoryBufferImplSurfaceTexture);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc
deleted file mode 100644
index 587c024..0000000
--- a/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc
+++ /dev/null
@@ -1,240 +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 "content/common/gpu/client/gpu_memory_buffer_impl.h"
-
-#include "base/bind.h"
-#include "content/common/gpu/gpu_memory_buffer_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/buffer_format_util.h"
-
-namespace content {
-namespace {
-
-const int kClientId = 1;
-
-class GpuMemoryBufferImplTest
-    : public testing::TestWithParam<gfx::GpuMemoryBufferType> {
- public:
-  GpuMemoryBufferImplTest() : buffer_count_(0), factory_(nullptr) {}
-
-  // Overridden from testing::Test:
-  void SetUp() override {
-    factory_ = GpuMemoryBufferFactory::Create(GetParam());
-    factory_->GetSupportedGpuMemoryBufferConfigurations(
-        &supported_configurations_);
-  }
-  void TearDown() override { factory_.reset(); }
-
-  gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                                                   const gfx::Size& size,
-                                                   gfx::BufferFormat format,
-                                                   gfx::BufferUsage usage) {
-    ++buffer_count_;
-    return factory_->CreateGpuMemoryBuffer(id, size, format, usage, kClientId,
-                                           gfx::kNullPluginWindow);
-  }
-
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, uint32 sync_point) {
-    factory_->DestroyGpuMemoryBuffer(id, kClientId);
-    DCHECK_GT(buffer_count_, 0);
-    --buffer_count_;
-  }
-
-  std::vector<GpuMemoryBufferFactory::Configuration> supported_configurations_;
-  int buffer_count_;
-
- private:
-  scoped_ptr<GpuMemoryBufferFactory> factory_;
-};
-
-TEST_P(GpuMemoryBufferImplTest, CreateFromHandle) {
-  const gfx::GpuMemoryBufferId kBufferId(1);
-
-  gfx::Size buffer_size(8, 8);
-
-  for (auto configuration : supported_configurations_) {
-    scoped_ptr<GpuMemoryBufferImpl> buffer(
-        GpuMemoryBufferImpl::CreateFromHandle(
-            CreateGpuMemoryBuffer(kBufferId, buffer_size, configuration.format,
-                                  configuration.usage),
-            buffer_size, configuration.format, configuration.usage,
-            base::Bind(&GpuMemoryBufferImplTest::DestroyGpuMemoryBuffer,
-                       base::Unretained(this), kBufferId)));
-    EXPECT_EQ(1, buffer_count_);
-    ASSERT_TRUE(buffer);
-    EXPECT_EQ(buffer->GetFormat(), configuration.format);
-
-    // Check if destruction callback is executed when deleting the buffer.
-    buffer.reset();
-    EXPECT_EQ(0, buffer_count_);
-  }
-}
-
-TEST_P(GpuMemoryBufferImplTest, Map) {
-  const gfx::GpuMemoryBufferId kBufferId(1);
-
-  // Use a multiple of 4 for both dimensions to support compressed formats.
-  gfx::Size buffer_size(4, 4);
-
-  for (auto configuration : supported_configurations_) {
-    if (configuration.usage != gfx::BufferUsage::MAP)
-      continue;
-
-    scoped_ptr<GpuMemoryBufferImpl> buffer(
-        GpuMemoryBufferImpl::CreateFromHandle(
-            CreateGpuMemoryBuffer(kBufferId, buffer_size, configuration.format,
-                                  configuration.usage),
-            buffer_size, configuration.format, configuration.usage,
-            base::Bind(&GpuMemoryBufferImplTest::DestroyGpuMemoryBuffer,
-                       base::Unretained(this), kBufferId)));
-    ASSERT_TRUE(buffer);
-    EXPECT_FALSE(buffer->IsMapped());
-
-    size_t num_planes =
-        gfx::NumberOfPlanesForBufferFormat(configuration.format);
-
-    // Map buffer into user space.
-    scoped_ptr<void*[]> mapped_buffers(new void*[num_planes]);
-    bool rv = buffer->Map(mapped_buffers.get());
-    ASSERT_TRUE(rv);
-    EXPECT_TRUE(buffer->IsMapped());
-
-    // Get strides.
-    scoped_ptr<int[]> strides(new int[num_planes]);
-    buffer->GetStride(strides.get());
-
-    // Copy and compare mapped buffers.
-    for (size_t plane = 0; plane < num_planes; ++plane) {
-      size_t row_size_in_bytes = 0;
-      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
-          buffer_size.width(), configuration.format, plane,
-          &row_size_in_bytes));
-      EXPECT_GT(row_size_in_bytes, 0u);
-
-      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
-      memset(data.get(), 0x2a + plane, row_size_in_bytes);
-
-      size_t height =
-          buffer_size.height() /
-          gfx::SubsamplingFactorForBufferFormat(configuration.format, plane);
-      for (size_t y = 0; y < height; ++y) {
-        memcpy(static_cast<char*>(mapped_buffers[plane]) + y * strides[plane],
-               data.get(), row_size_in_bytes);
-        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
-                             y * strides[plane],
-                         data.get(), row_size_in_bytes),
-                  0);
-      }
-    }
-
-    buffer->Unmap();
-    EXPECT_FALSE(buffer->IsMapped());
-  }
-}
-
-TEST_P(GpuMemoryBufferImplTest, PersistentMap) {
-  const gfx::GpuMemoryBufferId kBufferId(1);
-
-  // Use a multiple of 4 for both dimensions to support compressed formats.
-  gfx::Size buffer_size(4, 4);
-
-  for (auto configuration : supported_configurations_) {
-    if (configuration.usage != gfx::BufferUsage::PERSISTENT_MAP)
-      continue;
-
-    scoped_ptr<GpuMemoryBufferImpl> buffer(
-        GpuMemoryBufferImpl::CreateFromHandle(
-            CreateGpuMemoryBuffer(kBufferId, buffer_size, configuration.format,
-                                  configuration.usage),
-            buffer_size, configuration.format, configuration.usage,
-            base::Bind(&GpuMemoryBufferImplTest::DestroyGpuMemoryBuffer,
-                       base::Unretained(this), kBufferId)));
-    ASSERT_TRUE(buffer);
-    EXPECT_FALSE(buffer->IsMapped());
-
-    size_t num_planes =
-        gfx::NumberOfPlanesForBufferFormat(configuration.format);
-
-    // Map buffer into user space.
-    scoped_ptr<void* []> mapped_buffers(new void* [num_planes]);
-    bool rv = buffer->Map(mapped_buffers.get());
-    ASSERT_TRUE(rv);
-    EXPECT_TRUE(buffer->IsMapped());
-
-    // Get strides.
-    scoped_ptr<int[]> strides(new int[num_planes]);
-    buffer->GetStride(strides.get());
-
-    // Copy and compare mapped buffers.
-    for (size_t plane = 0; plane < num_planes; ++plane) {
-      size_t row_size_in_bytes;
-      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
-          buffer_size.width(), configuration.format, plane,
-          &row_size_in_bytes));
-
-      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
-      memset(data.get(), 0x2a + plane, row_size_in_bytes);
-
-      size_t height =
-          buffer_size.height() /
-          gfx::SubsamplingFactorForBufferFormat(configuration.format, plane);
-      for (size_t y = 0; y < height; ++y) {
-        memcpy(static_cast<char*>(mapped_buffers[plane]) + y * strides[plane],
-               data.get(), row_size_in_bytes);
-        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
-                             y * strides[plane],
-                         data.get(), row_size_in_bytes),
-                  0);
-      }
-    }
-
-    buffer->Unmap();
-    EXPECT_FALSE(buffer->IsMapped());
-
-    // Remap the buffer, and compare again. It should contain the same data.
-    rv = buffer->Map(mapped_buffers.get());
-    ASSERT_TRUE(rv);
-    EXPECT_TRUE(buffer->IsMapped());
-
-    buffer->GetStride(strides.get());
-
-    for (size_t plane = 0; plane < num_planes; ++plane) {
-      size_t row_size_in_bytes;
-      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
-          buffer_size.width(), configuration.format, plane,
-          &row_size_in_bytes));
-
-      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
-      memset(data.get(), 0x2a + plane, row_size_in_bytes);
-
-      size_t height =
-          buffer_size.height() /
-          gfx::SubsamplingFactorForBufferFormat(configuration.format, plane);
-      for (size_t y = 0; y < height; ++y) {
-        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
-                             y * strides[plane],
-                         data.get(), row_size_in_bytes),
-                  0);
-      }
-    }
-
-    buffer->Unmap();
-    EXPECT_FALSE(buffer->IsMapped());
-  }
-}
-
-std::vector<gfx::GpuMemoryBufferType> GetSupportedGpuMemoryBufferTypes() {
-  std::vector<gfx::GpuMemoryBufferType> supported_types;
-  GpuMemoryBufferFactory::GetSupportedTypes(&supported_types);
-  return supported_types;
-}
-
-INSTANTIATE_TEST_CASE_P(
-    GpuMemoryBufferImplTests,
-    GpuMemoryBufferImplTest,
-    ::testing::ValuesIn(GetSupportedGpuMemoryBufferTypes()));
-
-}  // namespace
-}  // namespace content
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 4a43a752..acff5d4 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -76,16 +76,16 @@
   return new GpuChannelMessageQueue(gpu_channel, task_runner);
 }
 
-scoped_refptr<gpu::SyncPointClientState>
-    GpuChannelMessageQueue::GetSyncPointClientState() {
-  return sync_point_client_state_;
+scoped_refptr<gpu::SyncPointOrderData>
+GpuChannelMessageQueue::GetSyncPointOrderData() {
+  return sync_point_order_data_;
 }
 
 GpuChannelMessageQueue::GpuChannelMessageQueue(
     const base::WeakPtr<GpuChannel>& gpu_channel,
     base::SingleThreadTaskRunner* task_runner)
     : enabled_(true),
-      sync_point_client_state_(gpu::SyncPointClientState::Create()),
+      sync_point_order_data_(gpu::SyncPointOrderData::Create()),
       gpu_channel_(gpu_channel),
       task_runner_(task_runner) {}
 
@@ -94,11 +94,11 @@
 }
 
 uint32_t GpuChannelMessageQueue::GetUnprocessedOrderNum() const {
-  return sync_point_client_state_->unprocessed_order_num();
+  return sync_point_order_data_->unprocessed_order_num();
 }
 
 uint32_t GpuChannelMessageQueue::GetProcessedOrderNum() const {
-  return sync_point_client_state_->processed_order_num();
+  return sync_point_order_data_->processed_order_num();
 }
 
 void GpuChannelMessageQueue::PushBackMessage(
@@ -147,9 +147,9 @@
   base::AutoLock auto_lock(channel_messages_lock_);
   if (!channel_messages_.empty()) {
     DCHECK_GT(channel_messages_.front()->order_number,
-              sync_point_client_state_->processed_order_num());
+              sync_point_order_data_->processed_order_num());
     DCHECK_LE(channel_messages_.front()->order_number,
-              sync_point_client_state_->unprocessed_order_num());
+              sync_point_order_data_->unprocessed_order_num());
 
     return channel_messages_.front();
   }
@@ -158,7 +158,7 @@
 
 void GpuChannelMessageQueue::BeginMessageProcessing(
     const GpuChannelMessage* msg) {
-  sync_point_client_state_->BeginProcessingOrderNumber(msg->order_number);
+  sync_point_order_data_->BeginProcessingOrderNumber(msg->order_number);
 }
 
 bool GpuChannelMessageQueue::MessageProcessed() {
@@ -166,7 +166,7 @@
   DCHECK(!channel_messages_.empty());
   scoped_ptr<GpuChannelMessage> msg(channel_messages_.front());
   channel_messages_.pop_front();
-  sync_point_client_state_->FinishProcessingOrderNumber(msg->order_number);
+  sync_point_order_data_->FinishProcessingOrderNumber(msg->order_number);
   return !channel_messages_.empty();
 }
 
@@ -193,6 +193,11 @@
           msg->sync_point);
     }
   }
+
+  if (sync_point_order_data_) {
+    sync_point_order_data_->Destroy();
+    sync_point_order_data_ = nullptr;
+  }
 }
 
 void GpuChannelMessageQueue::ScheduleHandleMessage() {
@@ -206,9 +211,8 @@
   channel_messages_lock_.AssertAcquired();
   DCHECK(enabled_);
 
-  msg->order_number =
-      sync_point_client_state_->GenerateUnprocessedOrderNumber(
-          sync_point_manager);
+  msg->order_number = sync_point_order_data_->GenerateUnprocessedOrderNumber(
+      sync_point_manager);
   msg->time_received = base::TimeTicks::Now();
 
   bool had_messages = !channel_messages_.empty();
@@ -301,6 +305,12 @@
     return true;
   }
 
+  if (message.type() == GpuChannelMsg_Nop::ID) {
+    IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
+    Send(reply);
+    return true;
+  }
+
   for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) {
     if (filter->OnMessageReceived(message)) {
       return true;
@@ -788,8 +798,8 @@
   return handled;
 }
 
-scoped_refptr<gpu::SyncPointClientState> GpuChannel::GetSyncPointClientState() {
-  return message_queue_->GetSyncPointClientState();
+scoped_refptr<gpu::SyncPointOrderData> GpuChannel::GetSyncPointOrderData() {
+  return message_queue_->GetSyncPointOrderData();
 }
 
 void GpuChannel::HandleMessage() {
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index 69c49eb..53b706b 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -36,7 +36,7 @@
 
 namespace gpu {
 class PreemptionFlag;
-class SyncPointClientState;
+class SyncPointOrderData;
 class SyncPointManager;
 union ValueState;
 class ValueStateMap;
@@ -172,8 +172,8 @@
   // Returns the global order number for the last unprocessed IPC message.
   uint32_t GetUnprocessedOrderNum() const;
 
-  // Returns the shared sync point client state.
-  scoped_refptr<gpu::SyncPointClientState> GetSyncPointClientState();
+  // Returns the shared sync point global order data.
+  scoped_refptr<gpu::SyncPointOrderData> GetSyncPointOrderData();
 
   void HandleMessage();
 
@@ -410,7 +410,7 @@
       const base::WeakPtr<GpuChannel>& gpu_channel,
       base::SingleThreadTaskRunner* task_runner);
 
-  scoped_refptr<gpu::SyncPointClientState> GetSyncPointClientState();
+  scoped_refptr<gpu::SyncPointOrderData> GetSyncPointOrderData();
 
   // Returns the global order number for the last unprocessed IPC message.
   uint32_t GetUnprocessedOrderNum() const;
@@ -461,7 +461,7 @@
   mutable base::Lock channel_messages_lock_;
 
   // Keeps track of sync point related state such as message order numbers.
-  scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
+  scoped_refptr<gpu::SyncPointOrderData> sync_point_order_data_;
 
   base::WeakPtr<GpuChannel> gpu_channel_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 6f49445..381f354a 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -536,11 +536,9 @@
   scheduler_.reset(new gpu::GpuScheduler(command_buffer_.get(),
                                          decoder_.get(),
                                          decoder_.get()));
-  sync_point_client_ =
-      sync_point_manager->CreateSyncPointClient(
-          channel_->GetSyncPointClientState(),
-          gpu::CommandBufferNamespace::GPU_IO,
-          command_buffer_id_);
+  sync_point_client_ = sync_point_manager->CreateSyncPointClient(
+      channel_->GetSyncPointOrderData(), gpu::CommandBufferNamespace::GPU_IO,
+      command_buffer_id_);
 
   if (preemption_flag_.get())
     scheduler_->SetPreemptByFlag(preemption_flag_);
@@ -645,6 +643,10 @@
   decoder_->SetWaitSyncPointCallback(
       base::Bind(&GpuCommandBufferStub::OnWaitSyncPoint,
                  base::Unretained(this)));
+  decoder_->SetFenceSyncReleaseCallback(base::Bind(
+      &GpuCommandBufferStub::OnFenceSyncRelease, base::Unretained(this)));
+  decoder_->SetWaitFenceSyncCallback(base::Bind(
+      &GpuCommandBufferStub::OnWaitFenceSync, base::Unretained(this)));
 
   command_buffer_->SetPutOffsetChangeCallback(
       base::Bind(&GpuCommandBufferStub::PutChanged, base::Unretained(this)));
@@ -933,8 +935,15 @@
 
   gpu::gles2::MailboxManager* mailbox_manager =
       context_group_->mailbox_manager();
-  if (mailbox_manager->UsesSync() && MakeCurrent())
-    mailbox_manager->PushTextureUpdates(sync_point);
+  if (mailbox_manager->UsesSync() && MakeCurrent()) {
+    // Old sync points are global and do not have a command buffer ID,
+    // We can simply use the global sync point number as the release count with
+    // 0 for the command buffer ID (under normal circumstances 0 is invalid so
+    // will not be used) until the old sync points are replaced.
+    gpu::gles2::SyncToken sync_token = {gpu::CommandBufferNamespace::GPU_IO, 0,
+                                        sync_point};
+    mailbox_manager->PushTextureUpdates(sync_token);
+  }
 
   GpuChannelManager* manager = channel_->gpu_channel_manager();
   manager->sync_point_manager()->RetireSyncPoint(sync_point);
@@ -947,7 +956,11 @@
     return true;
   GpuChannelManager* manager = channel_->gpu_channel_manager();
   if (manager->sync_point_manager()->IsSyncPointRetired(sync_point)) {
-    PullTextureUpdates(sync_point);
+    // Old sync points are global and do not have a command buffer ID,
+    // We can simply use the global sync point number as the release count with
+    // 0 for the command buffer ID (under normal circumstances 0 is invalid so
+    // will not be used) until the old sync points are replaced.
+    PullTextureUpdates(gpu::CommandBufferNamespace::GPU_IO, 0, sync_point);
     return true;
   }
 
@@ -969,16 +982,26 @@
   DCHECK(!scheduler_->scheduled());
   TRACE_EVENT_ASYNC_END1("gpu", "WaitSyncPoint", this, "GpuCommandBufferStub",
                          this);
-  PullTextureUpdates(sync_point);
+  // Old sync points are global and do not have a command buffer ID,
+  // We can simply use the global sync point number as the release count with
+  // 0 for the command buffer ID (under normal circumstances 0 is invalid so
+  // will not be used) until the old sync points are replaced.
+  PullTextureUpdates(gpu::CommandBufferNamespace::GPU_IO, 0, sync_point);
   waiting_for_sync_point_ = false;
   scheduler_->SetScheduled(true);
 }
 
-void GpuCommandBufferStub::PullTextureUpdates(uint32 sync_point) {
+void GpuCommandBufferStub::PullTextureUpdates(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint32_t release) {
   gpu::gles2::MailboxManager* mailbox_manager =
       context_group_->mailbox_manager();
-  if (mailbox_manager->UsesSync() && MakeCurrent())
-    mailbox_manager->PullTextureUpdates(sync_point);
+  if (mailbox_manager->UsesSync() && MakeCurrent()) {
+    gpu::gles2::SyncToken sync_token = {namespace_id, command_buffer_id,
+                                        release};
+    mailbox_manager->PullTextureUpdates(sync_token);
+  }
 }
 
 void GpuCommandBufferStub::OnSignalSyncPoint(uint32 sync_point, uint32 id) {
@@ -1013,6 +1036,73 @@
   OnSignalSyncPointAck(id);
 }
 
+void GpuCommandBufferStub::OnFenceSyncRelease(uint64_t release) {
+  if (sync_point_client_->client_state()->IsFenceSyncReleased(release)) {
+    DLOG(ERROR) << "Fence Sync has already been released.";
+    return;
+  }
+
+  gpu::gles2::MailboxManager* mailbox_manager =
+      context_group_->mailbox_manager();
+  if (mailbox_manager->UsesSync() && MakeCurrent()) {
+    gpu::gles2::SyncToken sync_token = {gpu::CommandBufferNamespace::GPU_IO,
+                                        command_buffer_id_, release};
+    mailbox_manager->PushTextureUpdates(sync_token);
+  }
+
+  sync_point_client_->ReleaseFenceSync(release);
+}
+
+bool GpuCommandBufferStub::OnWaitFenceSync(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint64_t release) {
+  DCHECK(!waiting_for_sync_point_);
+  DCHECK(scheduler_->scheduled());
+
+  GpuChannelManager* manager = channel_->gpu_channel_manager();
+  DCHECK(manager);
+
+  gpu::SyncPointManager* sync_point_manager = manager->sync_point_manager();
+  DCHECK(sync_point_manager);
+
+  scoped_refptr<gpu::SyncPointClientState> release_state =
+      sync_point_manager->GetSyncPointClientState(namespace_id,
+                                                  command_buffer_id);
+
+  if (!release_state)
+    return true;
+
+  if (release_state->IsFenceSyncReleased(release)) {
+    PullTextureUpdates(namespace_id, command_buffer_id, release);
+    return true;
+  }
+
+  TRACE_EVENT_ASYNC_BEGIN1("gpu", "WaitFenceSync", this, "GpuCommandBufferStub",
+                           this);
+  scheduler_->SetScheduled(false);
+  waiting_for_sync_point_ = true;
+  sync_point_client_->WaitNonThreadSafe(
+      release_state.get(), release, task_runner_,
+      base::Bind(&GpuCommandBufferStub::OnWaitFenceSyncCompleted,
+                 this->AsWeakPtr(), namespace_id, command_buffer_id, release));
+
+  return scheduler_->scheduled();
+}
+
+void GpuCommandBufferStub::OnWaitFenceSyncCompleted(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint64_t release) {
+  DCHECK(waiting_for_sync_point_);
+  DCHECK(!scheduler_->scheduled());
+  TRACE_EVENT_ASYNC_END1("gpu", "WaitFenceSync", this, "GpuCommandBufferStub",
+                         this);
+  PullTextureUpdates(namespace_id, command_buffer_id, release);
+  waiting_for_sync_point_ = false;
+  scheduler_->SetScheduled(true);
+}
+
 void GpuCommandBufferStub::OnCreateImage(int32 id,
                                          gfx::GpuMemoryBufferHandle handle,
                                          gfx::Size size,
diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h
index 8e854df..b139a78 100644
--- a/content/common/gpu/gpu_command_buffer_stub.h
+++ b/content/common/gpu/gpu_command_buffer_stub.h
@@ -202,6 +202,14 @@
   void OnSignalSyncPointAck(uint32 id);
   void OnSignalQuery(uint32 query, uint32 id);
 
+  void OnFenceSyncRelease(uint64_t release);
+  bool OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+                       uint64_t command_buffer_id,
+                       uint64_t release);
+  void OnWaitFenceSyncCompleted(gpu::CommandBufferNamespace namespace_id,
+                                uint64_t command_buffer_id,
+                                uint64_t release);
+
   void OnCreateImage(int32 id,
                      gfx::GpuMemoryBufferHandle handle,
                      gfx::Size size,
@@ -233,7 +241,9 @@
 
   bool CheckContextLost();
   void CheckCompleteWaits();
-  void PullTextureUpdates(uint32 sync_point);
+  void PullTextureUpdates(gpu::CommandBufferNamespace namespace_id,
+                          uint64_t command_buffer_id,
+                          uint32_t release);
 
   // The lifetime of objects of this class is managed by a GpuChannel. The
   // GpuChannels destroy all the GpuCommandBufferStubs that they own when they
diff --git a/content/common/gpu/gpu_memory_buffer_factory.cc b/content/common/gpu/gpu_memory_buffer_factory.cc
index 2c5bd39..31e2dbc 100644
--- a/content/common/gpu/gpu_memory_buffer_factory.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory.cc
@@ -5,7 +5,6 @@
 #include "content/common/gpu/gpu_memory_buffer_factory.h"
 
 #include "base/logging.h"
-#include "content/common/gpu/gpu_memory_buffer_factory_shared_memory.h"
 
 #if defined(OS_MACOSX)
 #include "content/common/gpu/gpu_memory_buffer_factory_io_surface.h"
@@ -22,45 +21,32 @@
 namespace content {
 
 // static
-void GpuMemoryBufferFactory::GetSupportedTypes(
-    std::vector<gfx::GpuMemoryBufferType>* types) {
-  const gfx::GpuMemoryBufferType supported_types[] = {
+gfx::GpuMemoryBufferType GpuMemoryBufferFactory::GetNativeType() {
 #if defined(OS_MACOSX)
-    gfx::IO_SURFACE_BUFFER,
+  return gfx::IO_SURFACE_BUFFER;
 #endif
 #if defined(OS_ANDROID)
-    gfx::SURFACE_TEXTURE_BUFFER,
+  return gfx::SURFACE_TEXTURE_BUFFER;
 #endif
 #if defined(USE_OZONE)
-    gfx::OZONE_NATIVE_PIXMAP,
+  return gfx::OZONE_NATIVE_PIXMAP;
 #endif
-    gfx::SHARED_MEMORY_BUFFER
-  };
-  types->assign(supported_types, supported_types + arraysize(supported_types));
+  return gfx::EMPTY_BUFFER;
 }
 
 // static
-scoped_ptr<GpuMemoryBufferFactory> GpuMemoryBufferFactory::Create(
-    gfx::GpuMemoryBufferType type) {
-  switch (type) {
-    case gfx::SHARED_MEMORY_BUFFER:
-      return make_scoped_ptr(new GpuMemoryBufferFactorySharedMemory);
+scoped_ptr<GpuMemoryBufferFactory> GpuMemoryBufferFactory::CreateNativeType() {
 #if defined(OS_MACOSX)
-    case gfx::IO_SURFACE_BUFFER:
-      return make_scoped_ptr(new GpuMemoryBufferFactoryIOSurface);
+  return make_scoped_ptr(new GpuMemoryBufferFactoryIOSurface);
 #endif
 #if defined(OS_ANDROID)
-    case gfx::SURFACE_TEXTURE_BUFFER:
-      return make_scoped_ptr(new GpuMemoryBufferFactorySurfaceTexture);
+  return make_scoped_ptr(new GpuMemoryBufferFactorySurfaceTexture);
 #endif
 #if defined(USE_OZONE)
-    case gfx::OZONE_NATIVE_PIXMAP:
-      return make_scoped_ptr(new GpuMemoryBufferFactoryOzoneNativePixmap);
+  return make_scoped_ptr(new GpuMemoryBufferFactoryOzoneNativePixmap);
 #endif
-    default:
-      NOTREACHED();
-      return scoped_ptr<GpuMemoryBufferFactory>();
-  }
+  NOTREACHED();
+  return nullptr;
 }
 
 }  // namespace content
diff --git a/content/common/gpu/gpu_memory_buffer_factory.h b/content/common/gpu/gpu_memory_buffer_factory.h
index 1269c96..7edb0e4 100644
--- a/content/common/gpu/gpu_memory_buffer_factory.h
+++ b/content/common/gpu/gpu_memory_buffer_factory.h
@@ -26,24 +26,14 @@
 
 class CONTENT_EXPORT GpuMemoryBufferFactory {
  public:
-  struct Configuration {
-    gfx::BufferFormat format;
-    gfx::BufferUsage usage;
-  };
-
   virtual ~GpuMemoryBufferFactory() {}
 
-  // Gets system supported GPU memory buffer factory types. Preferred type at
-  // the front of vector.
-  static void GetSupportedTypes(std::vector<gfx::GpuMemoryBufferType>* types);
+  // Returns the native GPU memory buffer factory type. Returns EMPTY_BUFFER
+  // type if native buffers are not supported.
+  static gfx::GpuMemoryBufferType GetNativeType();
 
-  // Creates a new factory instance for |type|.
-  static scoped_ptr<GpuMemoryBufferFactory> Create(
-      gfx::GpuMemoryBufferType type);
-
-  // Gets supported format/usage configurations.
-  virtual void GetSupportedGpuMemoryBufferConfigurations(
-      std::vector<Configuration>* configurations) = 0;
+  // Creates a new factory instance for native GPU memory buffers.
+  static scoped_ptr<GpuMemoryBufferFactory> CreateNativeType();
 
   // Creates a new GPU memory buffer instance. A valid handle is returned on
   // success. It can be called on any thread.
diff --git a/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc b/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
index a6ce59f..a6b41f3 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc
@@ -83,18 +83,6 @@
   return 0;
 }
 
-const GpuMemoryBufferFactory::Configuration kSupportedConfigurations[] = {
-    {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT},
-    {gfx::BufferFormat::R_8, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::R_8, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::UYVY_422, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::UYVY_422, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::PERSISTENT_MAP},
-};
-
 }  // namespace
 
 GpuMemoryBufferFactoryIOSurface::GpuMemoryBufferFactoryIOSurface() {
@@ -107,29 +95,24 @@
 bool GpuMemoryBufferFactoryIOSurface::IsGpuMemoryBufferConfigurationSupported(
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
-  for (auto& configuration : kSupportedConfigurations) {
-    if (configuration.format == format && configuration.usage == usage)
-      return true;
+  switch (usage) {
+    case gfx::BufferUsage::SCANOUT:
+      return format == gfx::BufferFormat::BGRA_8888;
+    case gfx::BufferUsage::MAP:
+    case gfx::BufferUsage::PERSISTENT_MAP:
+      return format == gfx::BufferFormat::R_8 ||
+             format == gfx::BufferFormat::BGRA_8888 ||
+             format == gfx::BufferFormat::UYVY_422 ||
+             format == gfx::BufferFormat::YUV_420_BIPLANAR;
   }
-
+  NOTREACHED();
   return false;
 }
 
-void GpuMemoryBufferFactoryIOSurface::GetSupportedGpuMemoryBufferConfigurations(
-    std::vector<Configuration>* configurations) {
-  configurations->assign(
-      kSupportedConfigurations,
-      kSupportedConfigurations + arraysize(kSupportedConfigurations));
-}
-
-gfx::GpuMemoryBufferHandle
-GpuMemoryBufferFactoryIOSurface::CreateGpuMemoryBuffer(
-    gfx::GpuMemoryBufferId id,
+// static
+IOSurfaceRef GpuMemoryBufferFactoryIOSurface::CreateIOSurface(
     const gfx::Size& size,
-    gfx::BufferFormat format,
-    gfx::BufferUsage usage,
-    int client_id,
-    gfx::PluginWindowHandle surface_handle) {
+    gfx::BufferFormat format) {
   size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
   base::ScopedCFTypeRef<CFMutableArrayRef> planes(CFArrayCreateMutable(
       kCFAllocatorDefault, num_planes, &kCFTypeArrayCallBacks));
@@ -158,7 +141,18 @@
   AddIntegerValue(properties, kIOSurfacePixelFormat, PixelFormat(format));
   CFDictionaryAddValue(properties, kIOSurfacePlaneInfo, planes);
 
-  base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceCreate(properties));
+  return IOSurfaceCreate(properties);
+}
+
+gfx::GpuMemoryBufferHandle
+GpuMemoryBufferFactoryIOSurface::CreateGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id,
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    int client_id,
+    gfx::PluginWindowHandle surface_handle) {
+  base::ScopedCFTypeRef<IOSurfaceRef> io_surface(CreateIOSurface(size, format));
   if (!io_surface)
     return gfx::GpuMemoryBufferHandle();
 
diff --git a/content/common/gpu/gpu_memory_buffer_factory_io_surface.h b/content/common/gpu/gpu_memory_buffer_factory_io_surface.h
index 77a2e7f..1cafa79 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_io_surface.h
+++ b/content/common/gpu/gpu_memory_buffer_factory_io_surface.h
@@ -5,12 +5,15 @@
 #ifndef CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_IO_SURFACE_H_
 #define CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_IO_SURFACE_H_
 
+#include <utility>
+
 #include <IOSurface/IOSurface.h>
 
 #include "base/containers/hash_tables.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
 #include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "content/common/mac/io_surface_manager.h"
 #include "gpu/command_buffer/service/image_factory.h"
@@ -23,8 +26,9 @@
 
 namespace content {
 
-class GpuMemoryBufferFactoryIOSurface : public GpuMemoryBufferFactory,
-                                        public gpu::ImageFactory {
+class CONTENT_EXPORT GpuMemoryBufferFactoryIOSurface
+    : public GpuMemoryBufferFactory,
+      public gpu::ImageFactory {
  public:
   GpuMemoryBufferFactoryIOSurface();
   ~GpuMemoryBufferFactoryIOSurface() override;
@@ -32,9 +36,10 @@
   static bool IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
                                                       gfx::BufferUsage usage);
 
+  static IOSurfaceRef CreateIOSurface(const gfx::Size& size,
+                                      gfx::BufferFormat format);
+
   // Overridden from GpuMemoryBufferFactory:
-  void GetSupportedGpuMemoryBufferConfigurations(
-      std::vector<Configuration>* configurations) override;
   gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
diff --git a/content/common/gpu/gpu_memory_buffer_factory_io_surface_unittest.cc b/content/common/gpu/gpu_memory_buffer_factory_io_surface_unittest.cc
new file mode 100644
index 0000000..7f4a132
--- /dev/null
+++ b/content/common/gpu/gpu_memory_buffer_factory_io_surface_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/gpu_memory_buffer_factory_io_surface.h"
+#include "content/test/gpu_memory_buffer_factory_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferFactoryIOSurface,
+                              GpuMemoryBufferFactoryTest,
+                              GpuMemoryBufferFactoryIOSurface);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
index 0aea4b6b..952e6c50 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
@@ -11,25 +11,6 @@
 #include "ui/ozone/public/surface_factory_ozone.h"
 
 namespace content {
-namespace {
-
-void GetSupportedConfigurations(
-    std::vector<GpuMemoryBufferFactory::Configuration>* configurations) {
-  if (!ui::ClientNativePixmapFactory::GetInstance()) {
-    // unittests don't have to set ClientNativePixmapFactory.
-    return;
-  }
-  std::vector<ui::ClientNativePixmapFactory::Configuration>
-      native_pixmap_configurations =
-          ui::ClientNativePixmapFactory::GetInstance()
-              ->GetSupportedConfigurations();
-  for (auto& native_pixmap_configuration : native_pixmap_configurations) {
-    configurations->push_back({native_pixmap_configuration.format,
-                               native_pixmap_configuration.usage});
-  }
-}
-
-}  // namespace
 
 GpuMemoryBufferFactoryOzoneNativePixmap::
     GpuMemoryBufferFactoryOzoneNativePixmap() {}
@@ -41,20 +22,12 @@
 bool GpuMemoryBufferFactoryOzoneNativePixmap::
     IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
                                             gfx::BufferUsage usage) {
-  std::vector<Configuration> configurations;
-  GetSupportedConfigurations(&configurations);
-  for (auto& configuration : configurations) {
-    if (configuration.format == format && configuration.usage == usage)
-      return true;
+  if (!ui::ClientNativePixmapFactory::GetInstance()) {
+    // unittests don't have to set ClientNativePixmapFactory.
+    return false;
   }
-
-  return false;
-}
-
-void GpuMemoryBufferFactoryOzoneNativePixmap::
-    GetSupportedGpuMemoryBufferConfigurations(
-        std::vector<Configuration>* configurations) {
-  GetSupportedConfigurations(configurations);
+  return ui::ClientNativePixmapFactory::GetInstance()->IsConfigurationSupported(
+      format, usage);
 }
 
 gfx::GpuMemoryBufferHandle
diff --git a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h
index 4a1ad65..19f35c1a 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h
+++ b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/hash_tables.h"
 #include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
 #include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "gpu/command_buffer/service/image_factory.h"
 #include "ui/ozone/public/native_pixmap.h"
@@ -17,8 +18,9 @@
 
 namespace content {
 
-class GpuMemoryBufferFactoryOzoneNativePixmap : public GpuMemoryBufferFactory,
-                                                public gpu::ImageFactory {
+class CONTENT_EXPORT GpuMemoryBufferFactoryOzoneNativePixmap
+    : public GpuMemoryBufferFactory,
+      public gpu::ImageFactory {
  public:
   GpuMemoryBufferFactoryOzoneNativePixmap();
   ~GpuMemoryBufferFactoryOzoneNativePixmap() override;
@@ -27,8 +29,6 @@
                                                       gfx::BufferUsage usage);
 
   // Overridden from GpuMemoryBufferFactory:
-  void GetSupportedGpuMemoryBufferConfigurations(
-      std::vector<Configuration>* configurations) override;
   gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
diff --git a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap_unittest.cc b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap_unittest.cc
new file mode 100644
index 0000000..c6755161
--- /dev/null
+++ b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h"
+#include "content/test/gpu_memory_buffer_factory_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferFactoryOzoneNativePixmap,
+                              GpuMemoryBufferFactoryTest,
+                              GpuMemoryBufferFactoryOzoneNativePixmap);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc b/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc
deleted file mode 100644
index 690b8047..0000000
--- a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.cc
+++ /dev/null
@@ -1,114 +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 "content/common/gpu/gpu_memory_buffer_factory_shared_memory.h"
-
-#include <vector>
-
-#include "base/logging.h"
-#include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h"
-#include "ui/gfx/buffer_format_util.h"
-#include "ui/gl/gl_image.h"
-#include "ui/gl/gl_image_shared_memory.h"
-
-namespace content {
-namespace {
-
-const GpuMemoryBufferFactory::Configuration kSupportedConfigurations[] = {
-    {gfx::BufferFormat::R_8, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::R_8, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::RGBA_4444, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::PERSISTENT_MAP},
-    {gfx::BufferFormat::YUV_420, gfx::BufferUsage::MAP},
-    {gfx::BufferFormat::YUV_420, gfx::BufferUsage::PERSISTENT_MAP}};
-
-}  // namespace
-
-GpuMemoryBufferFactorySharedMemory::GpuMemoryBufferFactorySharedMemory() {
-}
-
-GpuMemoryBufferFactorySharedMemory::~GpuMemoryBufferFactorySharedMemory() {
-}
-
-// static
-bool GpuMemoryBufferFactorySharedMemory::
-    IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
-                                            gfx::BufferUsage usage) {
-  for (auto& configuration : kSupportedConfigurations) {
-    if (configuration.format == format && configuration.usage == usage)
-      return true;
-  }
-
-  return false;
-}
-
-void GpuMemoryBufferFactorySharedMemory::
-    GetSupportedGpuMemoryBufferConfigurations(
-        std::vector<Configuration>* configurations) {
-  configurations->assign(
-      kSupportedConfigurations,
-      kSupportedConfigurations + arraysize(kSupportedConfigurations));
-}
-
-gfx::GpuMemoryBufferHandle
-GpuMemoryBufferFactorySharedMemory::CreateGpuMemoryBuffer(
-    gfx::GpuMemoryBufferId id,
-    const gfx::Size& size,
-    gfx::BufferFormat format,
-    gfx::BufferUsage usage,
-    int client_id,
-    gfx::PluginWindowHandle surface_handle) {
-  size_t buffer_size = 0u;
-  if (!gfx::BufferSizeForBufferFormatChecked(size, format, &buffer_size))
-    return gfx::GpuMemoryBufferHandle();
-
-  base::SharedMemory shared_memory;
-  if (!shared_memory.CreateAnonymous(buffer_size))
-    return gfx::GpuMemoryBufferHandle();
-
-#if DCHECK_IS_ON()
-  DCHECK(buffers_.insert(id).second);
-#endif
-  gfx::GpuMemoryBufferHandle handle;
-  handle.type = gfx::SHARED_MEMORY_BUFFER;
-  handle.id = id;
-  shared_memory.ShareToProcess(base::GetCurrentProcessHandle(), &handle.handle);
-  return handle;
-}
-
-void GpuMemoryBufferFactorySharedMemory::DestroyGpuMemoryBuffer(
-    gfx::GpuMemoryBufferId id,
-    int client_id) {
-#if DCHECK_IS_ON()
-  DCHECK_EQ(buffers_.erase(id), 1u);
-#endif
-}
-
-gpu::ImageFactory* GpuMemoryBufferFactorySharedMemory::AsImageFactory() {
-  return this;
-}
-
-scoped_refptr<gfx::GLImage>
-GpuMemoryBufferFactorySharedMemory::CreateImageForGpuMemoryBuffer(
-    const gfx::GpuMemoryBufferHandle& handle,
-    const gfx::Size& size,
-    gfx::BufferFormat format,
-    unsigned internalformat,
-    int client_id) {
-#if DCHECK_IS_ON()
-  DCHECK_EQ(buffers_.count(handle.id), 1u);
-#endif
-  scoped_refptr<gfx::GLImageSharedMemory> image(
-      new gfx::GLImageSharedMemory(size, internalformat));
-  if (!image->Initialize(handle, format))
-    return scoped_refptr<gfx::GLImage>();
-
-  return image;
-}
-
-}  // namespace content
diff --git a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.h b/content/common/gpu/gpu_memory_buffer_factory_shared_memory.h
deleted file mode 100644
index 0d3291d..0000000
--- a/content/common/gpu/gpu_memory_buffer_factory_shared_memory.h
+++ /dev/null
@@ -1,65 +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.
-
-#ifndef CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_SHARED_MEMORY_H_
-#define CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_SHARED_MEMORY_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/common/gpu/gpu_memory_buffer_factory.h"
-#include "gpu/command_buffer/service/image_factory.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/gpu_memory_buffer.h"
-
-#if DCHECK_IS_ON()
-#include <set>
-#endif
-
-namespace gfx {
-class GLImage;
-}
-
-namespace content {
-
-class GpuMemoryBufferFactorySharedMemory : public GpuMemoryBufferFactory,
-                                           public gpu::ImageFactory {
- public:
-  GpuMemoryBufferFactorySharedMemory();
-  ~GpuMemoryBufferFactorySharedMemory() override;
-
-  static bool IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
-                                                      gfx::BufferUsage usage);
-
-  // Overridden from GpuMemoryBufferFactory:
-  void GetSupportedGpuMemoryBufferConfigurations(
-      std::vector<Configuration>* configurations) override;
-  gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
-      gfx::GpuMemoryBufferId id,
-      const gfx::Size& size,
-      gfx::BufferFormat format,
-      gfx::BufferUsage usage,
-      int client_id,
-      gfx::PluginWindowHandle surface_handle) override;
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                              int client_id) override;
-  gpu::ImageFactory* AsImageFactory() override;
-
-  // Overridden from gpu::ImageFactory:
-  scoped_refptr<gfx::GLImage> CreateImageForGpuMemoryBuffer(
-      const gfx::GpuMemoryBufferHandle& handle,
-      const gfx::Size& size,
-      gfx::BufferFormat format,
-      unsigned internalformat,
-      int client_id) override;
-
- private:
-#if DCHECK_IS_ON()
-  std::set<gfx::GpuMemoryBufferId> buffers_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferFactorySharedMemory);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_SHARED_MEMORY_H_
diff --git a/content/common/gpu/gpu_memory_buffer_factory_surface_texture.cc b/content/common/gpu/gpu_memory_buffer_factory_surface_texture.cc
index 490c45e..3648817d 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_surface_texture.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_surface_texture.cc
@@ -9,12 +9,6 @@
 #include "ui/gl/gl_image_surface_texture.h"
 
 namespace content {
-namespace {
-
-const GpuMemoryBufferFactory::Configuration kSupportedConfigurations[] = {
-    {gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::MAP}};
-
-}  // namespace
 
 GpuMemoryBufferFactorySurfaceTexture::GpuMemoryBufferFactorySurfaceTexture() {
 }
@@ -26,22 +20,17 @@
 bool GpuMemoryBufferFactorySurfaceTexture::
     IsGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format,
                                             gfx::BufferUsage usage) {
-  for (auto& configuration : kSupportedConfigurations) {
-    if (configuration.format == format && configuration.usage == usage)
-      return true;
+  switch (usage) {
+    case gfx::BufferUsage::SCANOUT:
+    case gfx::BufferUsage::PERSISTENT_MAP:
+      return false;
+    case gfx::BufferUsage::MAP:
+      return format == gfx::BufferFormat::RGBA_8888;
   }
-
+  NOTREACHED();
   return false;
 }
 
-void GpuMemoryBufferFactorySurfaceTexture::
-    GetSupportedGpuMemoryBufferConfigurations(
-        std::vector<Configuration>* configurations) {
-  configurations->assign(
-      kSupportedConfigurations,
-      kSupportedConfigurations + arraysize(kSupportedConfigurations));
-}
-
 gfx::GpuMemoryBufferHandle
 GpuMemoryBufferFactorySurfaceTexture::CreateGpuMemoryBuffer(
     gfx::GpuMemoryBufferId id,
diff --git a/content/common/gpu/gpu_memory_buffer_factory_surface_texture.h b/content/common/gpu/gpu_memory_buffer_factory_surface_texture.h
index 1b1ae4eb..57c3cd9a 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_surface_texture.h
+++ b/content/common/gpu/gpu_memory_buffer_factory_surface_texture.h
@@ -5,9 +5,12 @@
 #ifndef CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_SURFACE_TEXTURE_H_
 #define CONTENT_COMMON_GPU_GPU_MEMORY_BUFFER_FACTORY_SURFACE_TEXTURE_H_
 
+#include <utility>
+
 #include "base/containers/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
 #include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "gpu/command_buffer/service/image_factory.h"
 #include "ui/gfx/geometry/size.h"
@@ -20,8 +23,9 @@
 
 namespace content {
 
-class GpuMemoryBufferFactorySurfaceTexture : public GpuMemoryBufferFactory,
-                                             public gpu::ImageFactory {
+class CONTENT_EXPORT GpuMemoryBufferFactorySurfaceTexture
+    : public GpuMemoryBufferFactory,
+      public gpu::ImageFactory {
  public:
   GpuMemoryBufferFactorySurfaceTexture();
   ~GpuMemoryBufferFactorySurfaceTexture() override;
@@ -30,8 +34,6 @@
                                                       gfx::BufferUsage usage);
 
   // Overridden from GpuMemoryBufferFactory:
-  void GetSupportedGpuMemoryBufferConfigurations(
-      std::vector<Configuration>* configurations) override;
   gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
diff --git a/content/common/gpu/gpu_memory_buffer_factory_surface_texture_unittest.cc b/content/common/gpu/gpu_memory_buffer_factory_surface_texture_unittest.cc
new file mode 100644
index 0000000..683860fe
--- /dev/null
+++ b/content/common/gpu/gpu_memory_buffer_factory_surface_texture_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "content/common/gpu/gpu_memory_buffer_factory_surface_texture.h"
+#include "content/test/gpu_memory_buffer_factory_test_template.h"
+
+namespace content {
+namespace {
+
+INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferFactorySurfaceTexture,
+                              GpuMemoryBufferFactoryTest,
+                              GpuMemoryBufferFactorySurfaceTexture);
+
+}  // namespace
+}  // namespace content
diff --git a/content/common/gpu/gpu_memory_buffer_factory_unittest.cc b/content/common/gpu/gpu_memory_buffer_factory_unittest.cc
deleted file mode 100644
index dbd9b76..0000000
--- a/content/common/gpu/gpu_memory_buffer_factory_unittest.cc
+++ /dev/null
@@ -1,58 +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 "content/common/gpu/gpu_memory_buffer_factory.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-namespace {
-
-class GpuMemoryBufferFactoryTest
-    : public testing::TestWithParam<gfx::GpuMemoryBufferType> {
- public:
-  GpuMemoryBufferFactoryTest() : factory_(nullptr) {}
-
-  // Overridden from testing::Test:
-  void SetUp() override {
-    factory_ = GpuMemoryBufferFactory::Create(GetParam());
-    factory_->GetSupportedGpuMemoryBufferConfigurations(
-        &supported_configurations_);
-  }
-  void TearDown() override { factory_.reset(); }
-
- protected:
-  scoped_ptr<GpuMemoryBufferFactory> factory_;
-  std::vector<GpuMemoryBufferFactory::Configuration> supported_configurations_;
-};
-
-TEST_P(GpuMemoryBufferFactoryTest, CreateAndDestroy) {
-  const gfx::GpuMemoryBufferId kBufferId(1);
-  const int kClientId = 1;
-
-  gfx::Size buffer_size(2, 2);
-
-  for (auto configuration : supported_configurations_) {
-    gfx::GpuMemoryBufferHandle handle = factory_->CreateGpuMemoryBuffer(
-        kBufferId, buffer_size, configuration.format, configuration.usage,
-        kClientId, gfx::kNullPluginWindow);
-    EXPECT_EQ(handle.type, GetParam());
-    factory_->DestroyGpuMemoryBuffer(kBufferId, kClientId);
-  }
-}
-
-std::vector<gfx::GpuMemoryBufferType>
-GetSupportedGpuMemoryBufferFactoryTypes() {
-  std::vector<gfx::GpuMemoryBufferType> supported_types;
-  GpuMemoryBufferFactory::GetSupportedTypes(&supported_types);
-  return supported_types;
-}
-
-INSTANTIATE_TEST_CASE_P(
-    GpuMemoryBufferFactoryTests,
-    GpuMemoryBufferFactoryTest,
-    ::testing::ValuesIn(GetSupportedGpuMemoryBufferFactoryTypes()));
-
-}  // namespace
-}  // namespace content
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index b4f85e8..f0f62e34 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -470,6 +470,10 @@
 IPC_SYNC_MESSAGE_CONTROL1_0(GpuChannelMsg_DestroyCommandBuffer,
                             int32 /* instance_id */)
 
+// Simple NOP message which can be used as fence to ensure all previous sent
+// messages have been received.
+IPC_SYNC_MESSAGE_CONTROL0_0(GpuChannelMsg_Nop)
+
 #if defined(OS_ANDROID)
 //------------------------------------------------------------------------------
 // Stream Texture Messages
diff --git a/content/content_common.gypi b/content/content_common.gypi
index c54f367..feea62c8 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -321,8 +321,6 @@
       'common/gpu/gpu_memory_buffer_factory.h',
       'common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc',
       'common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h',
-      'common/gpu/gpu_memory_buffer_factory_shared_memory.cc',
-      'common/gpu/gpu_memory_buffer_factory_shared_memory.h',
       'common/gpu/gpu_memory_manager.cc',
       'common/gpu/gpu_memory_manager.h',
       'common/gpu/gpu_memory_manager_client.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 5d088b6..f626594e 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -118,6 +118,7 @@
       'test/fake_plugin_service.h',
       'test/fake_renderer_scheduler.cc',
       'test/fake_renderer_scheduler.h',
+      'test/gpu_memory_buffer_factory_test_template.h',
       'test/mock_google_streaming_server.cc',
       'test/mock_google_streaming_server.h',
       'test/mock_keyboard.cc',
@@ -634,12 +635,11 @@
       'common/dwrite_font_platform_win_unittest.cc',
       'common/fileapi/file_system_util_unittest.cc',
       'common/font_warmup_win_unittest.cc',
-      'common/gpu/client/gpu_memory_buffer_impl_unittest.cc',
+      'common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc',
       'common/gpu/gpu_channel_test_common.cc',
       'common/gpu/gpu_channel_test_common.h',
       'common/gpu/gpu_channel_unittest.cc',
       'common/gpu/gpu_channel_manager_unittest.cc',
-      'common/gpu/gpu_memory_buffer_factory_unittest.cc',
       'common/gpu/gpu_memory_manager_unittest.cc',
       'common/host_discardable_shared_memory_manager_unittest.cc',
       'common/host_shared_bitmap_manager_unittest.cc',
@@ -788,8 +788,18 @@
       'browser/android/overscroll_refresh_unittest.cc',
       'browser/android/url_request_content_job_unittest.cc',
       'browser/renderer_host/input/motion_event_android_unittest.cc',
+      'common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc',
+      'common/gpu/gpu_memory_buffer_factory_surface_texture_unittest.cc',
       'renderer/java/gin_java_bridge_value_converter_unittest.cc',
     ],
+    'content_unittests_mac_sources': [
+      'common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc',
+      'common/gpu/gpu_memory_buffer_factory_io_surface_unittest.cc',
+    ],
+    'content_unittests_ozone_sources': [
+      'common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc',
+      'common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap_unittest.cc',
+    ],
   },
   'targets': [
     {
@@ -1217,12 +1227,14 @@
           ],
         }],
         ['use_ozone==1', {
+          'sources': [ '<@(content_unittests_ozone_sources)' ],
           'dependencies': [
             '../ui/ozone/ozone.gyp:ozone',
             '../ui/ozone/ozone.gyp:ozone_base',
           ],
         }],
         ['OS == "mac"', {
+          'sources': [ '<@(content_unittests_mac_sources)' ],
           'dependencies': [
             '../third_party/boringssl/boringssl.gyp:boringssl',
           ],
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index e40928c..40c808b 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -81,6 +81,8 @@
       const GpuMsg_CreateGpuMemoryBuffer_Params& params) {
     TRACE_EVENT2("gpu", "GpuMemoryBufferMessageFilter::OnCreateGpuMemoryBuffer",
                  "id", params.id.id, "client_id", params.client_id);
+
+    DCHECK(gpu_memory_buffer_factory_);
     sender_->Send(new GpuHostMsg_GpuMemoryBufferCreated(
         gpu_memory_buffer_factory_->CreateGpuMemoryBuffer(
             params.id, params.size, params.format, params.usage,
@@ -166,15 +168,6 @@
   }
 }
 
-// static
-gfx::GpuMemoryBufferType GpuChildThread::GetGpuMemoryBufferFactoryType() {
-  std::vector<gfx::GpuMemoryBufferType> supported_types;
-  GpuMemoryBufferFactory::GetSupportedTypes(&supported_types);
-  DCHECK(!supported_types.empty());
-  // Note: We always use the preferred type.
-  return supported_types[0];
-}
-
 void GpuChildThread::Shutdown() {
   ChildThreadImpl::Shutdown();
   logging::SetLogMessageHandler(NULL);
diff --git a/content/gpu/gpu_child_thread.h b/content/gpu/gpu_child_thread.h
index 11cd46d..b4ece634 100644
--- a/content/gpu/gpu_child_thread.h
+++ b/content/gpu/gpu_child_thread.h
@@ -55,8 +55,6 @@
 
   ~GpuChildThread() override;
 
-  static gfx::GpuMemoryBufferType GetGpuMemoryBufferFactoryType();
-
   void Shutdown() override;
 
   void Init(const base::Time& process_start_time);
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index e7ce06e..d27ef2b9 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -361,9 +361,10 @@
 
   logging::SetLogMessageHandler(NULL);
 
-  scoped_ptr<GpuMemoryBufferFactory> gpu_memory_buffer_factory =
-      GpuMemoryBufferFactory::Create(
-          GpuChildThread::GetGpuMemoryBufferFactoryType());
+  scoped_ptr<GpuMemoryBufferFactory> gpu_memory_buffer_factory;
+  if (GpuMemoryBufferFactory::GetNativeType() != gfx::EMPTY_BUFFER)
+    gpu_memory_buffer_factory = GpuMemoryBufferFactory::CreateNativeType();
+
   gpu::SyncPointManager sync_point_manager(false);
 
   GpuProcess gpu_process;
diff --git a/content/gpu/in_process_gpu_thread.cc b/content/gpu/in_process_gpu_thread.cc
index e4149db..bd9ef94f 100644
--- a/content/gpu/in_process_gpu_thread.cc
+++ b/content/gpu/in_process_gpu_thread.cc
@@ -18,8 +18,10 @@
       params_(params),
       gpu_process_(NULL),
       sync_point_manager_override_(sync_point_manager_override),
-      gpu_memory_buffer_factory_(GpuMemoryBufferFactory::Create(
-          GpuChildThread::GetGpuMemoryBufferFactoryType())) {
+      gpu_memory_buffer_factory_(
+          GpuMemoryBufferFactory::GetNativeType() != gfx::EMPTY_BUFFER
+              ? GpuMemoryBufferFactory::CreateNativeType()
+              : nullptr) {
   if (!sync_point_manager_override_) {
     sync_point_manager_.reset(new gpu::SyncPointManager(false));
     sync_point_manager_override_ = sync_point_manager_.get();
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index aa0cc6d9..374f228 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -83,6 +83,10 @@
 // features.
 const char kDisableBlinkFeatures[]          = "disable-blink-features";
 
+// Disables new cc/animation system (Project Heaviside). crbug.com/394772
+const char kDisableCompositorAnimationTimelines[] =
+    "disable-compositor-animation-timelines";
+
 // Disable the creation of compositing layers when it would prevent LCD text.
 const char kDisablePreferCompositingToLCDText[] =
     "disable-prefer-compositing-to-lcd-text";
@@ -289,10 +293,6 @@
 const char kDisableAcceleratedJpegDecoding[] =
     "disable-accelerated-jpeg-decoding";
 
-// Enables new cc/animation system (Project Heaviside). crbug.com/394772
-const char kEnableCompositorAnimationTimelines[] =
-    "enable-compositor-animation-timelines";
-
 // Enables LCD text.
 const char kEnableLCDText[]                 = "enable-lcd-text";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index fe4a2fcd..fb2b2d6 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -28,12 +28,13 @@
 CONTENT_EXPORT extern const char kDefaultTileHeight[];
 CONTENT_EXPORT extern const char kDisable2dCanvasAntialiasing[];
 CONTENT_EXPORT extern const char kDisable3DAPIs[];
-CONTENT_EXPORT extern const char kDisableBlinkFeatures[];
 CONTENT_EXPORT extern const char kDisableAccelerated2dCanvas[];
 CONTENT_EXPORT extern const char kDisableAcceleratedJpegDecoding[];
 CONTENT_EXPORT extern const char kDisableAcceleratedMjpegDecode[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[];
 extern const char kDisableBackingStoreLimit[];
+CONTENT_EXPORT extern const char kDisableBlinkFeatures[];
+CONTENT_EXPORT extern const char kDisableCompositorAnimationTimelines[];
 CONTENT_EXPORT extern const char kDisablePreferCompositingToLCDText[];
 CONTENT_EXPORT extern const char kDisableDatabases[];
 CONTENT_EXPORT extern const char kDisableDelayAgnosticAec[];
@@ -95,7 +96,6 @@
 CONTENT_EXPORT extern const char kDomAutomationController[];
 extern const char kEnable2dCanvasClipAntialiasing[];
 CONTENT_EXPORT extern const char kEnableAggressiveDOMStorageFlushing[];
-CONTENT_EXPORT extern const char kEnableCompositorAnimationTimelines[];
 CONTENT_EXPORT extern const char kEnableCredentialManagerAPI[];
 CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[];
 CONTENT_EXPORT extern const char kEnableBlinkFeatures[];
diff --git a/content/public/renderer/media_stream_api.cc b/content/public/renderer/media_stream_api.cc
index da39c4b..d668695 100644
--- a/content/public/renderer/media_stream_api.cc
+++ b/content/public/renderer/media_stream_api.cc
@@ -11,9 +11,6 @@
 #include "content/renderer/media/media_stream_audio_source.h"
 #include "content/renderer/media/media_stream_video_capturer_source.h"
 #include "content/renderer/media/media_stream_video_track.h"
-#include "media/base/audio_capturer_source.h"
-#include "media/base/video_capturer_source.h"
-#include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
 #include "third_party/WebKit/public/platform/WebMediaStream.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
@@ -37,61 +34,34 @@
     bool is_remote,
     bool is_readonly,
     const std::string& media_stream_url) {
-  blink::WebMediaStream stream =
+  blink::WebMediaStream web_stream =
       blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(
           GURL(media_stream_url));
-
-  if (stream.isNull()) {
-    LOG(ERROR) << "Stream not found";
-    return false;
-  }
-  blink::WebString track_id = MakeTrackId();
-  blink::WebMediaStreamSource webkit_source;
-  scoped_ptr<MediaStreamVideoSource> media_stream_source(
-      new MediaStreamVideoCapturerSource(
-          MediaStreamSource::SourceStoppedCallback(),
-          source.Pass()));
-  webkit_source.initialize(
-      track_id,
-      blink::WebMediaStreamSource::TypeVideo,
-      track_id,
-      is_remote,
-      is_readonly);
-  webkit_source.setExtraData(media_stream_source.get());
-
-  blink::WebMediaConstraints constraints;
-  constraints.initialize();
-  stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack(
-      media_stream_source.release(),
-      constraints,
-      MediaStreamVideoSource::ConstraintsCallback(),
-      true));
-  return true;
+  return AddVideoTrackToMediaStream(source.Pass(), is_remote, is_readonly,
+                                    &web_stream);
 }
 
 bool AddAudioTrackToMediaStream(
-    scoped_refptr<media::AudioCapturerSource> source,
+    const scoped_refptr<media::AudioCapturerSource>& source,
     const media::AudioParameters& params,
     bool is_remote,
     bool is_readonly,
     const std::string& media_stream_url) {
   DCHECK(params.IsValid()) << params.AsHumanReadableString();
-  blink::WebMediaStream stream =
+  blink::WebMediaStream web_stream =
       blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(
           GURL(media_stream_url));
-
-  if (stream.isNull()) {
-    LOG(ERROR) << "Stream not found";
+  if (web_stream.isNull()) {
+    DLOG(ERROR) << "Stream not found";
     return false;
   }
   blink::WebMediaStreamSource webkit_source;
-  blink::WebString track_id = MakeTrackId();
-  webkit_source.initialize(
-      track_id,
-      blink::WebMediaStreamSource::TypeAudio,
-      track_id,
-      is_remote,
-      is_readonly);
+  const blink::WebString track_id = MakeTrackId();
+  webkit_source.initialize(track_id,
+                           blink::WebMediaStreamSource::TypeAudio,
+                           track_id,
+                           is_remote,
+                           is_readonly);
 
   MediaStreamAudioSource* audio_source(
       new MediaStreamAudioSource(
@@ -114,7 +84,38 @@
   RenderThreadImpl::current()->GetPeerConnectionDependencyFactory()->
       CreateLocalAudioTrack(web_media_audio_track);
 
-  stream.addTrack(web_media_audio_track);
+  web_stream.addTrack(web_media_audio_track);
+  return true;
+}
+
+bool AddVideoTrackToMediaStream(scoped_ptr<media::VideoCapturerSource> source,
+                                bool is_remote,
+                                bool is_readonly,
+                                blink::WebMediaStream* web_stream) {
+  if (web_stream->isNull()) {
+    DLOG(ERROR) << "Stream not found";
+    return false;
+  }
+  const blink::WebString track_id = MakeTrackId();
+  blink::WebMediaStreamSource webkit_source;
+  scoped_ptr<MediaStreamVideoSource> media_stream_source(
+      new MediaStreamVideoCapturerSource(
+          MediaStreamSource::SourceStoppedCallback(),
+          source.Pass()));
+  webkit_source.initialize(track_id,
+                           blink::WebMediaStreamSource::TypeVideo,
+                           track_id,
+                           is_remote,
+                           is_readonly);
+  webkit_source.setExtraData(media_stream_source.get());
+
+  blink::WebMediaConstraints constraints;
+  constraints.initialize();
+  web_stream->addTrack(MediaStreamVideoTrack::CreateVideoTrack(
+      media_stream_source.release(),
+      constraints,
+      MediaStreamVideoSource::ConstraintsCallback(),
+      true));
   return true;
 }
 
diff --git a/content/public/renderer/media_stream_api.h b/content/public/renderer/media_stream_api.h
index 71ce675..2e87ff3 100644
--- a/content/public/renderer/media_stream_api.h
+++ b/content/public/renderer/media_stream_api.h
@@ -10,17 +10,19 @@
 #include "media/base/video_capturer_source.h"
 
 namespace blink {
-class WebMediaStreamSource;
+class WebMediaStream;
 }
 
-namespace Media {
+namespace media {
 class AudioParameters;
 }
 
 namespace content {
-
-// These two methods will initialize a WebMediaStreamSource object to take
-// data from the provided audio or video capturer source.
+// These methods create a WebMediaStreamSource + MediaStreamSource pair with the
+// provided audio or video capturer source. A new WebMediaStreamTrack +
+// MediaStreamTrack pair is created, holding the previous MediaStreamSource, and
+// is plugged into the stream identified by |media_stream_url| (or passed as
+// |web_stream|).
 // |is_remote| should be true if the source of the data is not a local device.
 // |is_readonly| should be true if the format of the data cannot be changed by
 //     MediaTrackConstraints.
@@ -29,8 +31,14 @@
     bool is_remote,
     bool is_readonly,
     const std::string& media_stream_url);
+CONTENT_EXPORT bool AddVideoTrackToMediaStream(
+    scoped_ptr<media::VideoCapturerSource> source,
+    bool is_remote,
+    bool is_readonly,
+    blink::WebMediaStream* web_stream);
+
 CONTENT_EXPORT bool AddAudioTrackToMediaStream(
-    scoped_refptr<media::AudioCapturerSource> source,
+    const scoped_refptr<media::AudioCapturerSource>& source,
     const media::AudioParameters& params,
     bool is_remote,
     bool is_readonly,
diff --git a/content/public/test/content_browser_test_utils_mac.mm b/content/public/test/content_browser_test_utils_mac.mm
index 165a177..d2e879cf 100644
--- a/content/public/test/content_browser_test_utils_mac.mm
+++ b/content/public/test/content_browser_test_utils_mac.mm
@@ -15,7 +15,7 @@
   NSRect new_bounds = NSRectFromCGRect(bounds.ToCGRect());
   if ([[NSScreen screens] count] > 0) {
     new_bounds.origin.y =
-        [[[NSScreen screens] objectAtIndex:0] frame].size.height -
+        [[[NSScreen screens] firstObject] frame].size.height -
         new_bounds.origin.y - new_bounds.size.height;
   }
 
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 2e97735..04df33c0 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -246,10 +246,10 @@
   settings.accelerated_animation_enabled =
       compositor_deps_->IsThreadedAnimationEnabled();
 
-  if (cmd->HasSwitch(switches::kEnableCompositorAnimationTimelines)) {
-    settings.use_compositor_animation_timelines = true;
-    blink::WebRuntimeFeatures::enableCompositorAnimationTimelines(true);
-  }
+  settings.use_compositor_animation_timelines =
+      !cmd->HasSwitch(switches::kDisableCompositorAnimationTimelines);
+  blink::WebRuntimeFeatures::enableCompositorAnimationTimelines(
+      settings.use_compositor_animation_timelines);
 
   settings.default_tile_size = CalculateDefaultTileSize(widget_);
   if (cmd->HasSwitch(switches::kDefaultTileWidth)) {
diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc
index b5dcb18..2c6234f3 100644
--- a/content/renderer/input/input_handler_proxy.cc
+++ b/content/renderer/input/input_handler_proxy.cc
@@ -536,13 +536,9 @@
           blink::WebSize()));
       disallow_horizontal_fling_scroll_ = !vx;
       disallow_vertical_fling_scroll_ = !vy;
-      TRACE_EVENT_ASYNC_BEGIN2("input",
+      TRACE_EVENT_ASYNC_BEGIN2("input,benchmark",
                                "InputHandlerProxy::HandleGestureFling::started",
-                               this,
-                               "vx",
-                               vx,
-                               "vy",
-                               vy);
+                               this, "vx", vx, "vy", vy);
       // Note that the timestamp will only be used to kickstart the animation if
       // its sufficiently close to the timestamp of the first call |Animate()|.
       has_fling_animation_started_ = false;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 551545e..c783b8e 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2610,8 +2610,9 @@
   if (!ds)
     return;
 
-  TRACE_EVENT2("navigation", "RenderFrameImpl::didStartProvisionalLoad",
-               "id", routing_id_, "url", ds->request().url().string().utf8());
+  TRACE_EVENT2("navigation,benchmark",
+               "RenderFrameImpl::didStartProvisionalLoad", "id", routing_id_,
+               "url", ds->request().url().string().utf8());
   DocumentState* document_state = DocumentState::FromDataSource(ds);
 
   // We should only navigate to swappedout:// when is_swapped_out_ is true.
@@ -2658,8 +2659,8 @@
     blink::WebLocalFrame* frame,
     const blink::WebURLError& error,
     blink::WebHistoryCommitType commit_type) {
-  TRACE_EVENT1("navigation", "RenderFrameImpl::didFailProvisionalLoad",
-               "id", routing_id_);
+  TRACE_EVENT1("navigation,benchmark",
+               "RenderFrameImpl::didFailProvisionalLoad", "id", routing_id_);
   DCHECK(!frame_ || frame_ == frame);
   WebDataSource* ds = frame->provisionalDataSource();
   DCHECK(ds);
@@ -2930,7 +2931,7 @@
 
 void RenderFrameImpl::didFinishDocumentLoad(blink::WebLocalFrame* frame,
                                             bool document_is_empty) {
-  TRACE_EVENT1("navigation", "RenderFrameImpl::didFinishDocumentLoad",
+  TRACE_EVENT1("navigation,benchmark", "RenderFrameImpl::didFinishDocumentLoad",
                "id", routing_id_);
   DCHECK(!frame_ || frame_ == frame);
   WebDataSource* ds = frame->dataSource();
@@ -3022,14 +3023,14 @@
 }
 
 void RenderFrameImpl::didFinishLoad(blink::WebLocalFrame* frame) {
-  TRACE_EVENT1("navigation", "RenderFrameImpl::didFinishLoad",
-               "id", routing_id_);
+  TRACE_EVENT1("navigation,benchmark", "RenderFrameImpl::didFinishLoad", "id",
+               routing_id_);
   DCHECK(!frame_ || frame_ == frame);
   WebDataSource* ds = frame->dataSource();
   DocumentState* document_state = DocumentState::FromDataSource(ds);
   if (document_state->finish_load_time().is_null()) {
     if (!frame->parent()) {
-      TRACE_EVENT_INSTANT0("WebCore", "LoadFinished",
+      TRACE_EVENT_INSTANT0("WebCore,benchmark", "LoadFinished",
                            TRACE_EVENT_SCOPE_PROCESS);
     }
     document_state->set_finish_load_time(Time::Now());
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index c8ca4bdb..a72214d 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -653,8 +653,8 @@
       *base::CommandLine::ForCurrentProcess();
 
   cc::LayerSettings layer_settings;
-  if (command_line.HasSwitch(switches::kEnableCompositorAnimationTimelines))
-    layer_settings.use_compositor_animation_timelines = true;
+  layer_settings.use_compositor_animation_timelines =
+      !command_line.HasSwitch(switches::kDisableCompositorAnimationTimelines);
   cc_blink::WebLayerImpl::SetLayerSettings(layer_settings);
   cc::SetClientNameForMetrics("Renderer");
 
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index 2233563c..f5d6dcdf 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -35,6 +35,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/web_preferences.h"
+#include "content/public/renderer/media_stream_api.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/render_view_visitor.h"
@@ -222,6 +223,23 @@
   DISALLOW_COPY_AND_ASSIGN(MockGamepadProvider);
 };
 
+class MockVideoCapturerSource : public media::VideoCapturerSource {
+ public:
+  MockVideoCapturerSource() = default;
+  ~MockVideoCapturerSource() override {}
+
+  void GetCurrentSupportedFormats(
+      int max_requested_width,
+      int max_requested_height,
+      double max_requested_frame_rate,
+      const VideoCaptureDeviceFormatsCB& callback) override {}
+  void StartCapture(
+      const media::VideoCaptureParams& params,
+      const VideoCaptureDeliverFrameCB& new_frame_callback,
+      const RunningCallback& running_callback) override {}
+  void StopCapture() override {}
+};
+
 }  // namespace
 
 BlinkTestRunner::BlinkTestRunner(RenderView* render_view)
@@ -715,6 +733,20 @@
     test_runner::WebTestProxyBase* proxy) {
 }
 
+bool BlinkTestRunner::AddMediaStreamSourceAndTrack(
+    blink::WebMediaStream* stream) {
+  DCHECK(stream);
+#if defined(ENABLE_WEBRTC)
+  return AddVideoTrackToMediaStream(
+      make_scoped_ptr(new MockVideoCapturerSource()),
+      false /* is_remote */,
+      false /* is_readonly */,
+      stream);
+#else
+  return false;
+#endif
+}
+
 // RenderViewObserver  --------------------------------------------------------
 
 void BlinkTestRunner::DidClearWindowObject(WebLocalFrame* frame) {
diff --git a/content/shell/renderer/layout_test/blink_test_runner.h b/content/shell/renderer/layout_test/blink_test_runner.h
index 6739bed..8ff5a55 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.h
+++ b/content/shell/renderer/layout_test/blink_test_runner.h
@@ -132,6 +132,7 @@
                      const GURL& origin,
                      const GURL& embedding_origin) override;
   void ResetPermissions() override;
+  bool AddMediaStreamSourceAndTrack(blink::WebMediaStream* stream) override;
   cc::SharedBitmapManager* GetSharedBitmapManager() override;
   void DispatchBeforeInstallPromptEvent(
       int request_id,
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index 341a64ed..5439f03 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -5,6 +5,7 @@
 #include "content/test/content_test_suite.h"
 
 #include "base/base_paths.h"
+#include "base/base_switches.h"
 #include "base/logging.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_paths.h"
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
index 184a9edf..eea73dd 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
@@ -1,21 +1,21 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
 ++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0><obj1><obj2>' caret_offset=3 n_selections=1 selection_start=0 selection_end=3
-++++IA2_ROLE_PARAGRAPH IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a <obj1> and an <obj3> and a <obj5>.' caret_offset=44 n_selections=1 selection_start=0 selection_end=44
-++++++ROLE_SYSTEM_STATICTEXT name='A contenteditable with a ' IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a ' caret_offset=25 n_selections=1 selection_start=0 selection_end=25
-++++++ROLE_SYSTEM_LINK name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' caret_offset=4 n_selections=1 selection_start=0 selection_end=4
-++++++++ROLE_SYSTEM_STATICTEXT name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' caret_offset=4 n_selections=1 selection_start=0 selection_end=4
-++++++ROLE_SYSTEM_STATICTEXT name=' and an ' IA2_STATE_EDITABLE ia2_hypertext=' and an ' caret_offset=8 n_selections=1 selection_start=0 selection_end=8
-++++++ROLE_SYSTEM_GRAPHIC name='Image' IA2_STATE_EDITABLE ia2_hypertext='Image' caret_offset=5 n_selections=1 selection_start=0 selection_end=5
-++++++ROLE_SYSTEM_STATICTEXT name=' and a ' IA2_STATE_EDITABLE ia2_hypertext=' and a ' caret_offset=7 n_selections=1 selection_start=0 selection_end=7
-++++++ROLE_SYSTEM_PUSHBUTTON name='Button' FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='Button' caret_offset=6 n_selections=1 selection_start=0 selection_end=6
-++++++ROLE_SYSTEM_STATICTEXT name='.' IA2_STATE_EDITABLE ia2_hypertext='.' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
-++++ROLE_SYSTEM_TABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0><obj1><obj2>' caret_offset=3 n_selections=1 selection_start=0 selection_end=3
-++++++ROLE_SYSTEM_ROW IA2_STATE_EDITABLE ia2_hypertext='<obj0>' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
-++++++++ROLE_SYSTEM_CELL IA2_STATE_EDITABLE ia2_hypertext='Always expose editable tables as tables.' caret_offset=40 n_selections=1 selection_start=0 selection_end=40
-++++++++++ROLE_SYSTEM_STATICTEXT name='Always expose editable tables as tables.' IA2_STATE_EDITABLE ia2_hypertext='Always expose editable tables as tables.' caret_offset=40 n_selections=1 selection_start=0 selection_end=40
+++++IA2_ROLE_PARAGRAPH IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a <obj1> and an <obj3> and a <obj5>.' n_selections=1 selection_start=0 selection_end=44
+++++++ROLE_SYSTEM_STATICTEXT name='A contenteditable with a ' IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a ' n_selections=1 selection_start=0 selection_end=25
+++++++ROLE_SYSTEM_LINK name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' n_selections=1 selection_start=0 selection_end=4
+++++++++ROLE_SYSTEM_STATICTEXT name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' n_selections=1 selection_start=0 selection_end=4
+++++++ROLE_SYSTEM_STATICTEXT name=' and an ' IA2_STATE_EDITABLE ia2_hypertext=' and an ' n_selections=1 selection_start=0 selection_end=8
+++++++ROLE_SYSTEM_GRAPHIC name='Image' IA2_STATE_EDITABLE ia2_hypertext='Image' n_selections=1 selection_start=0 selection_end=5
+++++++ROLE_SYSTEM_STATICTEXT name=' and a ' IA2_STATE_EDITABLE ia2_hypertext=' and a ' n_selections=1 selection_start=0 selection_end=7
+++++++ROLE_SYSTEM_PUSHBUTTON name='Button' FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='Button' n_selections=1 selection_start=0 selection_end=6
+++++++ROLE_SYSTEM_STATICTEXT name='.' IA2_STATE_EDITABLE ia2_hypertext='.' n_selections=1 selection_start=0 selection_end=1
+++++ROLE_SYSTEM_TABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0><obj1><obj2>' n_selections=1 selection_start=0 selection_end=3
+++++++ROLE_SYSTEM_ROW IA2_STATE_EDITABLE ia2_hypertext='<obj0>' n_selections=1 selection_start=0 selection_end=1
+++++++++ROLE_SYSTEM_CELL IA2_STATE_EDITABLE ia2_hypertext='Always expose editable tables as tables.' n_selections=1 selection_start=0 selection_end=40
+++++++++++ROLE_SYSTEM_STATICTEXT name='Always expose editable tables as tables.' IA2_STATE_EDITABLE ia2_hypertext='Always expose editable tables as tables.' n_selections=1 selection_start=0 selection_end=40
 ++++++ROLE_SYSTEM_COLUMN n_selections=0
 ++++++IA2_ROLE_SECTION n_selections=0
 ++++ROLE_SYSTEM_LIST IA2_STATE_EDITABLE ia2_hypertext='<obj0>' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
 ++++++ROLE_SYSTEM_LISTITEM name='Editable list item.' IA2_STATE_EDITABLE ia2_hypertext='<obj0>Editable list item.' caret_offset=20 n_selections=1 selection_start=0 selection_end=20
-++++++++ROLE_SYSTEM_STATICTEXT name='1' ia2_hypertext='1' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
+++++++++ROLE_SYSTEM_STATICTEXT name='1' ia2_hypertext='1' n_selections=1 selection_start=0 selection_end=1
 ++++++++ROLE_SYSTEM_STATICTEXT name='Editable list item.' IA2_STATE_EDITABLE ia2_hypertext='Editable list item.' caret_offset=19 n_selections=1 selection_start=0 selection_end=19
diff --git a/content/test/gpu/gpu_tests/webgl_conformance.py b/content/test/gpu/gpu_tests/webgl_conformance.py
index d576894..a23311d3 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance.py
@@ -88,6 +88,7 @@
         '--disable-gesture-requirement-for-media-playback',
         '--disable-domain-blocking-for-3d-apis',
         '--disable-gpu-process-crash-limit',
+        '--js-flags=--expose-gc',
         '--test-type=gpu'
     ])
     browser = browser_finder.FindBrowser(options.finder_options)
@@ -116,6 +117,7 @@
         '--disable-gesture-requirement-for-media-playback',
         '--disable-domain-blocking-for-3d-apis',
         '--disable-gpu-process-crash-limit',
+        '--js-flags=--expose-gc',
         '--enable-unsafe-es3-apis',
         '--test-type=gpu'
     ])
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index e7b4ff9..282925e3 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -36,10 +36,14 @@
         bug=478572)
     self.Fail('deqp/data/gles2/shaders/scoping.html',
         bug=478572)
+    self.Fail('conformance/extensions/ext-sRGB.html',
+        bug=540900)
+    self.Fail('conformance/extensions/oes-standard-derivatives.html',
+        bug=5400916)
+    self.Fail('conformance/extensions/ext-frag-depth.html',
+        bug=5400916)
     self.Fail('conformance/misc/expando-loss.html',
         bug=485634)
-    self.Fail('conformance/buffers/buffer-data-array-buffer.html',
-        bug=535077)
 
     # Win failures
     self.Fail('conformance/glsl/bugs/' +
@@ -109,6 +113,16 @@
     self.Fail('deqp/data/gles2/shaders/swizzles.html',
         ['win', 'amd', 'opengl'], bug=1007) # angle bug ID
 
+    # Win / OpenGL / Intel failures
+    self.Fail('conformance/extensions/webgl-draw-buffers.html',
+        ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
+    self.Fail('conformance/glsl/functions/glsl-function-normalize.html',
+        ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
+    self.Fail('conformance/glsl/misc/shader-struct-scope.html',
+        ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
+    self.Fail('conformance/uniforms/uniform-default-values.html',
+        ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
+
     # Mac failures
     self.Fail('conformance/glsl/misc/shaders-with-invariance.html',
         ['mac'], bug=421710)
diff --git a/content/test/gpu_memory_buffer_factory_test_template.h b/content/test/gpu_memory_buffer_factory_test_template.h
new file mode 100644
index 0000000..e4240128
--- /dev/null
+++ b/content/test/gpu_memory_buffer_factory_test_template.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.
+
+// This file defines tests that implementations of GpuMemoryBufferFactory should
+// pass in order to be conformant.
+
+#ifndef CONTENT_TEST_GPU_MEMORY_BUFFER_FACTORY_TEST_TEMPLATE_H_
+#define CONTENT_TEST_GPU_MEMORY_BUFFER_FACTORY_TEST_TEMPLATE_H_
+
+#include "content/common/gpu/gpu_memory_buffer_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace content {
+
+template <typename GpuMemoryBufferFactoryType>
+class GpuMemoryBufferFactoryTest : public testing::Test {
+ protected:
+  GpuMemoryBufferFactoryType factory_;
+};
+
+TYPED_TEST_CASE_P(GpuMemoryBufferFactoryTest);
+
+TYPED_TEST_P(GpuMemoryBufferFactoryTest, CreateGpuMemoryBuffer) {
+  const gfx::GpuMemoryBufferId kBufferId(1);
+  const int kClientId = 1;
+
+  gfx::Size buffer_size(2, 2);
+
+  for (auto format : gfx::GetBufferFormats()) {
+    gfx::BufferUsage usages[] = {gfx::BufferUsage::MAP,
+                                 gfx::BufferUsage::PERSISTENT_MAP,
+                                 gfx::BufferUsage::SCANOUT};
+    for (auto usage : usages) {
+      if (!TypeParam::IsGpuMemoryBufferConfigurationSupported(format, usage))
+        continue;
+
+      gfx::GpuMemoryBufferHandle handle =
+          TestFixture::factory_.CreateGpuMemoryBuffer(kBufferId, buffer_size,
+                                                      format, usage, kClientId,
+                                                      gfx::kNullPluginWindow);
+      EXPECT_NE(handle.type, gfx::EMPTY_BUFFER);
+      TestFixture::factory_.DestroyGpuMemoryBuffer(kBufferId, kClientId);
+    }
+  }
+}
+
+// The GpuMemoryBufferFactoryTest test case verifies behavior that is expected
+// from a GpuMemoryBuffer factory in order to be conformant.
+REGISTER_TYPED_TEST_CASE_P(GpuMemoryBufferFactoryTest, CreateGpuMemoryBuffer);
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_GPU_MEMORY_BUFFER_FACTORY_TEST_TEMPLATE_H_
diff --git a/content/test/gpu_memory_buffer_impl_test_template.h b/content/test/gpu_memory_buffer_impl_test_template.h
new file mode 100644
index 0000000..bb3d82c6
--- /dev/null
+++ b/content/test/gpu_memory_buffer_impl_test_template.h
@@ -0,0 +1,225 @@
+// 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 file defines tests that implementations of GpuMemoryBufferFactory should
+// pass in order to be conformant.
+
+#ifndef CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_
+#define CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace content {
+
+template <typename GpuMemoryBufferImplType>
+class GpuMemoryBufferImplTest : public testing::Test {
+ public:
+  GpuMemoryBufferImpl::DestructionCallback AllocateGpuMemoryBuffer(
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      gfx::GpuMemoryBufferHandle* handle,
+      bool* destroyed) {
+    return base::Bind(&GpuMemoryBufferImplTest::FreeGpuMemoryBuffer,
+                      base::Unretained(this),
+                      GpuMemoryBufferImplType::AllocateForTesting(
+                          size, format, usage, handle),
+                      base::Unretained(destroyed));
+  }
+
+ private:
+  void FreeGpuMemoryBuffer(const base::Closure& free_callback,
+                           bool* destroyed,
+                           uint32 sync_point) {
+    free_callback.Run();
+    if (destroyed)
+      *destroyed = true;
+  }
+};
+
+TYPED_TEST_CASE_P(GpuMemoryBufferImplTest);
+
+TYPED_TEST_P(GpuMemoryBufferImplTest, CreateFromHandle) {
+  gfx::Size buffer_size(8, 8);
+
+  for (auto format : gfx::GetBufferFormats()) {
+    gfx::BufferUsage usages[] = {gfx::BufferUsage::MAP,
+                                 gfx::BufferUsage::PERSISTENT_MAP,
+                                 gfx::BufferUsage::SCANOUT};
+    for (auto usage : usages) {
+      if (!TypeParam::IsConfigurationSupported(format, usage))
+        continue;
+
+      bool destroyed = false;
+      gfx::GpuMemoryBufferHandle handle;
+      GpuMemoryBufferImpl::DestructionCallback destroy_callback =
+          TestFixture::AllocateGpuMemoryBuffer(
+              buffer_size, format, gfx::BufferUsage::MAP, &handle, &destroyed);
+      scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle(
+          handle, buffer_size, format, usage, destroy_callback));
+      ASSERT_TRUE(buffer);
+      EXPECT_EQ(buffer->GetFormat(), format);
+
+      // Check if destruction callback is executed when deleting the buffer.
+      buffer.reset();
+      ASSERT_TRUE(destroyed);
+    }
+  }
+}
+
+TYPED_TEST_P(GpuMemoryBufferImplTest, Map) {
+  // Use a multiple of 4 for both dimensions to support compressed formats.
+  gfx::Size buffer_size(4, 4);
+
+  for (auto format : gfx::GetBufferFormats()) {
+    if (!TypeParam::IsConfigurationSupported(format, gfx::BufferUsage::MAP))
+      continue;
+
+    gfx::GpuMemoryBufferHandle handle;
+    GpuMemoryBufferImpl::DestructionCallback destroy_callback =
+        TestFixture::AllocateGpuMemoryBuffer(
+            buffer_size, format, gfx::BufferUsage::MAP, &handle, nullptr);
+    scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle(
+        handle, buffer_size, format, gfx::BufferUsage::MAP, destroy_callback));
+    ASSERT_TRUE(buffer);
+    EXPECT_FALSE(buffer->IsMapped());
+
+    size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
+
+    // Map buffer into user space.
+    scoped_ptr<void* []> mapped_buffers(new void*[num_planes]);
+    bool rv = buffer->Map(mapped_buffers.get());
+    ASSERT_TRUE(rv);
+    EXPECT_TRUE(buffer->IsMapped());
+
+    // Get strides.
+    scoped_ptr<int[]> strides(new int[num_planes]);
+    buffer->GetStride(strides.get());
+
+    // Copy and compare mapped buffers.
+    for (size_t plane = 0; plane < num_planes; ++plane) {
+      size_t row_size_in_bytes = 0;
+      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
+          buffer_size.width(), format, plane, &row_size_in_bytes));
+      EXPECT_GT(row_size_in_bytes, 0u);
+
+      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
+      memset(data.get(), 0x2a + plane, row_size_in_bytes);
+
+      size_t height = buffer_size.height() /
+                      gfx::SubsamplingFactorForBufferFormat(format, plane);
+      for (size_t y = 0; y < height; ++y) {
+        memcpy(static_cast<char*>(mapped_buffers[plane]) + y * strides[plane],
+               data.get(), row_size_in_bytes);
+        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
+                             y * strides[plane],
+                         data.get(), row_size_in_bytes),
+                  0);
+      }
+    }
+
+    buffer->Unmap();
+    EXPECT_FALSE(buffer->IsMapped());
+  }
+}
+
+TYPED_TEST_P(GpuMemoryBufferImplTest, PersistentMap) {
+  // Use a multiple of 4 for both dimensions to support compressed formats.
+  gfx::Size buffer_size(4, 4);
+
+  for (auto format : gfx::GetBufferFormats()) {
+    if (!TypeParam::IsConfigurationSupported(
+            format, gfx::BufferUsage::PERSISTENT_MAP)) {
+      continue;
+    }
+
+    gfx::GpuMemoryBufferHandle handle;
+    GpuMemoryBufferImpl::DestructionCallback destroy_callback =
+        TestFixture::AllocateGpuMemoryBuffer(buffer_size, format,
+                                             gfx::BufferUsage::PERSISTENT_MAP,
+                                             &handle, nullptr);
+    scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle(
+        handle, buffer_size, format, gfx::BufferUsage::PERSISTENT_MAP,
+        destroy_callback));
+    ASSERT_TRUE(buffer);
+    EXPECT_FALSE(buffer->IsMapped());
+
+    size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
+
+    // Map buffer into user space.
+    scoped_ptr<void* []> mapped_buffers(new void*[num_planes]);
+    bool rv = buffer->Map(mapped_buffers.get());
+    ASSERT_TRUE(rv);
+    EXPECT_TRUE(buffer->IsMapped());
+
+    // Get strides.
+    scoped_ptr<int[]> strides(new int[num_planes]);
+    buffer->GetStride(strides.get());
+
+    // Copy and compare mapped buffers.
+    for (size_t plane = 0; plane < num_planes; ++plane) {
+      size_t row_size_in_bytes;
+      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
+          buffer_size.width(), format, plane, &row_size_in_bytes));
+      EXPECT_GT(row_size_in_bytes, 0u);
+
+      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
+      memset(data.get(), 0x2a + plane, row_size_in_bytes);
+
+      size_t height = buffer_size.height() /
+                      gfx::SubsamplingFactorForBufferFormat(format, plane);
+      for (size_t y = 0; y < height; ++y) {
+        memcpy(static_cast<char*>(mapped_buffers[plane]) + y * strides[plane],
+               data.get(), row_size_in_bytes);
+        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
+                             y * strides[plane],
+                         data.get(), row_size_in_bytes),
+                  0);
+      }
+    }
+
+    buffer->Unmap();
+    EXPECT_FALSE(buffer->IsMapped());
+
+    // Remap the buffer, and compare again. It should contain the same data.
+    rv = buffer->Map(mapped_buffers.get());
+    ASSERT_TRUE(rv);
+    EXPECT_TRUE(buffer->IsMapped());
+
+    buffer->GetStride(strides.get());
+
+    for (size_t plane = 0; plane < num_planes; ++plane) {
+      size_t row_size_in_bytes;
+      EXPECT_TRUE(gfx::RowSizeForBufferFormatChecked(
+          buffer_size.width(), format, plane, &row_size_in_bytes));
+
+      scoped_ptr<char[]> data(new char[row_size_in_bytes]);
+      memset(data.get(), 0x2a + plane, row_size_in_bytes);
+
+      size_t height = buffer_size.height() /
+                      gfx::SubsamplingFactorForBufferFormat(format, plane);
+      for (size_t y = 0; y < height; ++y) {
+        EXPECT_EQ(memcmp(static_cast<char*>(mapped_buffers[plane]) +
+                             y * strides[plane],
+                         data.get(), row_size_in_bytes),
+                  0);
+      }
+    }
+
+    buffer->Unmap();
+    EXPECT_FALSE(buffer->IsMapped());
+  }
+}
+
+// The GpuMemoryBufferImplTest test case verifies behavior that is expected
+// from a GpuMemoryBuffer implementation in order to be conformant.
+REGISTER_TYPED_TEST_CASE_P(GpuMemoryBufferImplTest,
+                           CreateFromHandle,
+                           Map,
+                           PersistentMap);
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_
diff --git a/content/test/web_layer_tree_view_impl_for_testing.cc b/content/test/web_layer_tree_view_impl_for_testing.cc
index 45a4a288..e2d5275 100644
--- a/content/test/web_layer_tree_view_impl_for_testing.cc
+++ b/content/test/web_layer_tree_view_impl_for_testing.cc
@@ -13,18 +13,14 @@
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
 #include "cc/scheduler/begin_frame_source.h"
-#include "cc/test/pixel_test_output_surface.h"
-#include "cc/test/test_context_provider.h"
 #include "cc/trees/layer_tree_host.h"
 #include "content/test/test_blink_web_unit_test_support.h"
 #include "third_party/WebKit/public/platform/Platform.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/WebKit/public/platform/WebLayer.h"
 #include "third_party/WebKit/public/platform/WebLayerTreeView.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 
 using blink::WebColor;
-using blink::WebGraphicsContext3D;
 using blink::WebRect;
 using blink::WebSize;
 
@@ -127,15 +123,11 @@
 }
 
 void WebLayerTreeViewImplForTesting::RequestNewOutputSurface() {
-  // TODO(crbug.com/540026): Fix crashes with real OutputSurface
-  // bool flipped_output_surface = false;
-  // layer_tree_host_->SetOutputSurface(
-  //     make_scoped_ptr(new cc::PixelTestOutputSurface(
-  //         cc::TestContextProvider::Create(), flipped_output_surface)));
+  // Intentionally do not create and set an OutputSurface.
 }
 
 void WebLayerTreeViewImplForTesting::DidFailToInitializeOutputSurface() {
-  RequestNewOutputSurface();
+  NOTREACHED();
 }
 
 void WebLayerTreeViewImplForTesting::registerForAnimations(
diff --git a/content/test/web_layer_tree_view_impl_for_testing.h b/content/test/web_layer_tree_view_impl_for_testing.h
index 01192f442..70ffc0f 100644
--- a/content/test/web_layer_tree_view_impl_for_testing.h
+++ b/content/test/web_layer_tree_view_impl_for_testing.h
@@ -19,6 +19,7 @@
 
 namespace content {
 
+// Dummy WeblayerTeeView that does not support any actual compositing.
 class WebLayerTreeViewImplForTesting
     : public blink::WebLayerTreeView,
       public cc::LayerTreeHostClient,
diff --git a/extensions/common/api/networking_private.idl b/extensions/common/api/networking_private.idl
index ea77c3c6..2cf52fd 100644
--- a/extensions/common/api/networking_private.idl
+++ b/extensions/common/api/networking_private.idl
@@ -77,10 +77,10 @@
   // SharedSetting: The value set for all users of the device. Only provided if
   //     DeviceEditiable is true (i.e. no policy affects the property or the
   //     policy provided value is recommened only).
-  // UserEditable: True if the UserPolicy allows the property to be edited
-  //     (i.e. is a recommended value). Defaults to True.
-  // DeviceEditable: True if the DevicePolicy allows the property to be
-  //     edited (i.e. is a recommended value). Defaults to True.
+  // UserEditable: True if a UserPolicy exists and allows the property to be
+  //     edited (i.e. is a recommended value). Defaults to False.
+  // DeviceEditable: True if a DevicePolicy exists and allows the property to be
+  //     edited (i.e. is a recommended value). Defaults to False.
 
   dictionary ManagedBoolean {
     boolean? Active;
@@ -555,11 +555,13 @@
   };
 
   dictionary EthernetProperties {
+    boolean? AutoConnect;
     DOMString? Authentication;
     EAPProperties? EAP;
   };
 
   dictionary ManagedEthernetProperties {
+    ManagedBoolean? AutoConnect;
     ManagedDOMString? Authentication;
     ManagedEAPProperties? EAP;
   };
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 34ca8cba..94eb30e7 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -127,6 +127,7 @@
     "command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc",
     "command_buffer/tests/gl_cube_map_texture_unittest.cc",
     "command_buffer/tests/gl_depth_texture_unittest.cc",
+    "command_buffer/tests/gl_fence_sync_unittest.cc",
     "command_buffer/tests/gl_gpu_memory_buffer_unittest.cc",
     "command_buffer/tests/gl_lose_context_chromium_unittest.cc",
     "command_buffer/tests/gl_manager.cc",
@@ -262,6 +263,7 @@
     "command_buffer/service/shader_manager_unittest.cc",
     "command_buffer/service/shader_translator_cache_unittest.cc",
     "command_buffer/service/shader_translator_unittest.cc",
+    "command_buffer/service/sync_point_manager_unittest.cc",
     "command_buffer/service/test_helper.cc",
     "command_buffer/service/test_helper.h",
     "command_buffer/service/texture_manager_unittest.cc",
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_sync_point.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_sync_point.txt
index 9f1863c..80df38e 100644
--- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_sync_point.txt
+++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_sync_point.txt
@@ -41,7 +41,7 @@
 
     The command
 
-        uint InsertFenceSyncCHROMIUM()
+        uint64 InsertFenceSyncCHROMIUM()
 
     inserts a fence sync in the current command stream. The fence sync is
     signaled when previous commands have been submitted to the server, or when
@@ -53,7 +53,7 @@
 
     The command
 
-        void GenSyncTokenCHROMIUM(uint fence_sync, GLbyte *sync_token)
+        void GenSyncTokenCHROMIUM(uint64 fence_sync, GLbyte *sync_token)
 
     converts <fence_sync> which is only visible to the current context to a
     sync token which may be waited upon by any contexts on the same server.
@@ -67,7 +67,7 @@
 
     The command
 
-        void WaitSyncTokenCHROMIUM(GLbyte *sync_token)
+        void WaitSyncTokenCHROMIUM(const GLbyte *sync_token)
 
     causes the current context to stop submitting commands until the specified
     fence sync becomes signaled. This is implemented as a server-side wait.
@@ -79,7 +79,7 @@
 
     The size of a sync token name in bytes.
 
-        GL_SYNC_TOKEN_SIZE_CHROMIUM                        12
+        GL_SYNC_TOKEN_SIZE_CHROMIUM                        24
 
 Errors
 
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index 2e6ed9b..3b30b9b 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -327,6 +327,9 @@
 #define glLoseContextCHROMIUM GLES2_GET_FUN(LoseContextCHROMIUM)
 #define glInsertSyncPointCHROMIUM GLES2_GET_FUN(InsertSyncPointCHROMIUM)
 #define glWaitSyncPointCHROMIUM GLES2_GET_FUN(WaitSyncPointCHROMIUM)
+#define glInsertFenceSyncCHROMIUM GLES2_GET_FUN(InsertFenceSyncCHROMIUM)
+#define glGenSyncTokenCHROMIUM GLES2_GET_FUN(GenSyncTokenCHROMIUM)
+#define glWaitSyncTokenCHROMIUM GLES2_GET_FUN(WaitSyncTokenCHROMIUM)
 #define glDrawBuffersEXT GLES2_GET_FUN(DrawBuffersEXT)
 #define glDiscardBackbufferCHROMIUM GLES2_GET_FUN(DiscardBackbufferCHROMIUM)
 #define glScheduleOverlayPlaneCHROMIUM \
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 74988dc68..51928a3 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -695,12 +695,26 @@
 /* GL_CHROMIUM_sync_point */
 #ifndef GL_CHROMIUM_sync_point
 #define GL_CHROMIUM_sync_point 1
+
+#ifndef GL_SYNC_TOKEN_SIZE_CHROMIUM
+#define GL_SYNC_TOKEN_SIZE_CHROMIUM 24
+#endif
+
 #ifdef GL_GLEXT_PROTOTYPES
 GL_APICALL GLuint GL_APIENTRY glInsertSyncPointCHROMIUM();
 GL_APICALL void GL_APIENTRY glWaitSyncPointCHROMIUM(GLuint sync_point);
+GL_APICALL GLuint64 GL_APIENTRY glInsertFenceSyncCHROMIUM();
+GL_APICALL void GL_APIENTRY glGenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                                   GLbyte* sync_token);
+GL_APICALL void GL_APIENTRY glWaitSyncTokenCHROMIUM(const GLbyte* sync_token);
 #endif
 typedef GLuint (GL_APIENTRYP PFNGLINSERTSYNCPOINTCHROMIUMPROC) ();
 typedef void (GL_APIENTRYP PFNGLWAITSYNCPOINTCHROMIUMPROC) (GLuint sync_point);
+typedef GLuint64 (GL_APIENTRYP PFNGLINSERTFENCESYNCCHROMIUMPROC) ();
+typedef void (GL_APIENTRYP PFNGLGENSYNCTOKENCHROMIUMPROC) (GLuint64 fence_sync,
+                                                           GLbyte* sync_token);
+typedef void (GL_APIENTRYP PFNGLWAITSYNCTOKENCHROMIUM) (
+    const GLbyte* sync_tokens);
 #endif  /* GL_CHROMIUM_sync_point */
 
 #ifndef GL_CHROMIUM_color_buffer_float_rgba
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 597f5259..e726958 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -4090,6 +4090,30 @@
     'chromium': True,
     'trace_level': 1,
   },
+  'InsertFenceSyncCHROMIUM': {
+    'type': 'Custom',
+    'impl_func': False,
+    'cmd_args': 'GLuint64 release_count',
+    'extension': "CHROMIUM_sync_point",
+    'chromium': True,
+    'trace_level': 1,
+  },
+  'GenSyncTokenCHROMIUM': {
+    'type': 'Custom',
+    'impl_func': False,
+    'extension': "CHROMIUM_sync_point",
+    'chromium': True,
+  },
+  'WaitSyncTokenCHROMIUM': {
+    'type': 'Custom',
+    'impl_func': False,
+    'cmd_args': 'GLuint namespace_id, '
+                'GLuint64 command_buffer_id, '
+                'GLuint64 release_count',
+    'client_test': False,
+    'extension': "CHROMIUM_sync_point",
+    'chromium': True,
+  },
   'DiscardBackbufferCHROMIUM': {
     'type': 'Custom',
     'impl_func': True,
@@ -4605,9 +4629,7 @@
   def WriteHandlerExtensionCheck(self, func, f):
     if func.GetInfo('extension_flag'):
       f.write("  if (!features().%s) {\n" % func.GetInfo('extension_flag'))
-      f.write("    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, \"gl%s\","
-                 " \"function not available\");\n" % func.original_name)
-      f.write("    return error::kNoError;")
+      f.write("    return error::kUnknownCommand;")
       f.write("  }\n\n")
 
   def WriteHandlerDeferReadWrite(self, func, f):
diff --git a/gpu/command_buffer/client/client_test_helper.h b/gpu/command_buffer/client/client_test_helper.h
index e16e089..a0733b5 100644
--- a/gpu/command_buffer/client/client_test_helper.h
+++ b/gpu/command_buffer/client/client_test_helper.h
@@ -114,6 +114,9 @@
   MOCK_METHOD0(IsGpuChannelLost, bool());
   MOCK_CONST_METHOD0(GetNamespaceID, CommandBufferNamespace());
   MOCK_CONST_METHOD0(GetCommandBufferID, uint64_t());
+  MOCK_METHOD0(GenerateFenceSyncRelease, uint64_t());
+  MOCK_METHOD1(IsFenceSyncRelease, bool(uint64_t release));
+  MOCK_METHOD1(IsFenceSyncFlushed, bool(uint64_t release));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockClientGpuControl);
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index e1ce501..4b2ee2b 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1481,6 +1481,16 @@
 void GL_APIENTRY GLES2WaitSyncPointCHROMIUM(GLuint sync_point) {
   gles2::GetGLContext()->WaitSyncPointCHROMIUM(sync_point);
 }
+GLuint64 GL_APIENTRY GLES2InsertFenceSyncCHROMIUM() {
+  return gles2::GetGLContext()->InsertFenceSyncCHROMIUM();
+}
+void GL_APIENTRY GLES2GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                           GLbyte* sync_token) {
+  gles2::GetGLContext()->GenSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void GL_APIENTRY GLES2WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+  gles2::GetGLContext()->WaitSyncTokenCHROMIUM(sync_token);
+}
 void GL_APIENTRY GLES2DrawBuffersEXT(GLsizei count, const GLenum* bufs) {
   gles2::GetGLContext()->DrawBuffersEXT(count, bufs);
 }
@@ -2720,6 +2730,18 @@
         reinterpret_cast<GLES2FunctionPointer>(glWaitSyncPointCHROMIUM),
     },
     {
+        "glInsertFenceSyncCHROMIUM",
+        reinterpret_cast<GLES2FunctionPointer>(glInsertFenceSyncCHROMIUM),
+    },
+    {
+        "glGenSyncTokenCHROMIUM",
+        reinterpret_cast<GLES2FunctionPointer>(glGenSyncTokenCHROMIUM),
+    },
+    {
+        "glWaitSyncTokenCHROMIUM",
+        reinterpret_cast<GLES2FunctionPointer>(glWaitSyncTokenCHROMIUM),
+    },
+    {
         "glDrawBuffersEXT",
         reinterpret_cast<GLES2FunctionPointer>(glDrawBuffersEXT),
     },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index fb77fa2..b722d6e 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2757,6 +2757,34 @@
   }
 }
 
+void InsertFenceSyncCHROMIUM(GLuint64 release_count) {
+  gles2::cmds::InsertFenceSyncCHROMIUM* c =
+      GetCmdSpace<gles2::cmds::InsertFenceSyncCHROMIUM>();
+  if (c) {
+    c->Init(release_count);
+  }
+}
+
+void GenSyncTokenCHROMIUMImmediate(GLuint64 fence_sync) {
+  const uint32_t s = 0;  // TODO(gman): compute correct size
+  gles2::cmds::GenSyncTokenCHROMIUMImmediate* c =
+      GetImmediateCmdSpaceTotalSize<gles2::cmds::GenSyncTokenCHROMIUMImmediate>(
+          s);
+  if (c) {
+    c->Init(fence_sync);
+  }
+}
+
+void WaitSyncTokenCHROMIUM(GLuint namespace_id,
+                           GLuint64 command_buffer_id,
+                           GLuint64 release_count) {
+  gles2::cmds::WaitSyncTokenCHROMIUM* c =
+      GetCmdSpace<gles2::cmds::WaitSyncTokenCHROMIUM>();
+  if (c) {
+    c->Init(namespace_id, command_buffer_id, release_count);
+  }
+}
+
 void DrawBuffersEXTImmediate(GLsizei count, const GLenum* bufs) {
   const uint32_t size =
       gles2::cmds::DrawBuffersEXTImmediate::ComputeSize(count);
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 2b186a4..27bdd8a 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -5364,6 +5364,48 @@
   return share_group_->TracingGUID();
 }
 
+GLuint64 GLES2Implementation::InsertFenceSyncCHROMIUM() {
+  const uint64_t release = gpu_control_->GenerateFenceSyncRelease();
+  helper_->InsertFenceSyncCHROMIUM(release);
+  return release;
+}
+
+void GLES2Implementation::GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                               GLbyte* sync_token) {
+  if (!sync_token) {
+    SetGLError(GL_INVALID_VALUE, "glGenSyncTokenCHROMIUM", "empty sync_token");
+    return;
+  } else if (!gpu_control_->IsFenceSyncRelease(fence_sync)) {
+    SetGLError(GL_INVALID_VALUE, "glGenSyncTokenCHROMIUM",
+               "invalid fence sync");
+    return;
+  } else if (!gpu_control_->IsFenceSyncFlushed(fence_sync)) {
+    SetGLError(GL_INVALID_OPERATION, "glGenSyncTokenCHROMIUM",
+               "fence sync must be flushed before generating sync token");
+    return;
+  }
+
+  SyncToken* sync_token_data = reinterpret_cast<SyncToken*>(sync_token);
+  memset(sync_token_data, 0, sizeof(SyncToken));
+
+  sync_token_data->namespace_id = gpu_control_->GetNamespaceID();
+  sync_token_data->command_buffer_id = gpu_control_->GetCommandBufferID();
+  sync_token_data->release_count = fence_sync;
+}
+
+void GLES2Implementation::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+  if (!sync_token) {
+    SetGLError(GL_INVALID_VALUE, "glWaitSyncTokenCHROMIUM", "empty sync_token");
+    return;
+  };
+
+  const SyncToken* sync_token_data =
+      reinterpret_cast<const SyncToken*>(sync_token);
+  helper_->WaitSyncTokenCHROMIUM(sync_token_data->namespace_id,
+                                 sync_token_data->command_buffer_id,
+                                 sync_token_data->release_count);
+}
+
 namespace {
 
 bool ValidImageFormat(GLenum internalformat,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 305f971..3d551a9 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1027,6 +1027,12 @@
 
 void WaitSyncPointCHROMIUM(GLuint sync_point) override;
 
+GLuint64 InsertFenceSyncCHROMIUM() override;
+
+void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+
+void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
+
 void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
 
 void DiscardBackbufferCHROMIUM() override;
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 35218c3..d53e647 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3741,6 +3741,84 @@
   EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
 }
 
+TEST_F(GLES2ImplementationTest, InsertFenceSyncCHROMIUM) {
+  const GLuint64 kFenceSync = 123u;
+  EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease())
+      .WillOnce(testing::Return(kFenceSync));
+
+  struct Cmds {
+    cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+  };
+  Cmds expected;
+  expected.insert_fence_sync.Init(kFenceSync);
+
+  const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
+  EXPECT_EQ(kFenceSync, fence_sync);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest, GenSyncTokenCHROMIUM) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const GLuint64 kCommandBufferId = 234u;
+  const GLuint64 kFenceSync = 123u;
+  GLbyte sync_token[GL_SYNC_TOKEN_SIZE_CHROMIUM];
+
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillRepeatedly(testing::Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillRepeatedly(testing::Return(kCommandBufferId));
+
+  gl_->GenSyncTokenCHROMIUM(kFenceSync, nullptr);
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+
+  EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync))
+      .WillOnce(testing::Return(false));
+  gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token);
+  EXPECT_EQ(GL_INVALID_VALUE, CheckError());
+
+  EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync))
+      .WillOnce(testing::Return(false));
+  gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token);
+  EXPECT_EQ(GL_INVALID_OPERATION, CheckError());
+
+  EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync))
+      .WillOnce(testing::Return(true));
+  ClearCommands();
+  gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token);
+  EXPECT_TRUE(NoCommandsWritten());
+  EXPECT_EQ(GL_NO_ERROR, CheckError());
+}
+
+TEST_F(GLES2ImplementationTest, WaitSyncTokenCHROMIUM) {
+  const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
+  const GLuint64 kCommandBufferId = 234u;
+  const GLuint64 kFenceSync = 456u;
+  GLbyte sync_token[GL_SYNC_TOKEN_SIZE_CHROMIUM];
+
+  EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync))
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(*gpu_control_, GetNamespaceID())
+      .WillOnce(testing::Return(kNamespaceId));
+  EXPECT_CALL(*gpu_control_, GetCommandBufferID())
+      .WillOnce(testing::Return(kCommandBufferId));
+  gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token);
+
+  struct Cmds {
+    cmds::WaitSyncTokenCHROMIUM wait_sync_token;
+  };
+  Cmds expected;
+  expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId, kFenceSync);
+
+  gl_->WaitSyncTokenCHROMIUM(sync_token);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
 TEST_F(GLES2ImplementationTest, IsEnabled) {
   // If we use a valid enum, its state is cached on client side, so no command
   // is actually generated, and this test will fail.
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 949ec7e..3f96294 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -3093,6 +3093,8 @@
   gl_->WaitSyncPointCHROMIUM(1);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
+// TODO(zmo): Implement unit test for InsertFenceSyncCHROMIUM
+// TODO(zmo): Implement unit test for GenSyncTokenCHROMIUM
 
 TEST_F(GLES2ImplementationTest, DrawBuffersEXT) {
   GLenum data[1][1] = {{0}};
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 43e3b905..9b97beb 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -762,6 +762,9 @@
 virtual void LoseContextCHROMIUM(GLenum current, GLenum other) = 0;
 virtual GLuint InsertSyncPointCHROMIUM() = 0;
 virtual void WaitSyncPointCHROMIUM(GLuint sync_point) = 0;
+virtual GLuint64 InsertFenceSyncCHROMIUM() = 0;
+virtual void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) = 0;
+virtual void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) = 0;
 virtual void DrawBuffersEXT(GLsizei count, const GLenum* bufs) = 0;
 virtual void DiscardBackbufferCHROMIUM() = 0;
 virtual void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 0d08d67..e795738b 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -738,6 +738,9 @@
 void LoseContextCHROMIUM(GLenum current, GLenum other) override;
 GLuint InsertSyncPointCHROMIUM() override;
 void WaitSyncPointCHROMIUM(GLuint sync_point) override;
+GLuint64 InsertFenceSyncCHROMIUM() override;
+void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
 void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
 void DiscardBackbufferCHROMIUM() override;
 void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 6e30251..e7511e4 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1011,6 +1011,13 @@
   return 0;
 }
 void GLES2InterfaceStub::WaitSyncPointCHROMIUM(GLuint /* sync_point */) {}
+GLuint64 GLES2InterfaceStub::InsertFenceSyncCHROMIUM() {
+  return 0;
+}
+void GLES2InterfaceStub::GenSyncTokenCHROMIUM(GLuint64 /* fence_sync */,
+                                              GLbyte* /* sync_token */) {}
+void GLES2InterfaceStub::WaitSyncTokenCHROMIUM(const GLbyte* /* sync_token */) {
+}
 void GLES2InterfaceStub::DrawBuffersEXT(GLsizei /* count */,
                                         const GLenum* /* bufs */) {}
 void GLES2InterfaceStub::DiscardBackbufferCHROMIUM() {}
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 41140beb..f9580d0c 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -738,6 +738,9 @@
 void LoseContextCHROMIUM(GLenum current, GLenum other) override;
 GLuint InsertSyncPointCHROMIUM() override;
 void WaitSyncPointCHROMIUM(GLuint sync_point) override;
+GLuint64 InsertFenceSyncCHROMIUM() override;
+void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
 void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
 void DiscardBackbufferCHROMIUM() override;
 void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 11d76df9..86c547d 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2160,6 +2160,22 @@
   gl_->WaitSyncPointCHROMIUM(sync_point);
 }
 
+GLuint64 GLES2TraceImplementation::InsertFenceSyncCHROMIUM() {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::InsertFenceSyncCHROMIUM");
+  return gl_->InsertFenceSyncCHROMIUM();
+}
+
+void GLES2TraceImplementation::GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                                    GLbyte* sync_token) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GenSyncTokenCHROMIUM");
+  gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+
+void GLES2TraceImplementation::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::WaitSyncTokenCHROMIUM");
+  gl_->WaitSyncTokenCHROMIUM(sync_token);
+}
+
 void GLES2TraceImplementation::DrawBuffersEXT(GLsizei count,
                                               const GLenum* bufs) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::DrawBuffersEXT");
diff --git a/gpu/command_buffer/client/gpu_control.h b/gpu/command_buffer/client/gpu_control.h
index 7065d1ed..7000152 100644
--- a/gpu/command_buffer/client/gpu_control.h
+++ b/gpu/command_buffer/client/gpu_control.h
@@ -96,6 +96,14 @@
   virtual CommandBufferNamespace GetNamespaceID() const = 0;
   virtual uint64_t GetCommandBufferID() const = 0;
 
+  // Fence Syncs use release counters at a context level, these fence syncs
+  // need to be flushed before they can be shared with other contexts across
+  // channels. Subclasses should implement these functions and take care of
+  // figuring out when a fence sync has been flushed.
+  virtual uint64_t GenerateFenceSyncRelease() = 0;
+  virtual bool IsFenceSyncRelease(uint64_t release) = 0;
+  virtual bool IsFenceSyncFlushed(uint64_t release) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(GpuControl);
 };
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 9eda893e..9e97d6e 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -307,6 +307,9 @@
 GL_APICALL void         GL_APIENTRY glLoseContextCHROMIUM (GLenumResetStatus current, GLenumResetStatus other);
 GL_APICALL GLuint       GL_APIENTRY glInsertSyncPointCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glWaitSyncPointCHROMIUM (GLuint sync_point);
+GL_APICALL GLuint64     GL_APIENTRY glInsertFenceSyncCHROMIUM (void);
+GL_APICALL void         GL_APIENTRY glGenSyncTokenCHROMIUM (GLuint64 fence_sync, GLbyte* sync_token);
+GL_APICALL void         GL_APIENTRY glWaitSyncTokenCHROMIUM (const GLbyte* sync_token);
 GL_APICALL void         GL_APIENTRY glDrawBuffersEXT (GLsizei count, const GLenum* bufs);
 GL_APICALL void         GL_APIENTRY glDiscardBackbufferCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glScheduleOverlayPlaneCHROMIUM (GLint plane_z_order, GLenum plane_transform, GLuint overlay_texture_id, GLint bounds_x, GLint bounds_y, GLint bounds_width, GLint bounds_height, GLfloat uv_x, GLfloat uv_y, GLfloat uv_width, GLfloat uv_height);
diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h
index e14c06d..0d3180140 100644
--- a/gpu/command_buffer/common/constants.h
+++ b/gpu/command_buffer/common/constants.h
@@ -8,6 +8,11 @@
 #include <stddef.h>
 #include <stdint.h>
 
+// From glextchromium.h.
+#ifndef GL_SYNC_TOKEN_SIZE_CHROMIUM
+#define GL_SYNC_TOKEN_SIZE_CHROMIUM 24
+#endif
+
 namespace gpu {
 
 typedef int32_t CommandBufferOffset;
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index e4d00dd4..09b87a9 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "gpu/command_buffer/common/bitfield_helpers.h"
 #include "gpu/command_buffer/common/cmd_buffer_common.h"
+#include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/common/gles2_cmd_ids.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 
@@ -202,6 +203,23 @@
   // UniformES3Info uniforms[num_uniforms];
 };
 
+// The format of fence sync tokens.
+struct SyncToken {
+  CommandBufferNamespace namespace_id;
+  uint64_t command_buffer_id;
+  uint64_t release_count;
+
+  bool operator<(const SyncToken& other) const {
+    // TODO(dyen): Once all our compilers support c++11, we can replace this
+    // long list of comparisons with std::tie().
+    return (namespace_id < other.namespace_id) ||
+           ((namespace_id == other.namespace_id) &&
+            ((command_buffer_id < other.command_buffer_id) ||
+             ((command_buffer_id == other.command_buffer_id) &&
+              (release_count < other.release_count))));
+  }
+};
+
 // The format of QuerySync used by EXT_occlusion_query_boolean
 struct QuerySync {
   void Reset() {
@@ -296,6 +314,9 @@
 static_assert(offsetof(UniformBlocksHeader, num_uniform_blocks) == 0,
               "offset of UniformBlocksHeader.num_uniform_blocks should be 0");
 
+static_assert(sizeof(SyncToken) <= GL_SYNC_TOKEN_SIZE_CHROMIUM,
+              "size of SyncToken must not exceed GL_SYNC_TOKEN_SIZE_CHROMIUM");
+
 namespace cmds {
 
 #include "../common/gles2_cmd_format_autogen.h"
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index 3f288cb3..7dea6b5 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -13468,6 +13468,124 @@
 static_assert(offsetof(WaitSyncPointCHROMIUM, sync_point) == 4,
               "offset of WaitSyncPointCHROMIUM sync_point should be 4");
 
+struct InsertFenceSyncCHROMIUM {
+  typedef InsertFenceSyncCHROMIUM ValueType;
+  static const CommandId kCmdId = kInsertFenceSyncCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(1);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint64 _release_count) {
+    SetHeader();
+    release_count = _release_count;
+  }
+
+  void* Set(void* cmd, GLuint64 _release_count) {
+    static_cast<ValueType*>(cmd)->Init(_release_count);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t release_count;
+};
+
+static_assert(sizeof(InsertFenceSyncCHROMIUM) == 8,
+              "size of InsertFenceSyncCHROMIUM should be 8");
+static_assert(offsetof(InsertFenceSyncCHROMIUM, header) == 0,
+              "offset of InsertFenceSyncCHROMIUM header should be 0");
+static_assert(offsetof(InsertFenceSyncCHROMIUM, release_count) == 4,
+              "offset of InsertFenceSyncCHROMIUM release_count should be 4");
+
+struct GenSyncTokenCHROMIUMImmediate {
+  typedef GenSyncTokenCHROMIUMImmediate ValueType;
+  static const CommandId kCmdId = kGenSyncTokenCHROMIUMImmediate;
+  static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize(uint32_t size_in_bytes) {
+    return static_cast<uint32_t>(sizeof(ValueType) +  // NOLINT
+                                 RoundSizeToMultipleOfEntries(size_in_bytes));
+  }
+
+  void SetHeader(uint32_t size_in_bytes) {
+    header.SetCmdByTotalSize<ValueType>(size_in_bytes);
+  }
+
+  void Init(GLuint64 _fence_sync) {
+    uint32_t total_size = 0;  // TODO(gman): get correct size.
+    SetHeader(total_size);
+    fence_sync = _fence_sync;
+  }
+
+  void* Set(void* cmd, GLuint64 _fence_sync) {
+    uint32_t total_size = 0;  // TODO(gman): get correct size.
+    static_cast<ValueType*>(cmd)->Init(_fence_sync);
+    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, total_size);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t fence_sync;
+};
+
+static_assert(sizeof(GenSyncTokenCHROMIUMImmediate) == 8,
+              "size of GenSyncTokenCHROMIUMImmediate should be 8");
+static_assert(offsetof(GenSyncTokenCHROMIUMImmediate, header) == 0,
+              "offset of GenSyncTokenCHROMIUMImmediate header should be 0");
+static_assert(offsetof(GenSyncTokenCHROMIUMImmediate, fence_sync) == 4,
+              "offset of GenSyncTokenCHROMIUMImmediate fence_sync should be 4");
+
+struct WaitSyncTokenCHROMIUM {
+  typedef WaitSyncTokenCHROMIUM ValueType;
+  static const CommandId kCmdId = kWaitSyncTokenCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    SetHeader();
+    namespace_id = _namespace_id;
+    command_buffer_id = _command_buffer_id;
+    release_count = _release_count;
+  }
+
+  void* Set(void* cmd,
+            GLuint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    static_cast<ValueType*>(cmd)
+        ->Init(_namespace_id, _command_buffer_id, _release_count);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t namespace_id;
+  uint32_t command_buffer_id;
+  uint32_t release_count;
+};
+
+static_assert(sizeof(WaitSyncTokenCHROMIUM) == 16,
+              "size of WaitSyncTokenCHROMIUM should be 16");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, header) == 0,
+              "offset of WaitSyncTokenCHROMIUM header should be 0");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, namespace_id) == 4,
+              "offset of WaitSyncTokenCHROMIUM namespace_id should be 4");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, command_buffer_id) == 8,
+              "offset of WaitSyncTokenCHROMIUM command_buffer_id should be 8");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, release_count) == 12,
+              "offset of WaitSyncTokenCHROMIUM release_count should be 12");
+
 struct DrawBuffersEXTImmediate {
   typedef DrawBuffersEXTImmediate ValueType;
   static const CommandId kCmdId = kDrawBuffersEXTImmediate;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 62bff78..e1321cf 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -4768,6 +4768,32 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, InsertFenceSyncCHROMIUM) {
+  cmds::InsertFenceSyncCHROMIUM& cmd =
+      *GetBufferAs<cmds::InsertFenceSyncCHROMIUM>();
+  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint64>(11));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::InsertFenceSyncCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint64>(11), cmd.release_count);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(GLES2FormatTest, WaitSyncTokenCHROMIUM) {
+  cmds::WaitSyncTokenCHROMIUM& cmd =
+      *GetBufferAs<cmds::WaitSyncTokenCHROMIUM>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLuint64>(12),
+              static_cast<GLuint64>(13));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::WaitSyncTokenCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.namespace_id);
+  EXPECT_EQ(static_cast<GLuint64>(12), cmd.command_buffer_id);
+  EXPECT_EQ(static_cast<GLuint64>(13), cmd.release_count);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, DrawBuffersEXTImmediate) {
   const int kSomeBaseValueToTestWith = 51;
   static GLenum data[] = {
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 89514409..39e1705 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -296,28 +296,31 @@
   OP(LoseContextCHROMIUM)                      /* 537 */ \
   OP(InsertSyncPointCHROMIUM)                  /* 538 */ \
   OP(WaitSyncPointCHROMIUM)                    /* 539 */ \
-  OP(DrawBuffersEXTImmediate)                  /* 540 */ \
-  OP(DiscardBackbufferCHROMIUM)                /* 541 */ \
-  OP(ScheduleOverlayPlaneCHROMIUM)             /* 542 */ \
-  OP(SwapInterval)                             /* 543 */ \
-  OP(FlushDriverCachesCHROMIUM)                /* 544 */ \
-  OP(MatrixLoadfCHROMIUMImmediate)             /* 545 */ \
-  OP(MatrixLoadIdentityCHROMIUM)               /* 546 */ \
-  OP(GenPathsCHROMIUM)                         /* 547 */ \
-  OP(DeletePathsCHROMIUM)                      /* 548 */ \
-  OP(IsPathCHROMIUM)                           /* 549 */ \
-  OP(PathCommandsCHROMIUM)                     /* 550 */ \
-  OP(PathParameterfCHROMIUM)                   /* 551 */ \
-  OP(PathParameteriCHROMIUM)                   /* 552 */ \
-  OP(PathStencilFuncCHROMIUM)                  /* 553 */ \
-  OP(StencilFillPathCHROMIUM)                  /* 554 */ \
-  OP(StencilStrokePathCHROMIUM)                /* 555 */ \
-  OP(CoverFillPathCHROMIUM)                    /* 556 */ \
-  OP(CoverStrokePathCHROMIUM)                  /* 557 */ \
-  OP(StencilThenCoverFillPathCHROMIUM)         /* 558 */ \
-  OP(StencilThenCoverStrokePathCHROMIUM)       /* 559 */ \
-  OP(BlendBarrierKHR)                          /* 560 */ \
-  OP(ApplyScreenSpaceAntialiasingCHROMIUM)     /* 561 */
+  OP(InsertFenceSyncCHROMIUM)                  /* 540 */ \
+  OP(GenSyncTokenCHROMIUMImmediate)            /* 541 */ \
+  OP(WaitSyncTokenCHROMIUM)                    /* 542 */ \
+  OP(DrawBuffersEXTImmediate)                  /* 543 */ \
+  OP(DiscardBackbufferCHROMIUM)                /* 544 */ \
+  OP(ScheduleOverlayPlaneCHROMIUM)             /* 545 */ \
+  OP(SwapInterval)                             /* 546 */ \
+  OP(FlushDriverCachesCHROMIUM)                /* 547 */ \
+  OP(MatrixLoadfCHROMIUMImmediate)             /* 548 */ \
+  OP(MatrixLoadIdentityCHROMIUM)               /* 549 */ \
+  OP(GenPathsCHROMIUM)                         /* 550 */ \
+  OP(DeletePathsCHROMIUM)                      /* 551 */ \
+  OP(IsPathCHROMIUM)                           /* 552 */ \
+  OP(PathCommandsCHROMIUM)                     /* 553 */ \
+  OP(PathParameterfCHROMIUM)                   /* 554 */ \
+  OP(PathParameteriCHROMIUM)                   /* 555 */ \
+  OP(PathStencilFuncCHROMIUM)                  /* 556 */ \
+  OP(StencilFillPathCHROMIUM)                  /* 557 */ \
+  OP(StencilStrokePathCHROMIUM)                /* 558 */ \
+  OP(CoverFillPathCHROMIUM)                    /* 559 */ \
+  OP(CoverStrokePathCHROMIUM)                  /* 560 */ \
+  OP(StencilThenCoverFillPathCHROMIUM)         /* 561 */ \
+  OP(StencilThenCoverStrokePathCHROMIUM)       /* 562 */ \
+  OP(BlendBarrierKHR)                          /* 563 */ \
+  OP(ApplyScreenSpaceAntialiasingCHROMIUM)     /* 564 */
 
 enum CommandId {
   kStartPoint = cmd::kLastCommonId,  // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index 99f7837..af2b10e 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -1900,6 +1900,9 @@
         0x8CD9, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
     },
     {
+        24, "GL_SYNC_TOKEN_SIZE_CHROMIUM",
+    },
+    {
         0x84CC, "GL_TEXTURE12",
     },
     {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index fc12bfe..a76f7938 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -688,6 +688,9 @@
 
   void SetShaderCacheCallback(const ShaderCacheCallback& callback) override;
   void SetWaitSyncPointCallback(const WaitSyncPointCallback& callback) override;
+  void SetFenceSyncReleaseCallback(
+      const FenceSyncReleaseCallback& callback) override;
+  void SetWaitFenceSyncCallback(const WaitFenceSyncCallback& callback) override;
 
   void SetIgnoreCachedStateForTest(bool ignore) override;
   void ProcessFinishedAsyncTransfers();
@@ -1989,6 +1992,8 @@
   base::Callback<void(gfx::Size, float)> resize_callback_;
 
   WaitSyncPointCallback wait_sync_point_callback_;
+  FenceSyncReleaseCallback fence_sync_release_callback_;
+  WaitFenceSyncCallback wait_fence_sync_callback_;
 
   ShaderCacheCallback shader_cache_callback_;
 
@@ -3948,6 +3953,16 @@
   wait_sync_point_callback_ = callback;
 }
 
+void GLES2DecoderImpl::SetFenceSyncReleaseCallback(
+    const FenceSyncReleaseCallback& callback) {
+  fence_sync_release_callback_ = callback;
+}
+
+void GLES2DecoderImpl::SetWaitFenceSyncCallback(
+    const WaitFenceSyncCallback& callback) {
+  wait_fence_sync_callback_ = callback;
+}
+
 bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id,
                                            uint32* service_texture_id) {
   TextureRef* texture_ref = texture_manager()->GetTexture(client_texture_id);
@@ -7886,12 +7901,9 @@
     const void* cmd_data) {
   const gles2::cmds::DrawArraysInstancedANGLE& c =
       *static_cast<const gles2::cmds::DrawArraysInstancedANGLE*>(cmd_data);
-  if (!features().angle_instanced_arrays) {
-    LOCAL_SET_GL_ERROR(
-        GL_INVALID_OPERATION,
-        "glDrawArraysInstancedANGLE", "function not available");
-    return error::kNoError;
-  }
+  if (!features().angle_instanced_arrays)
+    return error::kUnknownCommand;
+
   return DoDrawArrays("glDrawArraysIntancedANGLE",
                       true,
                       static_cast<GLenum>(c.mode),
@@ -8034,12 +8046,9 @@
     const void* cmd_data) {
   const gles2::cmds::DrawElementsInstancedANGLE& c =
       *static_cast<const gles2::cmds::DrawElementsInstancedANGLE*>(cmd_data);
-  if (!features().angle_instanced_arrays) {
-    LOCAL_SET_GL_ERROR(
-        GL_INVALID_OPERATION,
-        "glDrawElementsInstancedANGLE", "function not available");
-    return error::kNoError;
-  }
+  if (!features().angle_instanced_arrays)
+    return error::kUnknownCommand;
+
   return DoDrawElements("glDrawElementsInstancedANGLE",
                         true,
                         static_cast<GLenum>(c.mode),
@@ -8732,12 +8741,9 @@
     const void* cmd_data) {
   const gles2::cmds::VertexAttribDivisorANGLE& c =
       *static_cast<const gles2::cmds::VertexAttribDivisorANGLE*>(cmd_data);
-  if (!features().angle_instanced_arrays) {
-    LOCAL_SET_GL_ERROR(
-        GL_INVALID_OPERATION,
-        "glVertexAttribDivisorANGLE", "function not available");
-    return error::kNoError;
-  }
+  if (!features().angle_instanced_arrays)
+    return error::kUnknownCommand;
+
   GLuint index = c.index;
   GLuint divisor = c.divisor;
   if (index >= group_->max_vertex_attribs()) {
@@ -9271,10 +9277,9 @@
   if (!location) {
     return error::kOutOfBounds;
   }
-  // Require the client to init this incase the context is lost and we are no
-  // longer executing commands.
+  // Check that the client initialized the result.
   if (*location != -1) {
-    return error::kGenericError;
+    return error::kInvalidArguments;
   }
   *location = program->GetAttribLocation(name_str);
   return error::kNoError;
@@ -9320,10 +9325,9 @@
   if (!location) {
     return error::kOutOfBounds;
   }
-  // Require the client to init this incase the context is lost an we are no
-  // longer executing commands.
+  // Check that the client initialized the result.
   if (*location != -1) {
-    return error::kGenericError;
+    return error::kInvalidArguments;
   }
   *location = program->GetUniformFakeLocation(name_str);
   return error::kNoError;
@@ -9406,10 +9410,9 @@
   if (!location) {
     return error::kOutOfBounds;
   }
-  // Require the client to init this incase the context is lost and we are no
-  // longer executing commands.
+  // Check that the client initialized the result.
   if (*location != -1) {
-    return error::kGenericError;
+    return error::kInvalidArguments;
   }
   Program* program = GetProgramInfoNotShader(
       client_id, "glGetFragDataLocation");
@@ -9458,10 +9461,9 @@
   if (!index) {
     return error::kOutOfBounds;
   }
-  // Require the client to init this in case the context is lost and we are no
-  // longer executing commands.
+  // Check that the client initialized the result.
   if (*index != GL_INVALID_INDEX) {
-    return error::kGenericError;
+    return error::kInvalidArguments;
   }
   Program* program = GetProgramInfoNotShader(
       c.program, "glGetUniformBlockIndex");
@@ -12242,6 +12244,42 @@
       error::kNoError : error::kDeferCommandUntilLater;
 }
 
+error::Error GLES2DecoderImpl::HandleInsertFenceSyncCHROMIUM(
+    uint32 immediate_data_size,
+    const void* cmd_data) {
+  const gles2::cmds::InsertFenceSyncCHROMIUM& c =
+      *static_cast<const gles2::cmds::InsertFenceSyncCHROMIUM*>(cmd_data);
+
+  const uint64_t release_count = c.release_count;
+  if (!fence_sync_release_callback_.is_null())
+    fence_sync_release_callback_.Run(release_count);
+  return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleGenSyncTokenCHROMIUMImmediate(
+    uint32 immediate_data_size,
+    const void* cmd_data) {
+  return error::kUnknownCommand;
+}
+
+error::Error GLES2DecoderImpl::HandleWaitSyncTokenCHROMIUM(
+    uint32 immediate_data_size,
+    const void* cmd_data) {
+  const gles2::cmds::WaitSyncTokenCHROMIUM& c =
+      *static_cast<const gles2::cmds::WaitSyncTokenCHROMIUM*>(cmd_data);
+
+  const gpu::CommandBufferNamespace namespace_id =
+      static_cast<gpu::CommandBufferNamespace>(c.namespace_id);
+  const uint64_t command_buffer_id = c.command_buffer_id;
+  const uint64_t release = c.release_count;
+  if (wait_fence_sync_callback_.is_null())
+    return error::kNoError;
+
+  return wait_fence_sync_callback_.Run(namespace_id, command_buffer_id, release)
+             ? error::kNoError
+             : error::kDeferCommandUntilLater;
+}
+
 error::Error GLES2DecoderImpl::HandleDiscardBackbufferCHROMIUM(
     uint32 immediate_data_size,
     const void* cmd_data) {
@@ -14610,11 +14648,8 @@
   static const char kFunctionName[] = "glGenPathsCHROMIUM";
   const gles2::cmds::GenPathsCHROMIUM& c =
       *static_cast<const gles2::cmds::GenPathsCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
 
   GLsizei range = static_cast<GLsizei>(c.range);
   if (range < 0) {
@@ -14640,11 +14675,8 @@
   static const char kFunctionName[] = "glDeletePathsCHROMIUM";
   const gles2::cmds::DeletePathsCHROMIUM& c =
       *static_cast<const gles2::cmds::DeletePathsCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
 
   GLsizei range = static_cast<GLsizei>(c.range);
   if (range < 0) {
@@ -14670,11 +14702,8 @@
   static const char kFunctionName[] = "glPathCommandsCHROMIUM";
   const gles2::cmds::PathCommandsCHROMIUM& c =
       *static_cast<const gles2::cmds::PathCommandsCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
 
   GLuint service_id = 0;
   if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
@@ -14778,11 +14807,9 @@
   static const char kFunctionName[] = "glPathParameterfCHROMIUM";
   const gles2::cmds::PathParameterfCHROMIUM& c =
       *static_cast<const gles2::cmds::PathParameterfCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLuint service_id = 0;
   if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
@@ -14832,11 +14859,9 @@
   static const char kFunctionName[] = "glPathParameteriCHROMIUM";
   const gles2::cmds::PathParameteriCHROMIUM& c =
       *static_cast<const gles2::cmds::PathParameteriCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLuint service_id = 0;
   if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
@@ -14884,11 +14909,9 @@
   static const char kFunctionName[] = "glStencilFillPathCHROMIUM";
   const gles2::cmds::StencilFillPathCHROMIUM& c =
       *static_cast<const gles2::cmds::StencilFillPathCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLenum fill_mode = static_cast<GLenum>(c.fillMode);
   if (!validators_->path_fill_mode.IsValid(fill_mode)) {
     LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, fill_mode, "fillMode");
@@ -14917,14 +14940,11 @@
 error::Error GLES2DecoderImpl::HandleStencilStrokePathCHROMIUM(
     uint32 immediate_data_size,
     const void* cmd_data) {
-  static const char kFunctionName[] = "glStencilStrokePathCHROMIUM";
   const gles2::cmds::StencilStrokePathCHROMIUM& c =
       *static_cast<const gles2::cmds::StencilStrokePathCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLuint service_id = 0;
   if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
     return error::kNoError;
@@ -14942,11 +14962,9 @@
   static const char kFunctionName[] = "glCoverFillPathCHROMIUM";
   const gles2::cmds::CoverFillPathCHROMIUM& c =
       *static_cast<const gles2::cmds::CoverFillPathCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLenum cover_mode = static_cast<GLenum>(c.coverMode);
   if (!validators_->path_cover_mode.IsValid(cover_mode)) {
     LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
@@ -14967,11 +14985,9 @@
   static const char kFunctionName[] = "glCoverStrokePathCHROMIUM";
   const gles2::cmds::CoverStrokePathCHROMIUM& c =
       *static_cast<const gles2::cmds::CoverStrokePathCHROMIUM*>(cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLenum cover_mode = static_cast<GLenum>(c.coverMode);
   if (!validators_->path_cover_mode.IsValid(cover_mode)) {
     LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
@@ -14993,11 +15009,9 @@
   const gles2::cmds::StencilThenCoverFillPathCHROMIUM& c =
       *static_cast<const gles2::cmds::StencilThenCoverFillPathCHROMIUM*>(
           cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLenum fill_mode = static_cast<GLenum>(c.fillMode);
   if (!validators_->path_fill_mode.IsValid(fill_mode)) {
     LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, fill_mode, "fillMode");
@@ -15032,11 +15046,9 @@
   const gles2::cmds::StencilThenCoverStrokePathCHROMIUM& c =
       *static_cast<const gles2::cmds::StencilThenCoverStrokePathCHROMIUM*>(
           cmd_data);
-  if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                       "function not available");
-    return error::kNoError;
-  }
+  if (!features().chromium_path_rendering)
+    return error::kUnknownCommand;
+
   GLenum cover_mode = static_cast<GLenum>(c.coverMode);
   if (!validators_->path_cover_mode.IsValid(cover_mode)) {
     LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 0fb4882..60ca384 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -65,6 +65,10 @@
  public:
   typedef error::Error Error;
   typedef base::Callback<bool(uint32 id)> WaitSyncPointCallback;
+  typedef base::Callback<void(uint64_t release)> FenceSyncReleaseCallback;
+  typedef base::Callback<bool(gpu::CommandBufferNamespace namespace_id,
+                              uint64_t command_buffer_id,
+                              uint64_t release)> WaitFenceSyncCallback;
 
   // The default stencil mask, which has all bits set.  This really should be a
   // GLuint, but we can't #include gl_bindings.h in this file without causing
@@ -234,6 +238,13 @@
   virtual void SetWaitSyncPointCallback(
       const WaitSyncPointCallback& callback) = 0;
 
+  // Sets the callback for fence sync release and wait calls. The wait call
+  // returns true if the channel is still scheduled.
+  virtual void SetFenceSyncReleaseCallback(
+      const FenceSyncReleaseCallback& callback) = 0;
+  virtual void SetWaitFenceSyncCallback(
+      const WaitFenceSyncCallback& callback) = 0;
+
   virtual void WaitForReadPixels(base::Closure callback) = 0;
   virtual uint32 GetTextureUploadCount() = 0;
   virtual base::TimeDelta GetTotalTextureUploadTime() = 0;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 303b7021..de3b3443 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -3984,9 +3984,7 @@
       *static_cast<const gles2::cmds::BlitFramebufferCHROMIUM*>(cmd_data);
   (void)c;
   if (!features().chromium_framebuffer_multisample) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBlitFramebufferCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   error::Error error;
@@ -4024,10 +4022,7 @@
           cmd_data);
   (void)c;
   if (!features().chromium_framebuffer_multisample) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glRenderbufferStorageMultisampleCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum target = static_cast<GLenum>(c.target);
@@ -4075,10 +4070,7 @@
           cmd_data);
   (void)c;
   if (!features().multisampled_render_to_texture) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glRenderbufferStorageMultisampleEXT",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum target = static_cast<GLenum>(c.target);
@@ -4124,10 +4116,7 @@
           cmd_data);
   (void)c;
   if (!features().multisampled_render_to_texture) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glFramebufferTexture2DMultisampleEXT",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum target = static_cast<GLenum>(c.target);
@@ -4846,9 +4835,7 @@
           cmd_data);
   (void)c;
   if (!features().ext_discard_framebuffer) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glDiscardFramebufferEXT",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum target = static_cast<GLenum>(c.target);
@@ -4949,9 +4936,7 @@
       *static_cast<const gles2::cmds::MatrixLoadfCHROMIUMImmediate*>(cmd_data);
   (void)c;
   if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glMatrixLoadfCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum matrixMode = static_cast<GLenum>(c.matrixMode);
@@ -4983,9 +4968,7 @@
       *static_cast<const gles2::cmds::MatrixLoadIdentityCHROMIUM*>(cmd_data);
   (void)c;
   if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glMatrixLoadIdentityCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum matrixMode = static_cast<GLenum>(c.matrixMode);
@@ -5005,9 +4988,7 @@
       *static_cast<const gles2::cmds::IsPathCHROMIUM*>(cmd_data);
   (void)c;
   if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glIsPathCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLuint path = c.path;
@@ -5028,9 +5009,7 @@
       *static_cast<const gles2::cmds::PathStencilFuncCHROMIUM*>(cmd_data);
   (void)c;
   if (!features().chromium_path_rendering) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glPathStencilFuncCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   GLenum func = static_cast<GLenum>(c.func);
@@ -5057,9 +5036,7 @@
       *static_cast<const gles2::cmds::BlendBarrierKHR*>(cmd_data);
   (void)c;
   if (!features().blend_equation_advanced) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBlendBarrierKHR",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   glBlendBarrierKHR();
@@ -5074,10 +5051,7 @@
           cmd_data);
   (void)c;
   if (!features().chromium_screen_space_antialiasing) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glApplyScreenSpaceAntialiasingCHROMIUM",
-                       "function not available");
-    return error::kNoError;
+    return error::kUnknownCommand;
   }
 
   DoApplyScreenSpaceAntialiasingCHROMIUM();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 62e1c9d4..590c27a5f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -115,6 +115,10 @@
                void(const ShaderCacheCallback& callback));
   MOCK_METHOD1(SetWaitSyncPointCallback,
                void(const WaitSyncPointCallback& callback));
+  MOCK_METHOD1(SetFenceSyncReleaseCallback,
+               void(const FenceSyncReleaseCallback& callback));
+  MOCK_METHOD1(SetWaitFenceSyncCallback,
+               void(const WaitFenceSyncCallback& callback));
   MOCK_METHOD1(WaitForReadPixels,
                void(base::Closure callback));
   MOCK_METHOD0(GetTextureUploadCount, uint32());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
index 4c7afcc..691c9dcc 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
@@ -461,6 +461,12 @@
 
 // TODO(gman): WaitSyncPointCHROMIUM
 
+// TODO(gman): InsertFenceSyncCHROMIUM
+
+// TODO(gman): GenSyncTokenCHROMIUMImmediate
+
+// TODO(gman): WaitSyncTokenCHROMIUM
+
 // TODO(gman): DrawBuffersEXTImmediate
 // TODO(gman): DiscardBackbufferCHROMIUM
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
index b2147cc..fefc36b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
@@ -938,8 +938,7 @@
       .RetiresOnSaturation();
   DrawArraysInstancedANGLE cmd;
   cmd.Init(GL_TRIANGLES, 0, kNumVertices, 1);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
 }
 
 TEST_P(GLES2DecoderWithShaderTest, VertexAttribDivisorANGLEFails) {
@@ -954,8 +953,7 @@
 
   VertexAttribDivisorANGLE cmd;
   cmd.Init(0, 1);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
 }
 
 TEST_P(GLES2DecoderGeometryInstancingTest,
@@ -1428,8 +1426,7 @@
            GL_UNSIGNED_SHORT,
            kValidIndexRangeStart * 2,
            1);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
 }
 
 TEST_P(GLES2DecoderGeometryInstancingTest,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
index 916f2aa..dbb9100f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
@@ -39,92 +39,77 @@
         0,
     };
     cmd.Init(GL_PATH_MODELVIEW_CHROMIUM, temp);
-    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteImmediateCmd(cmd, sizeof(temp)));
   }
   {
     cmds::MatrixLoadIdentityCHROMIUM cmd;
     cmd.Init(GL_PATH_PROJECTION_CHROMIUM);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::GenPathsCHROMIUM cmd;
     cmd.Init(0, 0);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::DeletePathsCHROMIUM cmd;
     cmd.Init(0, 0);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::IsPathCHROMIUM cmd;
     cmd.Init(kClientPathId, shared_memory_id_, shared_memory_offset_);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::PathCommandsCHROMIUM cmd;
     cmd.Init(kClientPathId, 0, 0, 0, 0, GL_FLOAT, 0, 0);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::PathParameterfCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1.0f);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::PathParameteriCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::PathStencilFuncCHROMIUM cmd;
     cmd.Init(GL_NEVER, 2, 3);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::StencilFillPathCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::StencilStrokePathCHROMIUM cmd;
     cmd.Init(kClientPathId, 1, 2);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::CoverFillPathCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::CoverStrokePathCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::StencilThenCoverFillPathCHROMIUM cmd;
     cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1, GL_BOUNDING_BOX_CHROMIUM);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
   {
     cmds::StencilThenCoverStrokePathCHROMIUM cmd;
     cmd.Init(kClientPathId, 1, 2, GL_BOUNDING_BOX_CHROMIUM);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
 }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
index 7e086fd..5c256709 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -1604,8 +1604,7 @@
            GL_RGBA4,
            TestHelper::kMaxRenderbufferSize,
            1);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
 }
 
 class GLES2DecoderMultisampledRenderToTextureTest
@@ -1620,8 +1619,7 @@
              GL_RGBA4,
              TestHelper::kMaxRenderbufferSize,
              1);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
-    EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+    EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
   }
 
   void TestRenderbufferStorageMultisampleEXT(const char* extension,
@@ -2271,8 +2269,8 @@
   cmd.Init(target, count, attachments);
 
   // Should not result into a call into GL.
-  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(attachments)));
-  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+  EXPECT_EQ(error::kUnknownCommand,
+            ExecuteImmediateCmd(cmd, sizeof(attachments)));
 }
 
 TEST_P(GLES2DecoderManualInitTest,
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 19e36161..13fc8ad 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -19,6 +19,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/thread_task_runner_handle.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
 #include "gpu/command_buffer/common/value_state.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/context_group.h"
@@ -182,8 +183,11 @@
       image_factory_(nullptr),
       last_put_offset_(-1),
       gpu_memory_buffer_manager_(nullptr),
+      next_fence_sync_release_(1),
+      flushed_fence_sync_release_(0),
       flush_event_(false, false),
       service_(GetInitialService(service)),
+      fence_sync_wait_event_(false, false),
       gpu_thread_weak_ptr_factory_(this) {
   DCHECK(service_.get());
   next_image_id_.GetNext();
@@ -344,10 +348,9 @@
     return false;
   }
 
-  sync_point_client_state_ = SyncPointClientState::Create();
+  sync_point_order_data_ = SyncPointOrderData::Create();
   sync_point_client_ = service_->sync_point_manager()->CreateSyncPointClient(
-      sync_point_client_state_,
-      GetNamespaceID(), GetCommandBufferID());
+      sync_point_order_data_, GetNamespaceID(), GetCommandBufferID());
 
   if (service_->UseVirtualizedGLContexts() ||
       decoder_->GetContextGroup()
@@ -414,6 +417,12 @@
   decoder_->SetWaitSyncPointCallback(
       base::Bind(&InProcessCommandBuffer::WaitSyncPointOnGpuThread,
                  base::Unretained(this)));
+  decoder_->SetFenceSyncReleaseCallback(
+      base::Bind(&InProcessCommandBuffer::FenceSyncReleaseOnGpuThread,
+                 base::Unretained(this)));
+  decoder_->SetWaitFenceSyncCallback(
+      base::Bind(&InProcessCommandBuffer::WaitFenceSyncOnGpuThread,
+                 base::Unretained(this)));
 
   image_factory_ = params.image_factory;
 
@@ -445,7 +454,10 @@
   context_ = NULL;
   surface_ = NULL;
   sync_point_client_ = NULL;
-  sync_point_client_state_ = NULL;
+  if (sync_point_order_data_) {
+    sync_point_order_data_->Destroy();
+    sync_point_order_data_ = nullptr;
+  }
   gl_share_group_ = NULL;
 #if defined(OS_ANDROID)
   stream_texture_manager_.reset();
@@ -494,7 +506,7 @@
   ScopedEvent handle_flush(&flush_event_);
   base::AutoLock lock(command_buffer_lock_);
 
-  sync_point_client_state_->BeginProcessingOrderNumber(order_num);
+  sync_point_order_data_->BeginProcessingOrderNumber(order_num);
   command_buffer_->Flush(put_offset);
   {
     // Update state before signaling the flush event.
@@ -509,7 +521,7 @@
   // order number function until the message is rescheduled and finished
   // processing. This DCHECK is to enforce this.
   DCHECK(context_lost_ || put_offset == state_after_last_flush_.get_offset);
-  sync_point_client_state_->FinishProcessingOrderNumber(order_num);
+  sync_point_order_data_->FinishProcessingOrderNumber(order_num);
 
   // If we've processed all pending commands but still have pending queries,
   // pump idle work until the query is passed.
@@ -553,13 +565,15 @@
 
   SyncPointManager* sync_manager = service_->sync_point_manager();
   const uint32_t order_num =
-      sync_point_client_state_->GenerateUnprocessedOrderNumber(sync_manager);
+      sync_point_order_data_->GenerateUnprocessedOrderNumber(sync_manager);
   last_put_offset_ = put_offset;
   base::Closure task = base::Bind(&InProcessCommandBuffer::FlushOnGpuThread,
                                   gpu_thread_weak_ptr_,
                                   put_offset,
                                   order_num);
   QueueTask(task);
+
+  flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
 }
 
 void InProcessCommandBuffer::OrderingBarrier(int32 put_offset) {
@@ -798,8 +812,15 @@
       base::AutoLock lock(command_buffer_lock_);
       make_current_success = MakeCurrent();
     }
-    if (make_current_success)
-      mailbox_manager->PushTextureUpdates(sync_point);
+    if (make_current_success) {
+      // Old sync points are global and do not have a command buffer ID,
+      // We can simply use the GPUIO namespace with 0 for the command buffer ID
+      // (under normal circumstances 0 is  invalid so  will not be used) until
+      // the old sync points are replaced.
+      gles2::SyncToken sync_token = {gpu::CommandBufferNamespace::GPU_IO, 0,
+                                     sync_point};
+      mailbox_manager->PushTextureUpdates(sync_token);
+    }
   }
   service_->sync_point_manager()->RetireSyncPoint(sync_point);
 }
@@ -817,7 +838,63 @@
   service_->sync_point_manager()->WaitSyncPoint(sync_point);
   gles2::MailboxManager* mailbox_manager =
       decoder_->GetContextGroup()->mailbox_manager();
-  mailbox_manager->PullTextureUpdates(sync_point);
+  // Old sync points are global and do not have a command buffer ID,
+  // We can simply use the GPUIO namespace with 0 for the command buffer ID
+  // (under normal circumstances 0 is  invalid so  will not be used) until
+  // the old sync points are replaced.
+  gles2::SyncToken sync_token = {gpu::CommandBufferNamespace::GPU_IO, 0,
+                                 sync_point};
+  mailbox_manager->PullTextureUpdates(sync_token);
+  return true;
+}
+
+void InProcessCommandBuffer::FenceSyncReleaseOnGpuThread(uint64_t release) {
+  DCHECK(!sync_point_client_->client_state()->IsFenceSyncReleased(release));
+  gles2::MailboxManager* mailbox_manager =
+      decoder_->GetContextGroup()->mailbox_manager();
+  if (mailbox_manager->UsesSync()) {
+    bool make_current_success = false;
+    {
+      base::AutoLock lock(command_buffer_lock_);
+      make_current_success = MakeCurrent();
+    }
+    if (make_current_success) {
+      gles2::SyncToken sync_token = {GetNamespaceID(), GetCommandBufferID(),
+                                     release};
+      mailbox_manager->PushTextureUpdates(sync_token);
+    }
+  }
+
+  sync_point_client_->ReleaseFenceSync(release);
+}
+
+bool InProcessCommandBuffer::WaitFenceSyncOnGpuThread(
+    gpu::CommandBufferNamespace namespace_id,
+    uint64_t command_buffer_id,
+    uint64_t release) {
+  gpu::SyncPointManager* sync_point_manager = service_->sync_point_manager();
+  DCHECK(sync_point_manager);
+
+  scoped_refptr<gpu::SyncPointClientState> release_state =
+      sync_point_manager->GetSyncPointClientState(namespace_id,
+                                                  command_buffer_id);
+
+  if (!release_state)
+    return true;
+
+  if (!release_state->IsFenceSyncReleased(release)) {
+    // Use waitable event which is signalled when the release fence is released.
+    sync_point_client_->Wait(
+        release_state.get(), release,
+        base::Bind(&base::WaitableEvent::Signal,
+                   base::Unretained(&fence_sync_wait_event_)));
+    fence_sync_wait_event_.Wait();
+  }
+
+  gles2::MailboxManager* mailbox_manager =
+      decoder_->GetContextGroup()->mailbox_manager();
+  gles2::SyncToken sync_token = {namespace_id, command_buffer_id, release};
+  mailbox_manager->PullTextureUpdates(sync_token);
   return true;
 }
 
@@ -881,6 +958,18 @@
   return command_buffer_id_;
 }
 
+uint64_t InProcessCommandBuffer::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool InProcessCommandBuffer::IsFenceSyncRelease(uint64_t release) {
+  return release != 0 && release < next_fence_sync_release_;
+}
+
+bool InProcessCommandBuffer::IsFenceSyncFlushed(uint64_t release) {
+  return release <= flushed_fence_sync_release_;
+}
+
 uint32 InProcessCommandBuffer::CreateStreamTextureOnGpuThread(
     uint32 client_texture_id) {
 #if defined(OS_ANDROID)
diff --git a/gpu/command_buffer/service/in_process_command_buffer.h b/gpu/command_buffer/service/in_process_command_buffer.h
index f4f91f81..7843aa3a 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.h
+++ b/gpu/command_buffer/service/in_process_command_buffer.h
@@ -48,7 +48,7 @@
 
 namespace gpu {
 class SyncPointClient;
-class SyncPointClientState;
+class SyncPointOrderData;
 class SyncPointManager;
 class ValueStateMap;
 
@@ -130,6 +130,9 @@
   bool IsGpuChannelLost() override;
   CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
   // The serializer interface to the GPU service (i.e. thread).
   class Service {
@@ -215,6 +218,10 @@
   void SignalSyncPointOnGpuThread(uint32 sync_point,
                                   const base::Closure& callback);
   bool WaitSyncPointOnGpuThread(uint32 sync_point);
+  void FenceSyncReleaseOnGpuThread(uint64_t release);
+  bool WaitFenceSyncOnGpuThread(gpu::CommandBufferNamespace namespace_id,
+                                uint64_t command_buffer_id,
+                                uint64_t release);
   void SignalQueryOnGpuThread(unsigned query_id, const base::Closure& callback);
   void DestroyTransferBufferOnGpuThread(int32 id);
   void CreateImageOnGpuThread(int32 id,
@@ -242,7 +249,7 @@
   scoped_ptr<gles2::GLES2Decoder> decoder_;
   scoped_refptr<gfx::GLContext> context_;
   scoped_refptr<gfx::GLSurface> surface_;
-  scoped_refptr<SyncPointClientState> sync_point_client_state_;
+  scoped_refptr<SyncPointOrderData> sync_point_order_data_;
   scoped_ptr<SyncPointClient> sync_point_client_;
   base::Closure context_lost_callback_;
   bool delayed_work_pending_;  // Used to throttle PerformDelayedWork.
@@ -254,6 +261,8 @@
   gpu::Capabilities capabilities_;
   GpuMemoryBufferManager* gpu_memory_buffer_manager_;
   base::AtomicSequenceNumber next_image_id_;
+  uint64_t next_fence_sync_release_;
+  uint64_t flushed_fence_sync_release_;
 
   // Accessed on both threads:
   scoped_ptr<CommandBufferServiceBase> command_buffer_;
@@ -263,6 +272,7 @@
   State state_after_last_flush_;
   base::Lock state_after_last_flush_lock_;
   scoped_refptr<gfx::GLShareGroup> gl_share_group_;
+  base::WaitableEvent fence_sync_wait_event_;
 
 #if defined(OS_ANDROID)
   scoped_ptr<StreamTextureManagerInProcess> stream_texture_manager_;
diff --git a/gpu/command_buffer/service/mailbox_manager.h b/gpu/command_buffer/service/mailbox_manager.h
index 2bfdb89..78fb8f8 100644
--- a/gpu/command_buffer/service/mailbox_manager.h
+++ b/gpu/command_buffer/service/mailbox_manager.h
@@ -13,6 +13,7 @@
 namespace gles2 {
 
 class Texture;
+struct SyncToken;
 
 // Manages resources scoped beyond the context or context group level.
 class GPU_EXPORT MailboxManager : public base::RefCounted<MailboxManager> {
@@ -29,8 +30,8 @@
   virtual bool UsesSync() = 0;
 
   // Used to synchronize texture state across share groups.
-  virtual void PushTextureUpdates(uint32 sync_point) = 0;
-  virtual void PullTextureUpdates(uint32 sync_point) = 0;
+  virtual void PushTextureUpdates(const SyncToken& token) = 0;
+  virtual void PullTextureUpdates(const SyncToken& token) = 0;
 
   // Destroy any mailbox that reference the given texture.
   virtual void TextureDeleted(Texture* texture) = 0;
diff --git a/gpu/command_buffer/service/mailbox_manager_impl.h b/gpu/command_buffer/service/mailbox_manager_impl.h
index 5ea906e8..135b826 100644
--- a/gpu/command_buffer/service/mailbox_manager_impl.h
+++ b/gpu/command_buffer/service/mailbox_manager_impl.h
@@ -30,8 +30,8 @@
   Texture* ConsumeTexture(const Mailbox& mailbox) override;
   void ProduceTexture(const Mailbox& mailbox, Texture* texture) override;
   bool UsesSync() override;
-  void PushTextureUpdates(uint32 sync_point) override {}
-  void PullTextureUpdates(uint32 sync_point) override {}
+  void PushTextureUpdates(const SyncToken& token) override {}
+  void PullTextureUpdates(const SyncToken& token) override {}
   void TextureDeleted(Texture* texture) override;
 
  protected:
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.cc b/gpu/command_buffer/service/mailbox_manager_sync.cc
index 09d0d52..383cbb5c 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.cc
+++ b/gpu/command_buffer/service/mailbox_manager_sync.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/linked_ptr.h"
 #include "base/synchronization/lock.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "ui/gl/gl_fence.h"
 #include "ui/gl/gl_implementation.h"
@@ -24,23 +25,23 @@
 
 base::LazyInstance<base::Lock> g_lock = LAZY_INSTANCE_INITIALIZER;
 
-typedef std::map<uint32, linked_ptr<gfx::GLFence>> SyncPointToFenceMap;
-base::LazyInstance<SyncPointToFenceMap> g_sync_point_to_fence =
-    LAZY_INSTANCE_INITIALIZER;
 #if !defined(OS_MACOSX)
-base::LazyInstance<std::queue<SyncPointToFenceMap::iterator>> g_sync_points =
+typedef std::map<SyncToken, linked_ptr<gfx::GLFence>> SyncTokenToFenceMap;
+base::LazyInstance<SyncTokenToFenceMap> g_sync_point_to_fence =
+    LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<std::queue<SyncTokenToFenceMap::iterator>> g_sync_points =
     LAZY_INSTANCE_INITIALIZER;
 #endif
 
-void CreateFenceLocked(uint32 sync_point) {
+void CreateFenceLocked(const SyncToken& sync_token) {
+#if !defined(OS_MACOSX)
   g_lock.Get().AssertAcquired();
   if (gfx::GetGLImplementation() == gfx::kGLImplementationMockGL)
     return;
 
-#if !defined(OS_MACOSX)
-  std::queue<SyncPointToFenceMap::iterator>& sync_points = g_sync_points.Get();
-  SyncPointToFenceMap& sync_point_to_fence = g_sync_point_to_fence.Get();
-  if (sync_point) {
+  std::queue<SyncTokenToFenceMap::iterator>& sync_points = g_sync_points.Get();
+  SyncTokenToFenceMap& sync_point_to_fence = g_sync_point_to_fence.Get();
+  if (sync_token.release_count) {
     while (!sync_points.empty() &&
            sync_points.front()->second->HasCompleted()) {
       sync_point_to_fence.erase(sync_points.front());
@@ -49,8 +50,8 @@
     // Need to use EGL fences since we are likely not in a single share group.
     linked_ptr<gfx::GLFence> fence(make_linked_ptr(new gfx::GLFenceEGL));
     if (fence.get()) {
-      std::pair<SyncPointToFenceMap::iterator, bool> result =
-          sync_point_to_fence.insert(std::make_pair(sync_point, fence));
+      std::pair<SyncTokenToFenceMap::iterator, bool> result =
+          sync_point_to_fence.insert(std::make_pair(sync_token, fence));
       DCHECK(result.second);
       sync_points.push(result.first);
     }
@@ -59,13 +60,15 @@
 #endif
 }
 
-void AcquireFenceLocked(uint32 sync_point) {
+void AcquireFenceLocked(const SyncToken& sync_token) {
+#if !defined(OS_MACOSX)
   g_lock.Get().AssertAcquired();
-  SyncPointToFenceMap::iterator fence_it =
-      g_sync_point_to_fence.Get().find(sync_point);
+  SyncTokenToFenceMap::iterator fence_it =
+      g_sync_point_to_fence.Get().find(sync_token);
   if (fence_it != g_sync_point_to_fence.Get().end()) {
     fence_it->second->ServerWait();
   }
+#endif
 }
 
 static const unsigned kNewTextureVersion = 1;
@@ -294,22 +297,22 @@
                                          gl_image ? image_buffer : NULL));
 }
 
-void MailboxManagerSync::PushTextureUpdates(uint32 sync_point) {
+void MailboxManagerSync::PushTextureUpdates(const SyncToken& token) {
   base::AutoLock lock(g_lock.Get());
 
   for (TextureToGroupMap::iterator it = texture_to_group_.begin();
        it != texture_to_group_.end(); it++) {
     UpdateDefinitionLocked(it->first, &it->second);
   }
-  CreateFenceLocked(sync_point);
+  CreateFenceLocked(token);
 }
 
-void MailboxManagerSync::PullTextureUpdates(uint32 sync_point) {
+void MailboxManagerSync::PullTextureUpdates(const SyncToken& token) {
   using TextureUpdatePair = std::pair<Texture*, TextureDefinition>;
   std::vector<TextureUpdatePair> needs_update;
   {
     base::AutoLock lock(g_lock.Get());
-    AcquireFenceLocked(sync_point);
+    AcquireFenceLocked(token);
 
     for (TextureToGroupMap::iterator it = texture_to_group_.begin();
          it != texture_to_group_.end(); it++) {
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.h b/gpu/command_buffer/service/mailbox_manager_sync.h
index 481948e..ff16dd7 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.h
+++ b/gpu/command_buffer/service/mailbox_manager_sync.h
@@ -33,8 +33,8 @@
   Texture* ConsumeTexture(const Mailbox& mailbox) override;
   void ProduceTexture(const Mailbox& mailbox, Texture* texture) override;
   bool UsesSync() override;
-  void PushTextureUpdates(uint32 sync_point) override;
-  void PullTextureUpdates(uint32 sync_point) override;
+  void PushTextureUpdates(const SyncToken& token) override;
+  void PullTextureUpdates(const SyncToken& token) override;
   void TextureDeleted(Texture* texture) override;
 
  private:
diff --git a/gpu/command_buffer/service/mailbox_manager_unittest.cc b/gpu/command_buffer/service/mailbox_manager_unittest.cc
index d715a398..5101604 100644
--- a/gpu/command_buffer/service/mailbox_manager_unittest.cc
+++ b/gpu/command_buffer/service/mailbox_manager_unittest.cc
@@ -17,6 +17,9 @@
 
 using namespace ::testing;
 
+static const SyncToken g_sync_token = {gpu::CommandBufferNamespace::GPU_IO, 123,
+                                       0};
+
 class MailboxManagerTest : public GpuServiceTest {
  public:
   MailboxManagerTest() {}
@@ -276,8 +279,8 @@
   EXPECT_EQ(texture, manager_->ConsumeTexture(name));
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   DestroyTexture(texture);
   EXPECT_EQ(NULL, manager_->ConsumeTexture(name));
@@ -291,7 +294,7 @@
   Mailbox name = Mailbox::Generate();
 
   manager_->ProduceTexture(name, texture);
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
 
   // Clobber
   Texture* old_texture = texture;
@@ -317,8 +320,8 @@
   EXPECT_EQ(texture, manager_->ConsumeTexture(name));
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
@@ -336,10 +339,10 @@
   EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL);
 
   // Synchronize again
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   SetupUpdateTexParamExpectations(
       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
   GLsizei width, height;
   new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height, nullptr);
   EXPECT_EQ(16, width);
@@ -360,7 +363,7 @@
 
   // The last change to the texture should be visible without a sync point (i.e.
   // push).
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
   new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height, nullptr);
   EXPECT_EQ(64, width);
   EXPECT_EQ(64, height);
@@ -388,8 +391,8 @@
   manager2_->ProduceTexture(name2, texture2);
 
   // Make visible.
-  manager_->PushTextureUpdates(0);
-  manager2_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PushTextureUpdates(g_sync_token);
 
   // Create textures in the other manager instances for texture1 and texture2,
   // respectively to create a real sharing scenario. Otherwise, there would
@@ -416,7 +419,7 @@
             SetParameter(texture1, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
 
   // Make sure this does not clobber it with the previous version we pushed.
-  manager_->PullTextureUpdates(0);
+  manager_->PullTextureUpdates(g_sync_token);
 
   // Make a change to texture2
   DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture2->mag_filter());
@@ -426,16 +429,16 @@
   Mock::VerifyAndClearExpectations(gl_.get());
 
   // Synchronize in both directions
-  manager_->PushTextureUpdates(0);
-  manager2_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PushTextureUpdates(g_sync_token);
   // manager1 should see the change to texture2 mag_filter being applied.
   SetupUpdateTexParamExpectations(
       new_texture2->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT);
-  manager_->PullTextureUpdates(0);
+  manager_->PullTextureUpdates(g_sync_token);
   // manager2 should see the change to texture1 min_filter being applied.
   SetupUpdateTexParamExpectations(
       new_texture1->service_id(), GL_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT);
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   DestroyTexture(texture1);
   DestroyTexture(texture2);
@@ -457,8 +460,8 @@
   EXPECT_EQ(texture, manager_->ConsumeTexture(name));
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
@@ -479,8 +482,8 @@
             SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
 
   // Synchronize in both directions - no changes, since it's not shared
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
   EXPECT_EQ(static_cast<GLuint>(GL_LINEAR), new_texture->min_filter());
 
   // Make a change to the previously shared texture
@@ -489,10 +492,10 @@
             SetParameter(old_texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
 
   // Synchronize and expect update
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   SetupUpdateTexParamExpectations(
       new_texture->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT);
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
@@ -522,8 +525,8 @@
   EXPECT_EQ(texture, manager_->ConsumeTexture(name));
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
@@ -540,10 +543,10 @@
   EXPECT_FALSE(texture->SafeToRenderFrom());
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   SetupUpdateTexParamExpectations(
       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   // Cleared state should be synced.
   EXPECT_FALSE(new_texture->SafeToRenderFrom());
@@ -568,8 +571,8 @@
   EXPECT_EQ(texture, manager_->ConsumeTexture(name));
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   // Should sync to new texture which is not defined.
   EXPECT_CALL(*gl_, GenTextures(1, _))
@@ -591,10 +594,10 @@
   EXPECT_TRUE(texture->IsDefined());
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   SetupUpdateTexParamExpectations(
       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   // Cleared state should be synced.
   EXPECT_TRUE(new_texture->IsDefined());
@@ -619,10 +622,10 @@
   manager_->ProduceTexture(name1, texture);
 
   // Share
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
-  manager2_->PullTextureUpdates(0);
+  manager2_->PullTextureUpdates(g_sync_token);
   SetupUpdateTexParamExpectations(
       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
   Texture* new_texture = manager2_->ConsumeTexture(name1);
@@ -631,8 +634,8 @@
   manager_->ProduceTexture(name2, texture);
 
   // Synchronize
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   // name2 should return the same texture
   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name2));
@@ -658,7 +661,7 @@
   manager_->ProduceTexture(name, texture1);
 
   // Share
-  manager_->PushTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
   EXPECT_CALL(*gl_, GenTextures(1, _))
       .WillOnce(SetArgPointee<1>(kNewTextureId));
   SetupUpdateTexParamExpectations(
@@ -671,8 +674,8 @@
   manager_->ProduceTexture(name, texture1);
 
   // Synchronize manager -> manager2
-  manager_->PushTextureUpdates(0);
-  manager2_->PullTextureUpdates(0);
+  manager_->PushTextureUpdates(g_sync_token);
+  manager2_->PullTextureUpdates(g_sync_token);
 
   // name should return the original texture, and not texture2 or a new one.
   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name));
diff --git a/gpu/command_buffer/service/sync_point_manager.cc b/gpu/command_buffer/service/sync_point_manager.cc
index 039e494d..bc73adb 100644
--- a/gpu/command_buffer/service/sync_point_manager.cc
+++ b/gpu/command_buffer/service/sync_point_manager.cc
@@ -6,47 +6,281 @@
 
 #include <climits>
 
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/rand_util.h"
 #include "base/sequence_checker.h"
+#include "base/single_thread_task_runner.h"
 
 namespace gpu {
 
 static const int kMaxSyncBase = INT_MAX;
 
-scoped_refptr<SyncPointClientState> SyncPointClientState::Create() {
-  return new SyncPointClientState;
+namespace {
+
+void RunOnThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                 const base::Closure& callback) {
+  if (task_runner->BelongsToCurrentThread()) {
+    callback.Run();
+  } else {
+    task_runner->PostTask(FROM_HERE, callback);
+  }
 }
 
-uint32_t SyncPointClientState::GenerateUnprocessedOrderNumber(
+}  // namespace
+
+scoped_refptr<SyncPointOrderData> SyncPointOrderData::Create() {
+  return new SyncPointOrderData;
+}
+
+void SyncPointOrderData::Destroy() {
+  // Because of circular references between the SyncPointOrderData and
+  // SyncPointClientState, we must remove the references on destroy. Releasing
+  // the fence syncs in the order fence queue would be redundant at this point
+  // because they are assumed to be released on the destruction of the
+  // SyncPointClient.
+  base::AutoLock auto_lock(lock_);
+  destroyed_ = true;
+  while (!order_fence_queue_.empty()) {
+    order_fence_queue_.pop();
+  }
+}
+
+uint32_t SyncPointOrderData::GenerateUnprocessedOrderNumber(
     SyncPointManager* sync_point_manager) {
   const uint32_t order_num = sync_point_manager->GenerateOrderNumber();
-  base::subtle::Release_Store(&unprocessed_order_num_, order_num);
+  base::AutoLock auto_lock(lock_);
+  unprocessed_order_num_ = order_num;
   return order_num;
 }
 
-SyncPointClientState::SyncPointClientState()
-  : processed_order_num_(0),
-    unprocessed_order_num_(0),
-    current_order_num_(0) {
+void SyncPointOrderData::BeginProcessingOrderNumber(uint32_t order_num) {
+  DCHECK(processing_thread_checker_.CalledOnValidThread());
+  DCHECK_GE(order_num, current_order_num_);
+  current_order_num_ = order_num;
+
+  // Catch invalid waits which were waiting on fence syncs that do not exist.
+  // When we begin processing an order number, we should release any fence
+  // syncs which were enqueued but the order number never existed.
+  // Release without the lock to avoid possible deadlocks.
+  std::vector<OrderFence> ensure_releases;
+  {
+    base::AutoLock auto_lock(lock_);
+    while (!order_fence_queue_.empty()) {
+      const OrderFence& order_fence = order_fence_queue_.top();
+      if (order_fence_queue_.top().order_num < order_num) {
+        ensure_releases.push_back(order_fence);
+        order_fence_queue_.pop();
+        continue;
+      }
+      break;
+    }
+  }
+
+  for (OrderFence& order_fence : ensure_releases) {
+    order_fence.client_state->EnsureReleased(order_fence.fence_release);
+  }
 }
 
+void SyncPointOrderData::FinishProcessingOrderNumber(uint32_t order_num) {
+  DCHECK(processing_thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(current_order_num_, order_num);
+
+  // Catch invalid waits which were waiting on fence syncs that do not exist.
+  // When we end processing an order number, we should release any fence syncs
+  // which were suppose to be released during this order number.
+  // Release without the lock to avoid possible deadlocks.
+  std::vector<OrderFence> ensure_releases;
+  {
+    base::AutoLock auto_lock(lock_);
+    DCHECK_GT(order_num, processed_order_num_);
+    processed_order_num_ = order_num;
+
+    while (!order_fence_queue_.empty()) {
+      const OrderFence& order_fence = order_fence_queue_.top();
+      if (order_fence_queue_.top().order_num <= order_num) {
+        ensure_releases.push_back(order_fence);
+        order_fence_queue_.pop();
+        continue;
+      }
+      break;
+    }
+  }
+
+  for (OrderFence& order_fence : ensure_releases) {
+    order_fence.client_state->EnsureReleased(order_fence.fence_release);
+  }
+}
+
+SyncPointOrderData::OrderFence::OrderFence(
+    uint32_t order,
+    uint64_t release,
+    scoped_refptr<SyncPointClientState> state)
+    : order_num(order), fence_release(release), client_state(state) {}
+
+SyncPointOrderData::OrderFence::~OrderFence() {}
+
+SyncPointOrderData::SyncPointOrderData()
+    : current_order_num_(0),
+      destroyed_(false),
+      processed_order_num_(0),
+      unprocessed_order_num_(0) {}
+
+SyncPointOrderData::~SyncPointOrderData() {}
+
+bool SyncPointOrderData::ValidateReleaseOrderNumber(
+    scoped_refptr<SyncPointClientState> client_state,
+    uint32_t wait_order_num,
+    uint64_t fence_release) {
+  base::AutoLock auto_lock(lock_);
+  if (destroyed_)
+    return false;
+
+  // Release should have a possible unprocessed order number lower
+  // than the wait order number.
+  if ((processed_order_num_ + 1) >= wait_order_num)
+    return false;
+
+  // Release should have more unprocessed numbers if we are waiting.
+  if (unprocessed_order_num_ <= processed_order_num_)
+    return false;
+
+  // So far it could be valid, but add an order fence guard to be sure it
+  // gets released eventually.
+  const uint32_t expected_order_num =
+      std::min(unprocessed_order_num_, wait_order_num);
+  order_fence_queue_.push(
+      OrderFence(expected_order_num, fence_release, client_state));
+  return true;
+}
+
+SyncPointClientState::ReleaseCallback::ReleaseCallback(
+    uint64_t release,
+    const base::Closure& callback)
+    : release_count(release), callback_closure(callback) {}
+
+SyncPointClientState::ReleaseCallback::~ReleaseCallback() {}
+
+SyncPointClientState::SyncPointClientState(
+    scoped_refptr<SyncPointOrderData> order_data)
+    : order_data_(order_data), fence_sync_release_(0) {}
+
 SyncPointClientState::~SyncPointClientState() {
 }
 
+bool SyncPointClientState::WaitForRelease(uint32_t wait_order_num,
+                                          uint64_t release,
+                                          const base::Closure& callback) {
+  // Lock must be held the whole time while we validate otherwise it could be
+  // released while we are checking.
+  {
+    base::AutoLock auto_lock(fence_sync_lock_);
+    if (release > fence_sync_release_) {
+      if (!order_data_->ValidateReleaseOrderNumber(this, wait_order_num,
+                                                   release)) {
+        return false;
+      } else {
+        // Add the callback which will be called upon release.
+        release_callback_queue_.push(ReleaseCallback(release, callback));
+        return true;
+      }
+    }
+  }
+
+  // Already released, run the callback now.
+  callback.Run();
+  return true;
+}
+
+void SyncPointClientState::ReleaseFenceSync(uint64_t release) {
+  // Call callbacks without the lock to avoid possible deadlocks.
+  std::vector<base::Closure> callback_list;
+  {
+    base::AutoLock auto_lock(fence_sync_lock_);
+    ReleaseFenceSyncLocked(release, &callback_list);
+  }
+
+  for (const base::Closure& closure : callback_list) {
+    closure.Run();
+  }
+}
+
+void SyncPointClientState::EnsureReleased(uint64_t release) {
+  // Call callbacks without the lock to avoid possible deadlocks.
+  std::vector<base::Closure> callback_list;
+  {
+    base::AutoLock auto_lock(fence_sync_lock_);
+    if (release <= fence_sync_release_)
+      return;
+
+    ReleaseFenceSyncLocked(release, &callback_list);
+  }
+
+  for (const base::Closure& closure : callback_list) {
+    closure.Run();
+  }
+}
+
+void SyncPointClientState::ReleaseFenceSyncLocked(
+    uint64_t release,
+    std::vector<base::Closure>* callback_list) {
+  fence_sync_lock_.AssertAcquired();
+  DCHECK_GT(release, fence_sync_release_);
+
+  fence_sync_release_ = release;
+  while (!release_callback_queue_.empty() &&
+         release_callback_queue_.top().release_count <= release) {
+    callback_list->push_back(release_callback_queue_.top().callback_closure);
+    release_callback_queue_.pop();
+  }
+}
+
 SyncPointClient::~SyncPointClient() {
+  // Release all fences on destruction.
+  ReleaseFenceSync(UINT64_MAX);
+
   sync_point_manager_->DestroySyncPointClient(namespace_id_, client_id_);
 }
 
+bool SyncPointClient::Wait(SyncPointClientState* release_state,
+                           uint64_t release_count,
+                           const base::Closure& wait_complete_callback) {
+  const uint32_t wait_order_number =
+      client_state_->order_data()->current_order_num();
+
+  // If waiting on self or wait was invalid, call the callback and return false.
+  if (client_state_ == release_state ||
+      !release_state->WaitForRelease(wait_order_number, release_count,
+                                     wait_complete_callback)) {
+    wait_complete_callback.Run();
+    return false;
+  }
+  return true;
+}
+
+bool SyncPointClient::WaitNonThreadSafe(
+    SyncPointClientState* release_state,
+    uint64_t release_count,
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const base::Closure& wait_complete_callback) {
+  return Wait(release_state, release_count,
+              base::Bind(&RunOnThread, runner, wait_complete_callback));
+}
+
+void SyncPointClient::ReleaseFenceSync(uint64_t release) {
+  client_state_->ReleaseFenceSync(release);
+}
+
 SyncPointClient::SyncPointClient(SyncPointManager* sync_point_manager,
-                                 scoped_refptr<SyncPointClientState> state,
+                                 scoped_refptr<SyncPointOrderData> order_data,
                                  CommandBufferNamespace namespace_id,
                                  uint64_t client_id)
-  : sync_point_manager_(sync_point_manager),
-    client_state_(state),
-    namespace_id_(namespace_id),
-    client_id_(client_id) {
-}
+    : sync_point_manager_(sync_point_manager),
+      client_state_(new SyncPointClientState(order_data)),
+      namespace_id_(namespace_id),
+      client_id_(client_id) {}
 
 SyncPointManager::SyncPointManager(bool allow_threaded_wait)
     : allow_threaded_wait_(allow_threaded_wait),
@@ -65,18 +299,17 @@
 }
 
 scoped_ptr<SyncPointClient> SyncPointManager::CreateSyncPointClient(
-    scoped_refptr<SyncPointClientState> client_state,
-    CommandBufferNamespace namespace_id, uint64_t client_id) {
+    scoped_refptr<SyncPointOrderData> order_data,
+    CommandBufferNamespace namespace_id,
+    uint64_t client_id) {
   DCHECK_GE(namespace_id, 0);
   DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_));
   base::AutoLock auto_lock(client_maps_lock_);
 
   ClientMap& client_map = client_maps_[namespace_id];
   std::pair<ClientMap::iterator, bool> result = client_map.insert(
-      std::make_pair(client_id, new SyncPointClient(this,
-                                                    client_state,
-                                                    namespace_id,
-                                                    client_id)));
+      std::make_pair(client_id, new SyncPointClient(this, order_data,
+                                                    namespace_id, client_id)));
   DCHECK(result.second);
 
   return make_scoped_ptr(result.first->second);
diff --git a/gpu/command_buffer/service/sync_point_manager.h b/gpu/command_buffer/service/sync_point_manager.h
index 3f11e05..a72566b0d 100644
--- a/gpu/command_buffer/service/sync_point_manager.h
+++ b/gpu/command_buffer/service/sync_point_manager.h
@@ -5,6 +5,8 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_
 #define GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_
 
+#include <functional>
+#include <queue>
 #include <vector>
 
 #include "base/atomic_sequence_num.h"
@@ -19,36 +21,34 @@
 #include "gpu/command_buffer/common/constants.h"
 #include "gpu/gpu_export.h"
 
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
 namespace gpu {
 
 class SyncPointClient;
+class SyncPointClientState;
 class SyncPointManager;
 
-class GPU_EXPORT SyncPointClientState
-    : public base::RefCountedThreadSafe<SyncPointClientState> {
+class GPU_EXPORT SyncPointOrderData
+    : public base::RefCountedThreadSafe<SyncPointOrderData> {
  public:
-  static scoped_refptr<SyncPointClientState> Create();
+  static scoped_refptr<SyncPointOrderData> Create();
+  void Destroy();
+
   uint32_t GenerateUnprocessedOrderNumber(SyncPointManager* sync_point_manager);
-
-  void BeginProcessingOrderNumber(uint32_t order_num) {
-    DCHECK(processing_thread_checker_.CalledOnValidThread());
-    DCHECK_GE(order_num, current_order_num_);
-    current_order_num_ = order_num;
-  }
-
-  void FinishProcessingOrderNumber(uint32_t order_num) {
-    DCHECK(processing_thread_checker_.CalledOnValidThread());
-    DCHECK_EQ(current_order_num_, order_num);
-    DCHECK_GT(order_num, processed_order_num());
-    base::subtle::Release_Store(&processed_order_num_, order_num);
-  }
+  void BeginProcessingOrderNumber(uint32_t order_num);
+  void FinishProcessingOrderNumber(uint32_t order_num);
 
   uint32_t processed_order_num() const {
-    return base::subtle::Acquire_Load(&processed_order_num_);
+    base::AutoLock auto_lock(lock_);
+    return processed_order_num_;
   }
 
   uint32_t unprocessed_order_num() const {
-    return base::subtle::Acquire_Load(&unprocessed_order_num_);
+    base::AutoLock auto_lock(lock_);
+    return unprocessed_order_num_;
   }
 
   uint32_t current_order_num() const {
@@ -56,18 +56,37 @@
     return current_order_num_;
   }
 
- protected:
-  friend class base::RefCountedThreadSafe<SyncPointClientState>;
-  friend class SyncPointClient;
+ private:
+  friend class base::RefCountedThreadSafe<SyncPointOrderData>;
+  friend class SyncPointClientState;
 
-  SyncPointClientState();
-  virtual ~SyncPointClientState();
+  struct OrderFence {
+    uint32_t order_num;
+    uint64_t fence_release;
+    scoped_refptr<SyncPointClientState> client_state;
 
-  // Last finished IPC order number.
-  base::subtle::Atomic32 processed_order_num_;
+    OrderFence(uint32_t order,
+               uint64_t release,
+               scoped_refptr<SyncPointClientState> state);
+    ~OrderFence();
 
-  // Unprocessed order number expected to be processed under normal execution.
-  base::subtle::Atomic32 unprocessed_order_num_;
+    bool operator>(const OrderFence& rhs) const {
+      return (order_num > rhs.order_num) ||
+             ((order_num == rhs.order_num) &&
+              (fence_release > rhs.fence_release));
+    }
+  };
+  typedef std::priority_queue<OrderFence,
+                              std::vector<OrderFence>,
+                              std::greater<OrderFence>> OrderFenceQueue;
+
+  SyncPointOrderData();
+  ~SyncPointOrderData();
+
+  bool ValidateReleaseOrderNumber(
+      scoped_refptr<SyncPointClientState> client_state,
+      uint32_t wait_order_num,
+      uint64_t fence_release);
 
   // Non thread-safe functions need to be called from a single thread.
   base::ThreadChecker processing_thread_checker_;
@@ -75,6 +94,95 @@
   // Current IPC order number being processed (only used on processing thread).
   uint32_t current_order_num_;
 
+  // This lock protects destroyed_, processed_order_num_,
+  // unprocessed_order_num_, and order_fence_queue_. All order numbers (n) in
+  // order_fence_queue_ must follow the invariant:
+  //   processed_order_num_ < n <= unprocessed_order_num_.
+  mutable base::Lock lock_;
+
+  bool destroyed_;
+
+  // Last finished IPC order number.
+  uint32_t processed_order_num_;
+
+  // Unprocessed order number expected to be processed under normal execution.
+  uint32_t unprocessed_order_num_;
+
+  // In situations where we are waiting on fence syncs that do not exist, we
+  // validate by making sure the order number does not pass the order number
+  // which the wait command was issued. If the order number reaches the
+  // wait command's, we should automatically release up to the expected
+  // release count. Note that this also releases other lower release counts,
+  // so a single misbehaved fence sync is enough to invalidate/signal all
+  // previous fence syncs.
+  OrderFenceQueue order_fence_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncPointOrderData);
+};
+
+class GPU_EXPORT SyncPointClientState
+    : public base::RefCountedThreadSafe<SyncPointClientState> {
+ public:
+  scoped_refptr<SyncPointOrderData> order_data() { return order_data_; }
+
+  bool IsFenceSyncReleased(uint64_t release) {
+    return release <= fence_sync_release();
+  }
+
+  uint64_t fence_sync_release() {
+    base::AutoLock auto_lock(fence_sync_lock_);
+    return fence_sync_release_;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<SyncPointClientState>;
+  friend class SyncPointClient;
+  friend class SyncPointOrderData;
+
+  struct ReleaseCallback {
+    uint64_t release_count;
+    base::Closure callback_closure;
+
+    ReleaseCallback(uint64_t release, const base::Closure& callback);
+    ~ReleaseCallback();
+
+    bool operator>(const ReleaseCallback& rhs) const {
+      return release_count > rhs.release_count;
+    }
+  };
+  typedef std::priority_queue<ReleaseCallback,
+                              std::vector<ReleaseCallback>,
+                              std::greater<ReleaseCallback>>
+      ReleaseCallbackQueue;
+
+  SyncPointClientState(scoped_refptr<SyncPointOrderData> order_data);
+  ~SyncPointClientState();
+
+  // Queues the callback to be called if the release is valid. If the release
+  // is invalid this function will return False and the callback will never
+  // be called.
+  bool WaitForRelease(uint32_t wait_order_num,
+                      uint64_t release,
+                      const base::Closure& callback);
+
+  void ReleaseFenceSync(uint64_t release);
+  void EnsureReleased(uint64_t release);
+  void ReleaseFenceSyncLocked(uint64_t release,
+                              std::vector<base::Closure>* callback_list);
+
+  // Global order data where releases will originate from.
+  scoped_refptr<SyncPointOrderData> order_data_;
+
+  // Protects fence_sync_release_, fence_callback_queue_.
+  base::Lock fence_sync_lock_;
+
+  // Current fence sync release that has been signaled.
+  uint64_t fence_sync_release_;
+
+  // In well defined fence sync operations, fence syncs are released in order
+  // so simply having a priority queue for callbacks is enough.
+  ReleaseCallbackQueue release_callback_queue_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncPointClientState);
 };
 
@@ -84,12 +192,32 @@
 
   scoped_refptr<SyncPointClientState> client_state() { return client_state_; }
 
+  // Wait for a release count to be reached on a SyncPointClientState. If this
+  // function returns false, that means the wait was invalid. Otherwise if it
+  // returns True it means the release was valid. In the case where the release
+  // is valid but has happened already, it will still return true. In all cases
+  // wait_complete_callback will be called eventually. The callback function
+  // may be called on another thread so it should be thread-safe. For
+  // convenience, another non-threadsafe version is defined below where you
+  // can supply a task runner.
+  bool Wait(SyncPointClientState* release_state,
+            uint64_t release_count,
+            const base::Closure& wait_complete_callback);
+
+  bool WaitNonThreadSafe(SyncPointClientState* release_state,
+                         uint64_t release_count,
+                         scoped_refptr<base::SingleThreadTaskRunner> runner,
+                         const base::Closure& wait_complete_callback);
+
+  void ReleaseFenceSync(uint64_t release);
+
  private:
   friend class SyncPointManager;
 
   SyncPointClient(SyncPointManager* sync_point_manager,
-                  scoped_refptr<SyncPointClientState> state,
-                  CommandBufferNamespace namespace_id, uint64_t client_id);
+                  scoped_refptr<SyncPointOrderData> order_data,
+                  CommandBufferNamespace namespace_id,
+                  uint64_t client_id);
 
   // Sync point manager is guaranteed to exist in the lifetime of the client.
   SyncPointManager* sync_point_manager_;
@@ -98,8 +226,8 @@
   scoped_refptr<SyncPointClientState> client_state_;
 
   // Unique namespace/client id pair for this sync point client.
-  CommandBufferNamespace namespace_id_;
-  uint64_t client_id_;
+  const CommandBufferNamespace namespace_id_;
+  const uint64_t client_id_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncPointClient);
 };
@@ -113,8 +241,9 @@
 
   // Creates/Destroy a sync point client which message processors should hold.
   scoped_ptr<SyncPointClient> CreateSyncPointClient(
-      scoped_refptr<SyncPointClientState> client_state,
-      CommandBufferNamespace namespace_id, uint64_t client_id);
+      scoped_refptr<SyncPointOrderData> order_data,
+      CommandBufferNamespace namespace_id,
+      uint64_t client_id);
 
   // Finds the state of an already created sync point client.
   scoped_refptr<SyncPointClientState> GetSyncPointClientState(
@@ -142,7 +271,7 @@
 
  private:
   friend class SyncPointClient;
-  friend class SyncPointClientState;
+  friend class SyncPointOrderData;
 
   typedef std::vector<base::Closure> ClosureList;
   typedef base::hash_map<uint32, ClosureList> SyncPointMap;
diff --git a/gpu/command_buffer/service/sync_point_manager_unittest.cc b/gpu/command_buffer/service/sync_point_manager_unittest.cc
new file mode 100644
index 0000000..0b42505
--- /dev/null
+++ b/gpu/command_buffer/service/sync_point_manager_unittest.cc
@@ -0,0 +1,415 @@
+// 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 <queue>
+
+#include "base/bind.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+
+class SyncPointManagerTest : public testing::Test {
+ public:
+  SyncPointManagerTest() {}
+
+  ~SyncPointManagerTest() override {}
+
+ protected:
+  void SetUp() override {
+    sync_point_manager_.reset(new SyncPointManager(false));
+  }
+
+  void TearDown() override { sync_point_manager_.reset(); }
+
+  // Simple static function which can be used to test callbacks.
+  static void SetIntegerFunction(int* test, int value) { *test = value; }
+
+  scoped_ptr<SyncPointManager> sync_point_manager_;
+};
+
+struct SyncPointStream {
+  scoped_refptr<SyncPointOrderData> order_data;
+  scoped_ptr<SyncPointClient> client;
+  std::queue<uint32_t> order_numbers;
+
+  SyncPointStream(SyncPointManager* sync_point_manager,
+                  CommandBufferNamespace namespace_id,
+                  uint64_t command_buffer_id)
+      : order_data(SyncPointOrderData::Create()),
+        client(sync_point_manager->CreateSyncPointClient(order_data,
+                                                         namespace_id,
+                                                         command_buffer_id)) {}
+
+  ~SyncPointStream() {
+    order_data->Destroy();
+    order_data = nullptr;
+  }
+
+  void AllocateOrderNum(SyncPointManager* sync_point_manager) {
+    order_numbers.push(
+        order_data->GenerateUnprocessedOrderNumber(sync_point_manager));
+  }
+
+  void BeginProcessing() {
+    ASSERT_FALSE(order_numbers.empty());
+    order_data->BeginProcessingOrderNumber(order_numbers.front());
+  }
+
+  void EndProcessing() {
+    ASSERT_FALSE(order_numbers.empty());
+    order_data->FinishProcessingOrderNumber(order_numbers.front());
+    order_numbers.pop();
+  }
+};
+
+TEST_F(SyncPointManagerTest, BasicSyncPointOrderDataTest) {
+  scoped_refptr<SyncPointOrderData> order_data = SyncPointOrderData::Create();
+
+  EXPECT_EQ(0u, order_data->current_order_num());
+  EXPECT_EQ(0u, order_data->processed_order_num());
+  EXPECT_EQ(0u, order_data->unprocessed_order_num());
+
+  const uint32_t order_num =
+      order_data->GenerateUnprocessedOrderNumber(sync_point_manager_.get());
+  EXPECT_EQ(1u, order_num);
+
+  EXPECT_EQ(0u, order_data->current_order_num());
+  EXPECT_EQ(0u, order_data->processed_order_num());
+  EXPECT_EQ(order_num, order_data->unprocessed_order_num());
+
+  order_data->BeginProcessingOrderNumber(order_num);
+  EXPECT_EQ(order_num, order_data->current_order_num());
+  EXPECT_EQ(0u, order_data->processed_order_num());
+  EXPECT_EQ(order_num, order_data->unprocessed_order_num());
+
+  order_data->FinishProcessingOrderNumber(order_num);
+  EXPECT_EQ(order_num, order_data->current_order_num());
+  EXPECT_EQ(order_num, order_data->processed_order_num());
+  EXPECT_EQ(order_num, order_data->unprocessed_order_num());
+}
+
+TEST_F(SyncPointManagerTest, SyncPointClientRegistration) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId = 0x123;
+
+  scoped_refptr<SyncPointClientState> empty_state =
+      sync_point_manager_->GetSyncPointClientState(kNamespaceId, kBufferId);
+  EXPECT_FALSE(empty_state);
+
+  scoped_refptr<SyncPointOrderData> order_data = SyncPointOrderData::Create();
+
+  scoped_ptr<SyncPointClient> client =
+      sync_point_manager_->CreateSyncPointClient(order_data, kNamespaceId,
+                                                 kBufferId);
+
+  EXPECT_EQ(order_data, client->client_state()->order_data());
+  EXPECT_EQ(
+      client->client_state(),
+      sync_point_manager_->GetSyncPointClientState(kNamespaceId, kBufferId));
+}
+
+TEST_F(SyncPointManagerTest, BasicFenceSyncRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId = 0x123;
+
+  scoped_refptr<SyncPointOrderData> order_data = SyncPointOrderData::Create();
+  scoped_ptr<SyncPointClient> client =
+      sync_point_manager_->CreateSyncPointClient(order_data, kNamespaceId,
+                                                 kBufferId);
+  scoped_refptr<SyncPointClientState> client_state = client->client_state();
+
+  EXPECT_EQ(0u, client_state->fence_sync_release());
+  EXPECT_FALSE(client_state->IsFenceSyncReleased(1));
+
+  client->ReleaseFenceSync(1);
+
+  EXPECT_EQ(1u, client_state->fence_sync_release());
+  EXPECT_TRUE(client_state->IsFenceSyncReleased(1));
+}
+
+TEST_F(SyncPointManagerTest, MultipleClientsPerOrderData) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  scoped_refptr<SyncPointOrderData> order_data = SyncPointOrderData::Create();
+  scoped_ptr<SyncPointClient> client1 =
+      sync_point_manager_->CreateSyncPointClient(order_data, kNamespaceId,
+                                                 kBufferId1);
+  scoped_ptr<SyncPointClient> client2 =
+      sync_point_manager_->CreateSyncPointClient(order_data, kNamespaceId,
+                                                 kBufferId2);
+
+  scoped_refptr<SyncPointClientState> client_state1 = client1->client_state();
+  scoped_refptr<SyncPointClientState> client_state2 = client2->client_state();
+
+  client1->ReleaseFenceSync(1);
+
+  EXPECT_TRUE(client_state1->IsFenceSyncReleased(1));
+  EXPECT_FALSE(client_state2->IsFenceSyncReleased(1));
+}
+
+TEST_F(SyncPointManagerTest, BasicFenceSyncWaitRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  ASSERT_TRUE(valid_wait);
+  EXPECT_EQ(10, test_num);
+
+  release_stream.BeginProcessing();
+  release_stream.client->ReleaseFenceSync(1);
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, WaitOnSelfFails) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Generate wait order number first.
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      wait_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_FALSE(valid_wait);
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, OutOfOrderRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Generate wait order number first.
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_FALSE(valid_wait);
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, HigherOrderNumberRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Generate wait order number first.
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  // Order number was higher but it was actually released.
+  release_stream.BeginProcessing();
+  release_stream.client->ReleaseFenceSync(1);
+  release_stream.EndProcessing();
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_TRUE(valid_wait);
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, DestroyedClientRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_TRUE(valid_wait);
+  EXPECT_EQ(10, test_num);
+
+  // Destroying the client should release the wait.
+  release_stream.client.reset();
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, NonExistentRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Assign release stream order [1] and wait stream order [2].
+  // This test simply tests that a wait stream of order [2] waiting on
+  // release stream of order [1] will still release the fence sync even
+  // though nothing was released.
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  wait_stream.BeginProcessing();
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_TRUE(valid_wait);
+  EXPECT_EQ(10, test_num);
+
+  // No release but finishing the order number should automatically release.
+  release_stream.BeginProcessing();
+  EXPECT_EQ(10, test_num);
+  release_stream.EndProcessing();
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, NonExistentRelease2) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Assign Release stream order [1] and assign Wait stream orders [2, 3].
+  // This test is similar to the NonExistentRelease case except
+  // we place an extra order number in between the release and wait.
+  // The wait stream [3] is waiting on release stream [1] even though
+  // order [2] was also generated. Although order [2] only exists on the
+  // wait stream so the release stream should only know about order [1].
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  // Have wait with order [3] to wait on release.
+  wait_stream.BeginProcessing();
+  ASSERT_EQ(2u, wait_stream.order_data->current_order_num());
+  wait_stream.EndProcessing();
+  wait_stream.BeginProcessing();
+  ASSERT_EQ(3u, wait_stream.order_data->current_order_num());
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_TRUE(valid_wait);
+  EXPECT_EQ(10, test_num);
+
+  // Even though release stream order [1] did not have a release, it
+  // should still release the fence when finish processing since the wait
+  // stream had expected on to exist there.
+  release_stream.BeginProcessing();
+  ASSERT_EQ(1u, release_stream.order_data->current_order_num());
+  release_stream.EndProcessing();
+  EXPECT_TRUE(release_stream.client->client_state()->IsFenceSyncReleased(1));
+  EXPECT_EQ(123, test_num);
+}
+
+TEST_F(SyncPointManagerTest, NonExistentOrderNumRelease) {
+  const CommandBufferNamespace kNamespaceId =
+      gpu::CommandBufferNamespace::GPU_IO;
+  const uint64_t kBufferId1 = 0x123;
+  const uint64_t kBufferId2 = 0x234;
+
+  SyncPointStream release_stream(sync_point_manager_.get(), kNamespaceId,
+                                 kBufferId1);
+  SyncPointStream wait_stream(sync_point_manager_.get(), kNamespaceId,
+                              kBufferId2);
+
+  // Assign Release stream orders [1, 4] and assign Wait stream orders [2, 3].
+  // Here we are testing that wait order [3] will wait on a fence sync
+  // in either order [1] or order [2]. Order [2] was not actually assigned
+  // to the release stream so it is essentially non-existent to the release
+  // stream's point of view. Once the release stream begins processing the next
+  // order [3], it should realize order [2] didn't exist and release the fence.
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+  wait_stream.AllocateOrderNum(sync_point_manager_.get());
+  release_stream.AllocateOrderNum(sync_point_manager_.get());
+
+  // Have wait with order [3] to wait on release order [1] or [2].
+  wait_stream.BeginProcessing();
+  ASSERT_EQ(2u, wait_stream.order_data->current_order_num());
+  wait_stream.EndProcessing();
+  wait_stream.BeginProcessing();
+  ASSERT_EQ(3u, wait_stream.order_data->current_order_num());
+  int test_num = 10;
+  const bool valid_wait = wait_stream.client->Wait(
+      release_stream.client->client_state().get(), 1,
+      base::Bind(&SyncPointManagerTest::SetIntegerFunction, &test_num, 123));
+  EXPECT_TRUE(valid_wait);
+  EXPECT_EQ(10, test_num);
+
+  // Release stream should know it should release fence sync by order [3],
+  // so going through order [1] should not release it yet.
+  release_stream.BeginProcessing();
+  ASSERT_EQ(1u, release_stream.order_data->current_order_num());
+  release_stream.EndProcessing();
+  EXPECT_FALSE(release_stream.client->client_state()->IsFenceSyncReleased(1));
+  EXPECT_EQ(10, test_num);
+
+  // Beginning order [4] should immediately trigger the release.
+  release_stream.BeginProcessing();
+  ASSERT_EQ(4u, release_stream.order_data->current_order_num());
+  EXPECT_TRUE(release_stream.client->client_state()->IsFenceSyncReleased(1));
+  EXPECT_EQ(123, test_num);
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_fence_sync_unittest.cc b/gpu/command_buffer/tests/gl_fence_sync_unittest.cc
new file mode 100644
index 0000000..ad21f34
--- /dev/null
+++ b/gpu/command_buffer/tests/gl_fence_sync_unittest.cc
@@ -0,0 +1,69 @@
+// 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 <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/tests/gl_manager.h"
+#include "gpu/command_buffer/tests/gl_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define SHADER(Src) #Src
+
+namespace gpu {
+
+class GLFenceSyncTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    sync_point_manager_.reset(new SyncPointManager(false));
+
+    GLManager::Options options;
+    options.sync_point_manager = sync_point_manager_.get();
+    gl1_.Initialize(options);
+    gl2_.Initialize(options);
+  }
+
+  void TearDown() override {
+    gl2_.Destroy();
+    gl1_.Destroy();
+
+    sync_point_manager_.reset();
+  }
+
+  scoped_ptr<SyncPointManager> sync_point_manager_;
+  GLManager gl1_;
+  GLManager gl2_;
+};
+
+TEST_F(GLFenceSyncTest, SimpleReleaseWait) {
+  gl1_.MakeCurrent();
+
+  const GLuint64 fence_sync = glInsertFenceSyncCHROMIUM();
+  GLbyte sync_token[GL_SYNC_TOKEN_SIZE_CHROMIUM];
+  glFlush();
+  glGenSyncTokenCHROMIUM(fence_sync, sync_token);
+  ASSERT_TRUE(GL_NO_ERROR == glGetError());
+
+  // Make sure it is actually released.
+  scoped_refptr<SyncPointClientState> gl1_client_state =
+      sync_point_manager_->GetSyncPointClientState(gl1_.GetNamespaceID(),
+                                                   gl1_.GetCommandBufferID());
+  EXPECT_TRUE(gl1_client_state->IsFenceSyncReleased(fence_sync));
+
+  gl2_.MakeCurrent();
+  glWaitSyncTokenCHROMIUM(sync_token);
+  glFinish();
+
+  // gl2 should not have released anything.
+  scoped_refptr<SyncPointClientState> gl2_client_state =
+      sync_point_manager_->GetSyncPointClientState(gl2_.GetNamespaceID(),
+                                                   gl2_.GetCommandBufferID());
+  EXPECT_EQ(0u, gl2_client_state->fence_sync_release());
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 9c6bf41..8d38fdbd 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -28,6 +28,7 @@
 #include "gpu/command_buffer/service/image_manager.h"
 #include "gpu/command_buffer/service/mailbox_manager_impl.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "gpu/command_buffer/service/valuebuffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,6 +42,8 @@
 namespace gpu {
 namespace {
 
+uint64_t g_next_command_buffer_id = 0;
+
 class GpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
  public:
   GpuMemoryBufferImpl(base::RefCountedBytes* bytes,
@@ -103,6 +106,7 @@
 
 GLManager::Options::Options()
     : size(4, 4),
+      sync_point_manager(NULL),
       share_group_manager(NULL),
       share_mailbox_manager(NULL),
       virtual_manager(NULL),
@@ -111,7 +115,11 @@
       context_lost_allowed(false),
       context_type(gles2::CONTEXT_TYPE_OPENGLES2) {}
 
-GLManager::GLManager() : context_lost_allowed_(false) {
+GLManager::GLManager()
+    : sync_point_manager_(nullptr),
+      context_lost_allowed_(false),
+      command_buffer_id_(g_next_command_buffer_id++),
+      next_fence_sync_release_(1) {
   SetupBaseContext();
 }
 
@@ -146,6 +154,7 @@
 void GLManager::Initialize(const GLManager::Options& options) {
   InitializeWithCommandLine(options, nullptr);
 }
+
 void GLManager::InitializeWithCommandLine(const GLManager::Options& options,
                                           base::CommandLine* command_line) {
   const int32 kCommandBufferSize = 1024 * 1024;
@@ -253,6 +262,22 @@
     return;
   }
 
+  if (options.sync_point_manager) {
+    sync_point_manager_ = options.sync_point_manager;
+    sync_point_order_data_ = SyncPointOrderData::Create();
+    sync_point_client_ = sync_point_manager_->CreateSyncPointClient(
+        sync_point_order_data_, GetNamespaceID(), GetCommandBufferID());
+
+    decoder_->SetFenceSyncReleaseCallback(
+        base::Bind(&GLManager::OnFenceSyncRelease, base::Unretained(this)));
+    decoder_->SetWaitFenceSyncCallback(
+        base::Bind(&GLManager::OnWaitFenceSync, base::Unretained(this)));
+  } else {
+    sync_point_manager_ = nullptr;
+    sync_point_order_data_ = nullptr;
+    sync_point_client_ = nullptr;
+  }
+
   command_buffer_->SetPutOffsetChangeCallback(
       base::Bind(&GLManager::PumpCommands, base::Unretained(this)));
   command_buffer_->SetGetBufferChangeCallback(
@@ -304,6 +329,28 @@
   ++use_count_;
 }
 
+void GLManager::OnFenceSyncRelease(uint64_t release) {
+  DCHECK(sync_point_client_);
+  DCHECK(!sync_point_client_->client_state()->IsFenceSyncReleased(release));
+  sync_point_client_->ReleaseFenceSync(release);
+}
+
+bool GLManager::OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+                                uint64_t command_buffer_id,
+                                uint64_t release) {
+  DCHECK(sync_point_client_);
+  scoped_refptr<gpu::SyncPointClientState> release_state =
+      sync_point_manager_->GetSyncPointClientState(namespace_id,
+                                                   command_buffer_id);
+  if (!release_state)
+    return true;
+
+  // GLManager does not support being multithreaded at this point, so the fence
+  // sync must be released by the time wait is called.
+  DCHECK(release_state->IsFenceSyncReleased(release));
+  return true;
+}
+
 void GLManager::MakeCurrent() {
   ::gles2::SetGLContext(gles2_implementation_.get());
 }
@@ -322,6 +369,12 @@
   transfer_buffer_.reset();
   gles2_helper_.reset();
   command_buffer_.reset();
+  sync_point_manager_ = nullptr;
+  sync_point_client_ = nullptr;
+  if (sync_point_order_data_) {
+    sync_point_order_data_->Destroy();
+    sync_point_order_data_ = nullptr;
+  }
   if (decoder_.get()) {
     bool have_context = decoder_->GetGLContext() &&
                         decoder_->GetGLContext()->MakeCurrent(surface_.get());
@@ -340,11 +393,24 @@
     command_buffer_->SetParseError(::gpu::error::kLostContext);
     return;
   }
+  uint32_t order_num = 0;
+  if (sync_point_manager_) {
+    // If sync point manager is supported, assign order numbers to commands.
+    order_num = sync_point_order_data_->GenerateUnprocessedOrderNumber(
+        sync_point_manager_);
+    sync_point_order_data_->BeginProcessingOrderNumber(order_num);
+  }
+
   gpu_scheduler_->PutChanged();
   ::gpu::CommandBuffer::State state = command_buffer_->GetLastState();
   if (!context_lost_allowed_) {
     ASSERT_EQ(::gpu::error::kNoError, state.error);
   }
+
+  if (sync_point_manager_) {
+    // Finish processing order number here.
+    sync_point_order_data_->FinishProcessingOrderNumber(order_num);
+  }
 }
 
 bool GLManager::GetBufferChanged(int32 transfer_buffer_id) {
@@ -441,7 +507,19 @@
 }
 
 uint64_t GLManager::GetCommandBufferID() const {
-  return 0;
+  return command_buffer_id_;
+}
+
+uint64_t GLManager::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool GLManager::IsFenceSyncRelease(uint64_t release) {
+  return release > 0 && release < next_fence_sync_release_;
+}
+
+bool GLManager::IsFenceSyncFlushed(uint64_t release) {
+  return IsFenceSyncRelease(release);
 }
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index 16c8e298..901bae8 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -30,6 +30,9 @@
 
 class CommandBufferService;
 class GpuScheduler;
+class SyncPointClient;
+class SyncPointOrderData;
+class SyncPointManager;
 class TransferBuffer;
 
 namespace gles2 {
@@ -50,6 +53,8 @@
     Options();
     // The size of the backbuffer.
     gfx::Size size;
+    // If not null will have a corresponding sync point manager.
+    SyncPointManager* sync_point_manager;
     // If not null will share resources with this context.
     GLManager* share_group_manager;
     // If not null will share a mailbox manager with this context.
@@ -127,12 +132,23 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
  private:
   void PumpCommands();
   bool GetBufferChanged(int32 transfer_buffer_id);
   void SetupBaseContext();
+  void OnFenceSyncRelease(uint64_t release);
+  bool OnWaitFenceSync(gpu::CommandBufferNamespace namespace_id,
+                       uint64_t command_buffer_id,
+                       uint64_t release);
 
+  SyncPointManager* sync_point_manager_;  // Non-owning.
+
+  scoped_refptr<SyncPointOrderData> sync_point_order_data_;
+  scoped_ptr<SyncPointClient> sync_point_client_;
   scoped_refptr<gles2::MailboxManager> mailbox_manager_;
   scoped_refptr<gfx::GLShareGroup> share_group_;
   scoped_ptr<CommandBufferService> command_buffer_;
@@ -145,6 +161,9 @@
   scoped_ptr<gles2::GLES2Implementation> gles2_implementation_;
   bool context_lost_allowed_;
 
+  const uint64_t command_buffer_id_;
+  uint64_t next_fence_sync_release_;
+
   // Used on Android to virtualize GL for all contexts.
   static int use_count_;
   static scoped_refptr<gfx::GLShareGroup>* base_share_group_;
diff --git a/gpu/gles2_conform_support/egl/display.cc b/gpu/gles2_conform_support/egl/display.cc
index 546184d..3168fa9 100644
--- a/gpu/gles2_conform_support/egl/display.cc
+++ b/gpu/gles2_conform_support/egl/display.cc
@@ -35,7 +35,8 @@
 #endif
       create_offscreen_(false),
       create_offscreen_width_(0),
-      create_offscreen_height_(0) {
+      create_offscreen_height_(0),
+      next_fence_sync_release_(1) {
 }
 
 Display::~Display() {
@@ -352,4 +353,16 @@
   return 0;
 }
 
+uint64_t Display::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool Display::IsFenceSyncRelease(uint64_t release) {
+  return release > 0 && release < next_fence_sync_release_;
+}
+
+bool Display::IsFenceSyncFlushed(uint64_t release) {
+  return IsFenceSyncRelease(release);
+}
+
 }  // namespace egl
diff --git a/gpu/gles2_conform_support/egl/display.h b/gpu/gles2_conform_support/egl/display.h
index 8954c2a97..a3c69ab 100644
--- a/gpu/gles2_conform_support/egl/display.h
+++ b/gpu/gles2_conform_support/egl/display.h
@@ -100,6 +100,9 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
  private:
   EGLNativeDisplayType display_id_;
@@ -116,6 +119,7 @@
   bool create_offscreen_;
   int create_offscreen_width_;
   int create_offscreen_height_;
+  uint64_t next_fence_sync_release_;
 
   scoped_refptr<gpu::TransferBufferManagerInterface> transfer_buffer_manager_;
   scoped_ptr<gpu::CommandBufferService> command_buffer_;
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 930153a..7a11c3cf 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -142,6 +142,13 @@
       'sources': [
         'angle_unittest_main.cc',
       ],
+      'conditions': [
+        ['OS=="android"', {
+          'dependencies': [
+            '../testing/android/native_test.gyp:native_test_native_code',
+          ],
+        }],
+      ],
     },
     {
       # GN version: //gpu:gpu_unittests
@@ -244,6 +251,7 @@
         'command_buffer/service/shader_manager_unittest.cc',
         'command_buffer/service/shader_translator_cache_unittest.cc',
         'command_buffer/service/shader_translator_unittest.cc',
+        'command_buffer/service/sync_point_manager_unittest.cc',
         'command_buffer/service/test_helper.cc',
         'command_buffer/service/test_helper.h',
         'command_buffer/service/path_manager_unittest.cc',
@@ -359,6 +367,7 @@
         'command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc',
         'command_buffer/tests/gl_cube_map_texture_unittest.cc',
         'command_buffer/tests/gl_depth_texture_unittest.cc',
+        'command_buffer/tests/gl_fence_sync_unittest.cc',
         'command_buffer/tests/gl_gpu_memory_buffer_unittest.cc',
         'command_buffer/tests/gl_lose_context_chromium_unittest.cc',
         'command_buffer/tests/gl_manager.cc',
@@ -707,6 +716,19 @@
     ['OS == "android"', {
       'targets': [
         {
+          'target_name': 'angle_unittests_apk',
+          'type': 'none',
+          'dependencies':
+          [
+            'angle_unittests',
+          ],
+          'variables':
+          {
+            'test_suite_name': 'angle_unittests',
+          },
+          'includes': [ '../build/apk_test.gypi' ],
+        },
+        {
           'target_name': 'gl_tests_apk',
           'type': 'none',
           'dependencies': [
diff --git a/ios/chrome/browser/application_context.h b/ios/chrome/browser/application_context.h
index 9cf30da..3efaa5c 100644
--- a/ios/chrome/browser/application_context.h
+++ b/ios/chrome/browser/application_context.h
@@ -31,12 +31,17 @@
 
 namespace policy {
 class BrowserPolicyConnector;
+class PolicyService;
 }
 
 namespace rappor {
 class RapporService;
 }
 
+namespace variations {
+class VariationsService;
+}
+
 class ApplicationContext;
 class PrefService;
 
@@ -48,6 +53,19 @@
   ApplicationContext();
   virtual ~ApplicationContext();
 
+  // Invoked when application enters foreground. Cancels the effect of
+  // OnAppEnterBackground(), in particular removes the boolean preference
+  // indicating that the ChromeBrowserStates have been shutdown.
+  virtual void OnAppEnterForeground() = 0;
+
+  // Invoked when application enters background. Saves any state that must be
+  // saved before shutdown can continue.
+  virtual void OnAppEnterBackground() = 0;
+
+  // Returns whether the last complete shutdown was clean (i.e. happened while
+  // the application was backgrounded).
+  virtual bool WasLastShutdownClean() = 0;
+
   // Gets the local state associated with this application.
   virtual PrefService* GetLocalState() = 0;
 
@@ -63,9 +81,15 @@
   // Gets the MetricsService used by this application.
   virtual metrics::MetricsService* GetMetricsService() = 0;
 
+  // Gets the VariationsService used by this application.
+  virtual variations::VariationsService* GetVariationsService() = 0;
+
   // Gets the policy connector, creating and starting it if necessary.
   virtual policy::BrowserPolicyConnector* GetBrowserPolicyConnector() = 0;
 
+  // Gets the policy service.
+  virtual policy::PolicyService* GetPolicyService() = 0;
+
   // Gets the RapporService. May return null.
   virtual rappor::RapporService* GetRapporService() = 0;
 
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc
index 5946416fb..e86a64b 100644
--- a/ios/chrome/browser/application_context_impl.cc
+++ b/ios/chrome/browser/application_context_impl.cc
@@ -4,19 +4,50 @@
 
 #include "ios/chrome/browser/application_context_impl.h"
 
+#include <algorithm>
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
 #include "base/time/default_tick_clock.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_service.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/network_time/network_time_tracker.h"
 #include "components/translate/core/browser/translate_download_manager.h"
+#include "components/variations/service/variations_service.h"
+#include "ios/chrome/browser/chrome_paths.h"
+#include "ios/chrome/browser/history/history_service_factory.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/prefs/browser_prefs.h"
+#include "ios/chrome/browser/prefs/ios_chrome_pref_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "net/log/net_log_capture_mode.h"
+#include "net/socket/client_socket_pool_manager.h"
+
+#if defined(ENABLE_CONFIGURATION_POLICY)
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/policy_service.h"
+#else
+#include "components/policy/core/common/policy_service_stub.h"
+#endif
 
 ApplicationContextImpl::ApplicationContextImpl(
-    const base::CommandLine& command_line) {
+    base::SequencedTaskRunner* local_state_task_runner,
+    const base::CommandLine& command_line)
+    : local_state_task_runner_(local_state_task_runner),
+      was_last_shutdown_clean_(false),
+      created_local_state_(false) {
   DCHECK(!GetApplicationContext());
   SetApplicationContext(this);
 
@@ -30,9 +61,87 @@
   SetApplicationContext(nullptr);
 }
 
+// static
+void ApplicationContextImpl::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(metrics::prefs::kMetricsReportingEnabled,
+                                false);
+  registry->RegisterBooleanPref(prefs::kLastSessionExitedCleanly, true);
+  registry->RegisterBooleanPref(prefs::kMetricsReportingWifiOnly, true);
+}
+
+void ApplicationContextImpl::SetApplicationLocale(const std::string& locale) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  application_locale_ = locale;
+  translate::TranslateDownloadManager::GetInstance()->set_application_locale(
+      application_locale_);
+}
+
+void ApplicationContextImpl::OnAppEnterForeground() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  PrefService* local_state = GetLocalState();
+  local_state->SetBoolean(prefs::kLastSessionExitedCleanly, true);
+
+  // Tell the metrics service that the application resumes.
+  metrics::MetricsService* metrics_service = GetMetricsService();
+  if (metrics_service && local_state) {
+    metrics_service->OnAppEnterForeground();
+    local_state->CommitPendingWrite();
+  }
+
+  variations::VariationsService* variations_service = GetVariationsService();
+  if (variations_service)
+    variations_service->OnAppEnterForeground();
+
+  std::vector<ios::ChromeBrowserState*> loaded_browser_state =
+      GetChromeBrowserStateManager()->GetLoadedChromeBrowserStates();
+  for (ios::ChromeBrowserState* browser_state : loaded_browser_state) {
+    browser_state->SetExitType(ios::ChromeBrowserState::EXIT_CRASHED);
+  }
+}
+
+void ApplicationContextImpl::OnAppEnterBackground() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Mark all the ChromeBrowserStates as clean and persist history.
+  std::vector<ios::ChromeBrowserState*> loaded_browser_state =
+      GetChromeBrowserStateManager()->GetLoadedChromeBrowserStates();
+  for (ios::ChromeBrowserState* browser_state : loaded_browser_state) {
+    if (history::HistoryService* history_service =
+            ios::HistoryServiceFactory::GetForBrowserStateIfExists(
+                browser_state, ServiceAccessType::EXPLICIT_ACCESS)) {
+      history_service->HandleBackgrounding();
+    }
+
+    browser_state->SetExitType(ios::ChromeBrowserState::EXIT_NORMAL);
+    PrefService* browser_state_prefs = browser_state->GetPrefs();
+    if (browser_state_prefs)
+      browser_state_prefs->CommitPendingWrite();
+  }
+
+  PrefService* local_state = GetLocalState();
+  local_state->SetBoolean(prefs::kLastSessionExitedCleanly, true);
+
+  // Tell the metrics service it was cleanly shutdown.
+  metrics::MetricsService* metrics_service = GetMetricsService();
+  if (metrics_service && local_state)
+    metrics_service->OnAppEnterBackground();
+
+  // Persisting to disk is protected by a critical task, so no other special
+  // handling is necessary on iOS.
+}
+
+bool ApplicationContextImpl::WasLastShutdownClean() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Make sure the locale state is created as the file is initialized there.
+  ignore_result(GetLocalState());
+  return was_last_shutdown_clean_;
+}
+
 PrefService* ApplicationContextImpl::GetLocalState() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return ios::GetChromeBrowserProvider()->GetLocalState();
+  if (!created_local_state_)
+    CreateLocalState();
+  return local_state_.get();
 }
 
 net::URLRequestContextGetter*
@@ -47,13 +156,6 @@
   return application_locale_;
 }
 
-void ApplicationContextImpl::SetApplicationLocale(const std::string& locale) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  application_locale_ = locale;
-  translate::TranslateDownloadManager::GetInstance()->set_application_locale(
-      application_locale_);
-}
-
 ios::ChromeBrowserStateManager*
 ApplicationContextImpl::GetChromeBrowserStateManager() {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -65,12 +167,28 @@
   return ios::GetChromeBrowserProvider()->GetMetricsService();
 }
 
+variations::VariationsService* ApplicationContextImpl::GetVariationsService() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return ios::GetChromeBrowserProvider()->GetVariationsService();
+}
+
 policy::BrowserPolicyConnector*
 ApplicationContextImpl::GetBrowserPolicyConnector() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return ios::GetChromeBrowserProvider()->GetBrowserPolicyConnector();
 }
 
+policy::PolicyService* ApplicationContextImpl::GetPolicyService() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(ENABLE_CONFIGURATION_POLICY)
+  return GetBrowserPolicyConnector()->GetPolicyService();
+#else
+  if (!policy_service_)
+    policy_service_.reset(new policy::PolicyServiceStub);
+  return policy_service_.get();
+#endif
+}
+
 rappor::RapporService* ApplicationContextImpl::GetRapporService() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return ios::GetChromeBrowserProvider()->GetRapporService();
@@ -90,3 +208,34 @@
   }
   return network_time_tracker_.get();
 }
+
+void ApplicationContextImpl::CreateLocalState() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!created_local_state_ && !local_state_);
+  created_local_state_ = true;
+
+  base::FilePath local_state_path;
+  CHECK(PathService::Get(ios::FILE_LOCAL_STATE, &local_state_path));
+  scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple);
+
+  // Register local state preferences.
+  RegisterLocalStatePrefs(pref_registry.get());
+
+  local_state_ =
+      ::CreateLocalState(local_state_path, local_state_task_runner_.get(),
+                         GetPolicyService(), pref_registry, false);
+
+  const int max_per_proxy =
+      local_state_->GetInteger(ios::prefs::kMaxConnectionsPerProxy);
+  net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
+      net::HttpNetworkSession::NORMAL_SOCKET_POOL,
+      std::max(std::min(max_per_proxy, 99),
+               net::ClientSocketPoolManager::max_sockets_per_group(
+                   net::HttpNetworkSession::NORMAL_SOCKET_POOL)));
+
+  // Register the shutdown state before anything changes it.
+  if (local_state_->HasPrefPath(prefs::kLastSessionExitedCleanly)) {
+    was_last_shutdown_clean_ =
+        local_state_->GetBoolean(prefs::kLastSessionExitedCleanly);
+  }
+}
diff --git a/ios/chrome/browser/application_context_impl.h b/ios/chrome/browser/application_context_impl.h
index 162954a..e02aace5 100644
--- a/ios/chrome/browser/application_context_impl.h
+++ b/ios/chrome/browser/application_context_impl.h
@@ -8,39 +8,68 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "ios/chrome/browser/application_context.h"
 
+class PrefRegistrySimple;
+
 namespace base {
 class CommandLine;
+class SequencedTaskRunner;
 }
 
 class ApplicationContextImpl : public ApplicationContext {
  public:
-  ApplicationContextImpl(const base::CommandLine& command_line);
+  ApplicationContextImpl(base::SequencedTaskRunner* local_state_task_runner,
+                         const base::CommandLine& command_line);
   ~ApplicationContextImpl() override;
 
+  // Registers local state prefs.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
   // Sets the locale used by the application.
   void SetApplicationLocale(const std::string& locale);
 
  private:
   // ApplicationContext implementation.
+  void OnAppEnterForeground() override;
+  void OnAppEnterBackground() override;
+  bool WasLastShutdownClean() override;
   PrefService* GetLocalState() override;
   net::URLRequestContextGetter* GetSystemURLRequestContext() override;
   const std::string& GetApplicationLocale() override;
   ios::ChromeBrowserStateManager* GetChromeBrowserStateManager() override;
   metrics::MetricsService* GetMetricsService() override;
+  variations::VariationsService* GetVariationsService() override;
   policy::BrowserPolicyConnector* GetBrowserPolicyConnector() override;
+  policy::PolicyService* GetPolicyService() override;
   rappor::RapporService* GetRapporService() override;
   net_log::ChromeNetLog* GetNetLog() override;
   network_time::NetworkTimeTracker* GetNetworkTimeTracker() override;
 
+  void CreateLocalState();
+
   base::ThreadChecker thread_checker_;
+  scoped_ptr<PrefService> local_state_;
   scoped_ptr<net_log::ChromeNetLog> net_log_;
   scoped_ptr<network_time::NetworkTimeTracker> network_time_tracker_;
   std::string application_locale_;
 
+#if !defined(ENABLE_CONFIGURATION_POLICY)
+  // Must be destroyed after |local_state_|.
+  // This is a stub when policy is not enabled. Otherwise the PolicyService is
+  // owned by the BrowserPolicyConnector and this is not used.
+  scoped_ptr<policy::PolicyService> policy_service_;
+#endif
+
+  // Sequenced task runner for local state related I/O tasks.
+  const scoped_refptr<base::SequencedTaskRunner> local_state_task_runner_;
+
+  bool was_last_shutdown_clean_;
+  bool created_local_state_;
+
   DISALLOW_COPY_AND_ASSIGN(ApplicationContextImpl);
 };
 
diff --git a/ios/chrome/browser/chrome_paths.h b/ios/chrome/browser/chrome_paths.h
index 1084da48..77a9122 100644
--- a/ios/chrome/browser/chrome_paths.h
+++ b/ios/chrome/browser/chrome_paths.h
@@ -17,6 +17,9 @@
   DIR_CRASH_DUMPS,             // Directory where crash dumps are written.
   DIR_TEST_DATA,               // Directory where unit test data resides.
 
+  FILE_LOCAL_STATE,  // Path and filename to the file in which
+                     // installation-specific state is saved.
+
   PATH_END
 };
 
diff --git a/ios/chrome/browser/chrome_paths.mm b/ios/chrome/browser/chrome_paths.mm
index 955df2a..0a86fb5 100644
--- a/ios/chrome/browser/chrome_paths.mm
+++ b/ios/chrome/browser/chrome_paths.mm
@@ -60,14 +60,18 @@
       cur = cur.Append(FILE_PATH_LITERAL("data"));
       break;
 
+    case FILE_LOCAL_STATE:
+      if (!PathService::Get(ios::DIR_USER_DATA, &cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("Local State"));
+      break;
+
     default:
       return false;
   }
 
-  if (!base::PathExists(cur)) {
-    if (!create_dir || !base::CreateDirectory(cur))
-      return false;
-  }
+  if (create_dir && !base::PathExists(cur) && !base::CreateDirectory(cur))
+    return false;
 
   *result = cur;
   return true;
diff --git a/ios/chrome/browser/pref_names.cc b/ios/chrome/browser/pref_names.cc
index c6b9a17f..db97805bb 100644
--- a/ios/chrome/browser/pref_names.cc
+++ b/ios/chrome/browser/pref_names.cc
@@ -19,6 +19,7 @@
 const char kDefaultCharset[] = "intl.charset_default";
 const char kEnableDoNotTrack[] = "enable_do_not_track";
 const char kHttpServerProperties[] = "net.http_server_properties";
+const char kMaxConnectionsPerProxy[] = "net.max_connections_per_proxy";
 const char kSavingBrowserHistoryDisabled[] = "history.saving_disabled";
 const char kSearchSuggestEnabled[] = "search.suggest_enabled";
 
@@ -43,6 +44,11 @@
 // the bookmark promo dialog.
 const char kIosBookmarkPromoAlreadySeen[] = "ios.bookmark.promo_already_seen";
 
+// Boolean which indicates whether browsing data migration is/was possible in
+// this or a previous cold start.
+const char kBrowsingDataMigrationHasBeenPossible[] =
+    "ios.browsing_data_migration_controller.migration_has_been_possible";
+
 // Boolean which indicates if the user has already set a "do not backup" bit to
 // the OTR Profiles's state stash path to ensure that the folder is not
 // automatically synced to iCloud/iTunes.
diff --git a/ios/chrome/browser/pref_names.h b/ios/chrome/browser/pref_names.h
index 16e8f97..ec1bd38 100644
--- a/ios/chrome/browser/pref_names.h
+++ b/ios/chrome/browser/pref_names.h
@@ -17,6 +17,7 @@
 extern const char kDefaultCharset[];
 extern const char kEnableDoNotTrack[];
 extern const char kHttpServerProperties[];
+extern const char kMaxConnectionsPerProxy[];
 extern const char kSavingBrowserHistoryDisabled[];
 extern const char kSearchSuggestEnabled[];
 
@@ -28,6 +29,7 @@
 extern const char kContextualSearchEnabled[];
 extern const char kIosBookmarkFolderDefault[];
 extern const char kIosBookmarkPromoAlreadySeen[];
+extern const char kBrowsingDataMigrationHasBeenPossible[];
 extern const char kOTRStashStatePathSystemBackupExcluded[];
 extern const char kIosHandoffToOtherDevices[];
 extern const char kLastSessionExitedCleanly[];
diff --git a/ios/chrome/browser/prefs/browser_prefs.cc b/ios/chrome/browser/prefs/browser_prefs.mm
similarity index 89%
rename from ios/chrome/browser/prefs/browser_prefs.cc
rename to ios/chrome/browser/prefs/browser_prefs.mm
index 1dfd220..c74ce0e 100644
--- a/ios/chrome/browser/prefs/browser_prefs.cc
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -22,7 +22,10 @@
 #include "components/translate/core/common/translate_pref_names.h"
 #include "components/variations/service/variations_service.h"
 #include "components/web_resource/promo_resource_service.h"
+#include "ios/chrome/browser/application_context_impl.h"
 #include "ios/chrome/browser/first_run/first_run.h"
+#import "ios/chrome/browser/geolocation/omnibox_geolocation_local_state.h"
+#import "ios/chrome/browser/memory/memory_debugger_manager.h"
 #include "ios/chrome/browser/net/http_server_properties_manager_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
@@ -66,6 +69,18 @@
   registry->RegisterIntegerPref(ios::prefs::kBrowserStatesNumCreated, 1);
   registry->RegisterListPref(ios::prefs::kBrowserStatesLastActive);
 
+  [OmniboxGeolocationLocalState registerLocalState:registry];
+  [MemoryDebuggerManager registerLocalState:registry];
+
+  // TODO(shreyasv): Remove this in M49 as almost all users would have the
+  // "do not backup" bit set by then. crbug.com/489865.
+  registry->RegisterBooleanPref(prefs::kOTRStashStatePathSystemBackupExcluded,
+                                false);
+  registry->RegisterBooleanPref(prefs::kBrowsingDataMigrationHasBeenPossible,
+                                false);
+
+  ApplicationContextImpl::RegisterPrefs(registry);
+
   ios::GetChromeBrowserProvider()->RegisterLocalState(registry);
 }
 
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index ddba590..7756f92 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -321,8 +321,8 @@
         'browser/passwords/password_generation_utils.mm',
         'browser/pref_names.cc',
         'browser/pref_names.h',
-        'browser/prefs/browser_prefs.cc',
         'browser/prefs/browser_prefs.h',
+        'browser/prefs/browser_prefs.mm',
         'browser/prefs/ios_chrome_pref_model_associator_client.cc',
         'browser/prefs/ios_chrome_pref_model_associator_client.h',
         'browser/prefs/ios_chrome_pref_service_factory.cc',
diff --git a/ios/chrome/test/testing_application_context.cc b/ios/chrome/test/testing_application_context.cc
index 014a913..62b2c255 100644
--- a/ios/chrome/test/testing_application_context.cc
+++ b/ios/chrome/test/testing_application_context.cc
@@ -12,7 +12,8 @@
 TestingApplicationContext::TestingApplicationContext()
     : application_locale_("en"),
       local_state_(nullptr),
-      chrome_browser_state_manager_(nullptr) {
+      chrome_browser_state_manager_(nullptr),
+      was_last_shutdown_clean_(false) {
   DCHECK(!GetApplicationContext());
   SetApplicationContext(this);
 }
@@ -22,19 +23,37 @@
   SetApplicationContext(nullptr);
 }
 
+// static
+TestingApplicationContext* TestingApplicationContext::GetGlobal() {
+  return static_cast<TestingApplicationContext*>(GetApplicationContext());
+}
+
 void TestingApplicationContext::SetLocalState(PrefService* local_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   local_state_ = local_state;
 }
 
+void TestingApplicationContext::SetLastShutdownClean(bool clean) {
+  was_last_shutdown_clean_ = clean;
+}
+
 void TestingApplicationContext::SetChromeBrowserStateManager(
     ios::ChromeBrowserStateManager* manager) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   chrome_browser_state_manager_ = manager;
 }
 
-// static
-TestingApplicationContext* TestingApplicationContext::GetGlobal() {
-  return static_cast<TestingApplicationContext*>(GetApplicationContext());
+void TestingApplicationContext::OnAppEnterForeground() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void TestingApplicationContext::OnAppEnterBackground() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+bool TestingApplicationContext::WasLastShutdownClean() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return was_last_shutdown_clean_;
 }
 
 PrefService* TestingApplicationContext::GetLocalState() {
@@ -65,12 +84,23 @@
   return nullptr;
 }
 
+variations::VariationsService*
+TestingApplicationContext::GetVariationsService() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return nullptr;
+}
+
 policy::BrowserPolicyConnector*
 TestingApplicationContext::GetBrowserPolicyConnector() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return nullptr;
 }
 
+policy::PolicyService* TestingApplicationContext::GetPolicyService() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return nullptr;
+}
+
 rappor::RapporService* TestingApplicationContext::GetRapporService() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return nullptr;
diff --git a/ios/chrome/test/testing_application_context.h b/ios/chrome/test/testing_application_context.h
index 0c073c3..8025c45 100644
--- a/ios/chrome/test/testing_application_context.h
+++ b/ios/chrome/test/testing_application_context.h
@@ -22,16 +22,24 @@
   // Sets the local state.
   void SetLocalState(PrefService* local_state);
 
+  // Sets the last shutdown "clean" state.
+  void SetLastShutdownClean(bool clean);
+
   // Sets the ChromeBrowserStateManager.
   void SetChromeBrowserStateManager(ios::ChromeBrowserStateManager* manager);
 
   // ApplicationContext implementation.
+  void OnAppEnterForeground() override;
+  void OnAppEnterBackground() override;
+  bool WasLastShutdownClean() override;
   PrefService* GetLocalState() override;
   net::URLRequestContextGetter* GetSystemURLRequestContext() override;
   const std::string& GetApplicationLocale() override;
   ios::ChromeBrowserStateManager* GetChromeBrowserStateManager() override;
   metrics::MetricsService* GetMetricsService() override;
+  variations::VariationsService* GetVariationsService() override;
   policy::BrowserPolicyConnector* GetBrowserPolicyConnector() override;
+  policy::PolicyService* GetPolicyService() override;
   rappor::RapporService* GetRapporService() override;
   net_log::ChromeNetLog* GetNetLog() override;
   network_time::NetworkTimeTracker* GetNetworkTimeTracker() override;
@@ -42,6 +50,7 @@
   PrefService* local_state_;
   ios::ChromeBrowserStateManager* chrome_browser_state_manager_;
   scoped_ptr<network_time::NetworkTimeTracker> network_time_tracker_;
+  bool was_last_shutdown_clean_;
 
   DISALLOW_COPY_AND_ASSIGN(TestingApplicationContext);
 };
diff --git a/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h b/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h
index 36e7f54e..beeb810 100644
--- a/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h
+++ b/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h
@@ -29,6 +29,11 @@
 // This class is a Chrome-specific extension of the BrowserState interface.
 class ChromeBrowserState : public web::BrowserState {
  public:
+  enum ExitType {
+    EXIT_NORMAL,
+    EXIT_CRASHED,
+  };
+
   ~ChromeBrowserState() override {}
 
   // Returns the ChromeBrowserState corresponding to the given BrowserState.
@@ -68,6 +73,18 @@
   // a syncable_prefs::PrefServiceSyncable.
   virtual syncable_prefs::PrefServiceSyncable* GetSyncablePrefs() = 0;
 
+  // Sets the ExitType for the ChromeBrowserState. This may be invoked multiple
+  // times during shutdown; only the first such change (the transition from
+  // EXIT_CRASHED to one of the other values) is written to prefs, any later
+  // calls are ignored.
+  //
+  // NOTE: this is invoked internally on a normal shutdown, but is public so
+  // that it can be invoked to handle backgrounding/foregrounding.
+  virtual void SetExitType(ExitType exit_type) = 0;
+
+  // Returns how the last session was shutdown.
+  virtual ExitType GetLastSessionExitType() = 0;
+
  protected:
   ChromeBrowserState() {}
 
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.cc b/ios/public/provider/chrome/browser/chrome_browser_provider.cc
index ba82d852e..b683726 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.cc
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.cc
@@ -33,10 +33,6 @@
   return nullptr;
 }
 
-PrefService* ChromeBrowserProvider::GetLocalState() {
-  return nullptr;
-}
-
 void ChromeBrowserProvider::AssertBrowserContextKeyedFactoriesBuilt() {
 }
 
@@ -97,6 +93,10 @@
   return nullptr;
 }
 
+variations::VariationsService* ChromeBrowserProvider::GetVariationsService() {
+  return nullptr;
+}
+
 autofill::CardUnmaskPromptView*
 ChromeBrowserProvider::CreateCardUnmaskPromptView(
     autofill::CardUnmaskPromptController* controller) {
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index fa6ae72..8105253 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -39,6 +39,10 @@
 class PrefRegistrySyncable;
 }
 
+namespace variations {
+class VariationsService;
+}
+
 // TODO(ios): Determine the best way to interface with Obj-C code through
 // the ChromeBrowserProvider. crbug/298181
 #ifdef __OBJC__
@@ -75,8 +79,6 @@
 
   // Gets the system URL request context.
   virtual net::URLRequestContextGetter* GetSystemURLRequestContext();
-  // Gets the local state.
-  virtual PrefService* GetLocalState();
   // Asserts all iOS-specific |BrowserContextKeyedServiceFactory| are built.
   virtual void AssertBrowserContextKeyedFactoriesBuilt();
   // Registers all prefs that will be used via the local state PrefService.
@@ -110,6 +112,8 @@
   virtual void SetUIViewAlphaWithAnimation(UIView* view, float alpha);
   // Returns the metrics service.
   virtual metrics::MetricsService* GetMetricsService();
+  // Returns the variations service.
+  virtual variations::VariationsService* GetVariationsService();
   // Returns an instance of a CardUnmaskPromptView used to unmask Wallet cards.
   // The view is responsible for its own lifetime.
   virtual autofill::CardUnmaskPromptView* CreateCardUnmaskPromptView(
diff --git a/ipc/attachment_broker.h b/ipc/attachment_broker.h
index 577d762..3f7a98c 100644
--- a/ipc/attachment_broker.h
+++ b/ipc/attachment_broker.h
@@ -63,7 +63,7 @@
   // IPC::Channel to communicate with the broker process. This may be the same
   // IPC::Channel that is requesting the brokering of an attachment.
   // Returns true on success and false otherwise.
-  virtual bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  virtual bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                        base::ProcessId destination_process) = 0;
 
   // Returns whether the attachment was available. If the attachment was
diff --git a/ipc/attachment_broker_privileged_mac.cc b/ipc/attachment_broker_privileged_mac.cc
index 7399326..0fbf2c4 100644
--- a/ipc/attachment_broker_privileged_mac.cc
+++ b/ipc/attachment_broker_privileged_mac.cc
@@ -64,7 +64,7 @@
 }
 
 bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess(
-    const BrokerableAttachment* attachment,
+    BrokerableAttachment* attachment,
     base::ProcessId destination_process) {
   switch (attachment->GetBrokerableType()) {
     case BrokerableAttachment::MACH_PORT: {
diff --git a/ipc/attachment_broker_privileged_mac.h b/ipc/attachment_broker_privileged_mac.h
index 24c8b0c..bf0418a 100644
--- a/ipc/attachment_broker_privileged_mac.h
+++ b/ipc/attachment_broker_privileged_mac.h
@@ -29,7 +29,7 @@
   void SetPortProvider(base::PortProvider* port_provider);
 
   // IPC::AttachmentBroker overrides.
-  bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override;
 
   // IPC::Listener overrides.
diff --git a/ipc/attachment_broker_privileged_win.cc b/ipc/attachment_broker_privileged_win.cc
index 6f3bc41..a638752f 100644
--- a/ipc/attachment_broker_privileged_win.cc
+++ b/ipc/attachment_broker_privileged_win.cc
@@ -19,7 +19,7 @@
 AttachmentBrokerPrivilegedWin::~AttachmentBrokerPrivilegedWin() {}
 
 bool AttachmentBrokerPrivilegedWin::SendAttachmentToProcess(
-    const BrokerableAttachment* attachment,
+    BrokerableAttachment* attachment,
     base::ProcessId destination_process) {
   switch (attachment->GetBrokerableType()) {
     case BrokerableAttachment::WIN_HANDLE: {
diff --git a/ipc/attachment_broker_privileged_win.h b/ipc/attachment_broker_privileged_win.h
index e986a7b..f40ccf5 100644
--- a/ipc/attachment_broker_privileged_win.h
+++ b/ipc/attachment_broker_privileged_win.h
@@ -20,7 +20,7 @@
   ~AttachmentBrokerPrivilegedWin() override;
 
   // IPC::AttachmentBroker overrides.
-  bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override;
 
   // IPC::Listener overrides.
diff --git a/ipc/attachment_broker_privileged_win_unittest.cc b/ipc/attachment_broker_privileged_win_unittest.cc
index 25cadcb6..02ef930 100644
--- a/ipc/attachment_broker_privileged_win_unittest.cc
+++ b/ipc/attachment_broker_privileged_win_unittest.cc
@@ -277,7 +277,7 @@
  public:
   MockBroker() {}
   ~MockBroker() override {}
-  bool SendAttachmentToProcess(const IPC::BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(IPC::BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override {
     return IPC::AttachmentBrokerUnprivilegedWin::SendAttachmentToProcess(
         attachment, base::Process::Current().Pid());
diff --git a/ipc/attachment_broker_unprivileged_mac.cc b/ipc/attachment_broker_unprivileged_mac.cc
index b4ced08..65d26f2 100644
--- a/ipc/attachment_broker_unprivileged_mac.cc
+++ b/ipc/attachment_broker_unprivileged_mac.cc
@@ -50,16 +50,19 @@
 AttachmentBrokerUnprivilegedMac::~AttachmentBrokerUnprivilegedMac() {}
 
 bool AttachmentBrokerUnprivilegedMac::SendAttachmentToProcess(
-    const BrokerableAttachment* attachment,
+    BrokerableAttachment* attachment,
     base::ProcessId destination_process) {
   switch (attachment->GetBrokerableType()) {
     case BrokerableAttachment::MACH_PORT: {
-      const internal::MachPortAttachmentMac* mach_port_attachment =
-          static_cast<const internal::MachPortAttachmentMac*>(attachment);
+      internal::MachPortAttachmentMac* mach_port_attachment =
+          static_cast<internal::MachPortAttachmentMac*>(attachment);
       internal::MachPortAttachmentMac::WireFormat format =
           mach_port_attachment->GetWireFormat(destination_process);
-      return get_sender()->Send(
-          new AttachmentBrokerMsg_DuplicateMachPort(format));
+      bool success =
+          get_sender()->Send(new AttachmentBrokerMsg_DuplicateMachPort(format));
+      if (success)
+        mach_port_attachment->reset_mach_port_ownership();
+      return success;
     }
     default:
       NOTREACHED();
diff --git a/ipc/attachment_broker_unprivileged_mac.h b/ipc/attachment_broker_unprivileged_mac.h
index 58c4f87..26cf443 100644
--- a/ipc/attachment_broker_unprivileged_mac.h
+++ b/ipc/attachment_broker_unprivileged_mac.h
@@ -22,7 +22,7 @@
   ~AttachmentBrokerUnprivilegedMac() override;
 
   // IPC::AttachmentBroker overrides.
-  bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override;
 
   // IPC::Listener overrides.
diff --git a/ipc/attachment_broker_unprivileged_win.cc b/ipc/attachment_broker_unprivileged_win.cc
index 1151f53..e1d9941 100644
--- a/ipc/attachment_broker_unprivileged_win.cc
+++ b/ipc/attachment_broker_unprivileged_win.cc
@@ -17,7 +17,7 @@
 AttachmentBrokerUnprivilegedWin::~AttachmentBrokerUnprivilegedWin() {}
 
 bool AttachmentBrokerUnprivilegedWin::SendAttachmentToProcess(
-    const BrokerableAttachment* attachment,
+    BrokerableAttachment* attachment,
     base::ProcessId destination_process) {
   switch (attachment->GetBrokerableType()) {
     case BrokerableAttachment::WIN_HANDLE: {
diff --git a/ipc/attachment_broker_unprivileged_win.h b/ipc/attachment_broker_unprivileged_win.h
index c3d29092..bcb1500a7 100644
--- a/ipc/attachment_broker_unprivileged_win.h
+++ b/ipc/attachment_broker_unprivileged_win.h
@@ -22,7 +22,7 @@
   ~AttachmentBrokerUnprivilegedWin() override;
 
   // IPC::AttachmentBroker overrides.
-  bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override;
 
   // IPC::Listener overrides.
diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc
index b9ab794..abe2f98 100644
--- a/ipc/ipc_channel_posix.cc
+++ b/ipc/ipc_channel_posix.cc
@@ -697,8 +697,8 @@
   if (message->HasBrokerableAttachments()) {
     DCHECK(GetAttachmentBroker());
     DCHECK(peer_pid_ != base::kNullProcessId);
-    for (const BrokerableAttachment* attachment :
-         message->attachment_set()->PeekBrokerableAttachments()) {
+    for (BrokerableAttachment* attachment :
+         message->attachment_set()->GetBrokerableAttachments()) {
       if (!GetAttachmentBroker()->SendAttachmentToProcess(attachment,
                                                           peer_pid_)) {
         delete message;
diff --git a/ipc/ipc_channel_reader.cc b/ipc/ipc_channel_reader.cc
index 5ca0e46..326c6d7e 100644
--- a/ipc/ipc_channel_reader.cc
+++ b/ipc/ipc_channel_reader.cc
@@ -213,8 +213,8 @@
 
 #if USE_ATTACHMENT_BROKER
   MessageAttachmentSet* set = msg->attachment_set();
-  std::vector<const BrokerableAttachment*> brokerable_attachments_copy =
-      set->PeekBrokerableAttachments();
+  std::vector<BrokerableAttachment*> brokerable_attachments_copy =
+      set->GetBrokerableAttachments();
   for (const BrokerableAttachment* attachment : brokerable_attachments_copy) {
     if (attachment->NeedsBrokering()) {
       AttachmentBroker* broker = GetAttachmentBroker();
diff --git a/ipc/ipc_channel_reader_unittest.cc b/ipc/ipc_channel_reader_unittest.cc
index 7728a817..c528996b 100644
--- a/ipc/ipc_channel_reader_unittest.cc
+++ b/ipc/ipc_channel_reader_unittest.cc
@@ -42,7 +42,7 @@
  public:
   typedef std::set<scoped_refptr<BrokerableAttachment>> AttachmentSet;
 
-  bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+  bool SendAttachmentToProcess(BrokerableAttachment* attachment,
                                base::ProcessId destination_process) override {
     return false;
   }
diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc
index c04f0c6..d4640f4 100644
--- a/ipc/ipc_channel_win.cc
+++ b/ipc/ipc_channel_win.cc
@@ -108,8 +108,8 @@
   if (message->HasBrokerableAttachments()) {
     DCHECK(GetAttachmentBroker());
     DCHECK(peer_pid_ != base::kNullProcessId);
-    for (const BrokerableAttachment* attachment :
-         message->attachment_set()->PeekBrokerableAttachments()) {
+    for (BrokerableAttachment* attachment :
+         message->attachment_set()->GetBrokerableAttachments()) {
       if (!GetAttachmentBroker()->SendAttachmentToProcess(attachment,
                                                           peer_pid_)) {
         delete message;
diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc
index df28464e..684c11b 100644
--- a/ipc/ipc_message.cc
+++ b/ipc/ipc_message.cc
@@ -144,8 +144,8 @@
 Message::SerializedAttachmentIds
 Message::SerializedIdsOfBrokerableAttachments() {
   DCHECK(HasBrokerableAttachments());
-  std::vector<const BrokerableAttachment*> attachments =
-      attachment_set_->PeekBrokerableAttachments();
+  std::vector<BrokerableAttachment*> attachments =
+      attachment_set_->GetBrokerableAttachments();
   CHECK_LE(attachments.size(), std::numeric_limits<size_t>::max() /
                                    BrokerableAttachment::kNonceSize);
   size_t size = attachments.size() * BrokerableAttachment::kNonceSize;
diff --git a/ipc/ipc_message_attachment_set.cc b/ipc/ipc_message_attachment_set.cc
index 6a8024f..faf6462 100644
--- a/ipc/ipc_message_attachment_set.cc
+++ b/ipc/ipc_message_attachment_set.cc
@@ -131,9 +131,9 @@
   consumed_descriptor_highwater_ = 0;
 }
 
-std::vector<const BrokerableAttachment*>
-MessageAttachmentSet::PeekBrokerableAttachments() const {
-  std::vector<const BrokerableAttachment*> output;
+std::vector<BrokerableAttachment*>
+MessageAttachmentSet::GetBrokerableAttachments() const {
+  std::vector<BrokerableAttachment*> output;
   for (const scoped_refptr<MessageAttachment>& attachment : attachments_) {
     if (attachment->GetType() ==
         MessageAttachment::TYPE_BROKERABLE_ATTACHMENT) {
diff --git a/ipc/ipc_message_attachment_set.h b/ipc/ipc_message_attachment_set.h
index 4707a50..0ed62ac 100644
--- a/ipc/ipc_message_attachment_set.h
+++ b/ipc/ipc_message_attachment_set.h
@@ -59,7 +59,7 @@
   void CommitAll();
 
   // Returns a vector of all brokerable attachments.
-  std::vector<const BrokerableAttachment*> PeekBrokerableAttachments() const;
+  std::vector<BrokerableAttachment*> GetBrokerableAttachments() const;
 
   // Replaces a placeholder brokerable attachment with |attachment|, matching
   // them by their id.
diff --git a/ipc/mach_port_attachment_mac.cc b/ipc/mach_port_attachment_mac.cc
index 2a0cfa44..5aec8fcd 100644
--- a/ipc/mach_port_attachment_mac.cc
+++ b/ipc/mach_port_attachment_mac.cc
@@ -4,21 +4,40 @@
 
 #include "ipc/mach_port_attachment_mac.h"
 
+#include "base/mac/mach_logging.h"
+
 namespace IPC {
 namespace internal {
 
 MachPortAttachmentMac::MachPortAttachmentMac(mach_port_t mach_port)
-    : mach_port_(mach_port) {}
+    : mach_port_(mach_port), owns_mach_port_(true) {
+  if (mach_port != MACH_PORT_NULL) {
+    kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_port,
+                                          MACH_PORT_RIGHT_SEND, 1);
+    MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+        << "MachPortAttachmentMac mach_port_mod_refs";
+  }
+}
 
 MachPortAttachmentMac::MachPortAttachmentMac(const WireFormat& wire_format)
     : BrokerableAttachment(wire_format.attachment_id),
-      mach_port_(static_cast<mach_port_t>(wire_format.mach_port)) {}
+      mach_port_(static_cast<mach_port_t>(wire_format.mach_port)),
+      owns_mach_port_(false) {}
 
 MachPortAttachmentMac::MachPortAttachmentMac(
     const BrokerableAttachment::AttachmentId& id)
-    : BrokerableAttachment(id), mach_port_(MACH_PORT_NULL) {}
+    : BrokerableAttachment(id),
+      mach_port_(MACH_PORT_NULL),
+      owns_mach_port_(false) {}
 
-MachPortAttachmentMac::~MachPortAttachmentMac() {}
+MachPortAttachmentMac::~MachPortAttachmentMac() {
+  if (mach_port_ != MACH_PORT_NULL && owns_mach_port_) {
+    kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_port_,
+                                          MACH_PORT_RIGHT_SEND, -1);
+    MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+        << "~MachPortAttachmentMac mach_port_mod_refs";
+  }
+}
 
 MachPortAttachmentMac::BrokerableType MachPortAttachmentMac::GetBrokerableType()
     const {
diff --git a/ipc/mach_port_attachment_mac.h b/ipc/mach_port_attachment_mac.h
index efe93c4..48dc9aa 100644
--- a/ipc/mach_port_attachment_mac.h
+++ b/ipc/mach_port_attachment_mac.h
@@ -45,7 +45,13 @@
     AttachmentId attachment_id;
   };
 
+  // This constructor increments the ref count of |mach_port_| and takes
+  // ownership of the result. Should only be called by the sender of a Chrome
+  // IPC message.
   explicit MachPortAttachmentMac(mach_port_t mach_port);
+
+  // These constructors do not take ownership of |mach_port|, and should only be
+  // called by the receiver of a Chrome IPC message.
   explicit MachPortAttachmentMac(const WireFormat& wire_format);
   explicit MachPortAttachmentMac(const BrokerableAttachment::AttachmentId& id);
 
@@ -56,9 +62,21 @@
 
   mach_port_t get_mach_port() const { return mach_port_; }
 
+  // The caller of this method has taken ownership of |mach_port_|.
+  void reset_mach_port_ownership() { owns_mach_port_ = false; }
+
  private:
   ~MachPortAttachmentMac() override;
   mach_port_t mach_port_;
+
+  // In the sender process, the attachment owns the Mach port of a newly created
+  // message. The attachment broker will eventually take ownership, and set
+  // this member to |false|.
+  // In the destination process, the attachment never owns the Mach port. The
+  // client code that receives the Chrome IPC message is always expected to take
+  // ownership.
+  bool owns_mach_port_;
+  DISALLOW_COPY_AND_ASSIGN(MachPortAttachmentMac);
 };
 
 }  // namespace internal
diff --git a/ipc/mach_port_mac.h b/ipc/mach_port_mac.h
index 0193f9da..199147c 100644
--- a/ipc/mach_port_mac.h
+++ b/ipc/mach_port_mac.h
@@ -7,24 +7,65 @@
 
 #include <mach/mach.h>
 
+#include "base/macros.h"
 #include "ipc/ipc_export.h"
 #include "ipc/ipc_message_macros.h"
 
 namespace IPC {
 
-// MachPortMac is a wrapper around an OSX mach_port_t that can be transported
-// across Chrome IPC channels that support attachment brokering. The mach_port_t
-// will be duplicated into the destination process by the attachment broker.
+// MachPortMac is a wrapper around an OSX Mach port that can be transported
+// across Chrome IPC channels that support attachment brokering. The send right
+// to the Mach port will be duplicated into the destination process by the
+// attachment broker. If needed, attachment brokering can be trivially extended
+// to support duplication of other types of rights.
 class IPC_EXPORT MachPortMac {
  public:
   MachPortMac() : mach_port_(MACH_PORT_NULL) {}
-  explicit MachPortMac(const mach_port_t& mach_port) : mach_port_(mach_port) {}
+
+  explicit MachPortMac(mach_port_t mach_port) {};
 
   mach_port_t get_mach_port() const { return mach_port_; }
+
+  // This method should only be used by ipc/ translation code.
   void set_mach_port(mach_port_t mach_port) { mach_port_ = mach_port; }
 
  private:
+  // The ownership semantics of |mach_port_| cannot be easily expressed with a
+  // C++ scoped object. This is partly due to the mechanism by which Mach ports
+  // are brokered, and partly due to the architecture of Chrome IPC.
+  //
+  // The broker for Mach ports may live in a different process than the sender
+  // of the original Chrome IPC. In this case, it is signalled asynchronously,
+  // and ownership of the Mach port passes from the sender process into the
+  // broker process.
+  //
+  // Chrome IPC is written with the assumption that translation is a stateless
+  // process. There is no good mechanism to convey the semantics of ownership
+  // transfer from the Chrome IPC stack into the client code that receives the
+  // translated message. As a result, Chrome IPC code assumes that every message
+  // has a handler, and that the handler will take ownership of the Mach port.
+  // Note that the same holds true for POSIX fds and Windows HANDLEs.
+  //
+  // When used by client code in the sender process, this class is just a
+  // wrapper. The client code calls Send(new Message(MachPortMac(mach_port)))
+  // and continues on its merry way. Behind the scenes, a MachPortAttachmentMac
+  // takes ownership of the Mach port. When the attachment broker sends the name
+  // of the Mach port to the broker process, it also releases
+  // MachPortAttachmentMac's reference to the Mach port, as ownership has
+  // effectively been transferred to the broker process.
+  //
+  // The broker process receives the name, duplicates the Mach port into the
+  // destination process, and then decrements the ref count in the original
+  // process.
+  //
+  // In the destination process, the attachment broker is responsible for
+  // coupling the Mach port (inserted by the broker process) with Chrome IPC in
+  // the form of a MachPortAttachmentMac. Due to the Chrome IPC translation
+  // semantics discussed above, this MachPortAttachmentMac does not take
+  // ownership of the Mach port, and assumes that the client code which receives
+  // the callback will take ownership of the Mach port.
   mach_port_t mach_port_;
+  DISALLOW_COPY_AND_ASSIGN(MachPortMac);
 };
 
 template <>
diff --git a/media/base/android/audio_decoder_job.cc b/media/base/android/audio_decoder_job.cc
index e532103..4ef622a 100644
--- a/media/base/android/audio_decoder_job.cc
+++ b/media/base/android/audio_decoder_job.cc
@@ -8,7 +8,6 @@
 #include "base/lazy_instance.h"
 #include "base/threading/thread.h"
 #include "media/base/android/media_codec_bridge.h"
-#include "media/base/android/media_statistics.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/timestamp_constants.h"
 
@@ -36,12 +35,10 @@
 
 AudioDecoderJob::AudioDecoderJob(
     const base::Closure& request_data_cb,
-    const base::Closure& on_demuxer_config_changed_cb,
-    FrameStatistics* frame_statistics)
+    const base::Closure& on_demuxer_config_changed_cb)
     : MediaDecoderJob(g_audio_decoder_thread.Pointer()->task_runner(),
                       request_data_cb,
-                      on_demuxer_config_changed_cb,
-                      frame_statistics),
+                      on_demuxer_config_changed_cb),
       audio_codec_(kUnknownAudioCodec),
       num_channels_(0),
       config_sampling_rate_(0),
@@ -102,9 +99,11 @@
     size_t offset,
     size_t size,
     bool render_output,
+    bool /* is_late_frame */,
     base::TimeDelta current_presentation_timestamp,
     const ReleaseOutputCompletionCallback& callback) {
   render_output = render_output && (size != 0u);
+  bool is_audio_underrun = false;
   if (render_output) {
     int64 head_position = (static_cast<AudioCodecBridge*>(
         media_codec_bridge_.get()))->PlayOutputBuffer(
@@ -125,10 +124,9 @@
         last_buffered -
         audio_timestamp_helper_->GetFrameDuration(frames_to_play);
 
-    if (!next_frame_time_limit_.is_null() &&
-        next_frame_time_limit_ < current_time) {
-      frame_statistics_->IncrementLateFrameCount();
-    }
+    // Potential audio underrun is considered a late frame for UMA.
+    is_audio_underrun = !next_frame_time_limit_.is_null() &&
+                        next_frame_time_limit_ < current_time;
 
     next_frame_time_limit_ =
         current_time + (last_buffered - current_presentation_timestamp);
@@ -137,7 +135,7 @@
   }
   media_codec_bridge_->ReleaseOutputBuffer(output_buffer_index, false);
 
-  callback.Run(current_presentation_timestamp,
+  callback.Run(is_audio_underrun, current_presentation_timestamp,
                audio_timestamp_helper_->GetTimestamp());
 }
 
diff --git a/media/base/android/audio_decoder_job.h b/media/base/android/audio_decoder_job.h
index baee4a35..f302a59 100644
--- a/media/base/android/audio_decoder_job.h
+++ b/media/base/android/audio_decoder_job.h
@@ -24,8 +24,7 @@
   // |on_demuxer_config_changed_cb| - Callback used to inform the caller that
   // demuxer config has changed.
   AudioDecoderJob(const base::Closure& request_data_cb,
-                  const base::Closure& on_demuxer_config_changed_cb,
-                  FrameStatistics* frame_statistics);
+                  const base::Closure& on_demuxer_config_changed_cb);
   ~AudioDecoderJob() override;
 
   // MediaDecoderJob implementation.
@@ -46,6 +45,7 @@
       size_t offset,
       size_t size,
       bool render_output,
+      bool is_late_frame,
       base::TimeDelta current_presentation_timestamp,
       const ReleaseOutputCompletionCallback& callback) override;
   bool ComputeTimeToRender() const override;
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index 855a813b..3bf5279 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -10,7 +10,6 @@
 #include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/android/media_drm_bridge.h"
-#include "media/base/android/media_statistics.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/timestamp_constants.h"
 
@@ -24,10 +23,8 @@
 MediaDecoderJob::MediaDecoderJob(
     const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner,
     const base::Closure& request_data_cb,
-    const base::Closure& config_changed_cb,
-    FrameStatistics* frame_statistics)
+    const base::Closure& config_changed_cb)
     : need_to_reconfig_decoder_job_(false),
-      frame_statistics_(frame_statistics),
       ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       decoder_task_runner_(decoder_task_runner),
       needs_flush_(false),
@@ -91,7 +88,7 @@
 
   if (stop_decode_pending_) {
     DCHECK(is_decoding());
-    OnDecodeCompleted(MEDIA_CODEC_ABORT, kNoTimestamp(), kNoTimestamp());
+    OnDecodeCompleted(MEDIA_CODEC_ABORT, false, kNoTimestamp(), kNoTimestamp());
     return;
   }
 
@@ -364,7 +361,7 @@
         // MEDIA_CODEC_OUTPUT_FORMAT_CHANGED status will come later.
         ui_task_runner_->PostTask(FROM_HERE, base::Bind(
             &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this),
-            MEDIA_CODEC_OK, kNoTimestamp(), kNoTimestamp()));
+            MEDIA_CODEC_OK, false, kNoTimestamp(), kNoTimestamp()));
         return;
       }
       // Start draining the decoder so that all the remaining frames are
@@ -400,7 +397,7 @@
     input_buf_index_ = -1;
     MediaCodecStatus reset_status = media_codec_bridge_->Reset();
     if (MEDIA_CODEC_OK != reset_status) {
-      callback.Run(reset_status, kNoTimestamp(), kNoTimestamp());
+      callback.Run(reset_status, false, kNoTimestamp(), kNoTimestamp());
       return;
     }
   }
@@ -412,7 +409,7 @@
 
   // For aborted access unit, just skip it and inform the player.
   if (unit.status == DemuxerStream::kAborted) {
-    callback.Run(MEDIA_CODEC_ABORT, kNoTimestamp(), kNoTimestamp());
+    callback.Run(MEDIA_CODEC_ABORT, false, kNoTimestamp(), kNoTimestamp());
     return;
   }
 
@@ -420,7 +417,7 @@
     if (unit.is_end_of_stream || unit.data.empty()) {
       input_eos_encountered_ = true;
       output_eos_encountered_ = true;
-      callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(),
+      callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, false, kNoTimestamp(),
                    kNoTimestamp());
       return;
     }
@@ -438,7 +435,7 @@
       // change can be resolved. Context: b/21786703
       DVLOG(1) << "dequeueInputBuffer gave AGAIN_LATER, dequeue output buffers";
     } else if (input_status != MEDIA_CODEC_OK) {
-      callback.Run(input_status, kNoTimestamp(), kNoTimestamp());
+      callback.Run(input_status, false, kNoTimestamp(), kNoTimestamp());
       return;
     }
   }
@@ -474,7 +471,7 @@
            status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
 
   if (status != MEDIA_CODEC_OK) {
-    callback.Run(status, kNoTimestamp(), kNoTimestamp());
+    callback.Run(status, false, kNoTimestamp(), kNoTimestamp());
     return;
   }
 
@@ -488,32 +485,19 @@
       (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
   base::TimeDelta time_to_render;
   DCHECK(!start_time_ticks.is_null());
-  if (render_output) {
-    frame_statistics_->IncrementFrameCount();
-
-    if (ComputeTimeToRender()) {
-      time_to_render =
-          presentation_timestamp - (base::TimeTicks::Now() - start_time_ticks +
-                                    start_presentation_timestamp);
-
-      // ComputeTimeToRender() returns true for video streams only, this is a
-      // video stream.
-      if (time_to_render < base::TimeDelta())
-        frame_statistics_->IncrementLateFrameCount();
-    }
+  if (render_output && ComputeTimeToRender()) {
+    time_to_render = presentation_timestamp - (base::TimeTicks::Now() -
+        start_time_ticks + start_presentation_timestamp);
   }
 
   if (time_to_render > base::TimeDelta()) {
     decoder_task_runner_->PostDelayedTask(
         FROM_HERE,
         base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
-                   base::Unretained(this),
-                   buffer_index,
-                   offset,
-                   size,
+                   base::Unretained(this), buffer_index, offset, size,
                    render_output,
-                   presentation_timestamp,
-                   base::Bind(callback, status)),
+                   false,  // this is not a late frame
+                   presentation_timestamp, base::Bind(callback, status)),
         time_to_render);
     return;
   }
@@ -531,14 +515,19 @@
   } else {
     presentation_timestamp = kNoTimestamp();
   }
+
   ReleaseOutputCompletionCallback completion_callback = base::Bind(
       callback, status);
-  ReleaseOutputBuffer(buffer_index, offset, size, render_output,
+
+  const bool is_late_frame = (time_to_render < base::TimeDelta());
+  ReleaseOutputBuffer(buffer_index, offset, size, render_output, is_late_frame,
                       presentation_timestamp, completion_callback);
 }
 
 void MediaDecoderJob::OnDecodeCompleted(
-    MediaCodecStatus status, base::TimeDelta current_presentation_timestamp,
+    MediaCodecStatus status,
+    bool is_late_frame,
+    base::TimeDelta current_presentation_timestamp,
     base::TimeDelta max_presentation_timestamp) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
@@ -600,8 +589,9 @@
   }
 
   stop_decode_pending_ = false;
-  base::ResetAndReturn(&decode_cb_).Run(
-      status, current_presentation_timestamp, max_presentation_timestamp);
+  base::ResetAndReturn(&decode_cb_)
+      .Run(status, is_late_frame, current_presentation_timestamp,
+           max_presentation_timestamp);
 }
 
 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index df7f454..752ac882 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -18,7 +18,6 @@
 
 namespace media {
 
-struct FrameStatistics;
 class MediaDrmBridge;
 
 // Class for managing all the decoding tasks. Each decoding task will be posted
@@ -40,17 +39,20 @@
   };
 
   // Callback when a decoder job finishes its work. Args: whether decode
-  // finished successfully, current presentation time, max presentation time.
+  // finished successfully, a flag whether the frame is late for statistics,
+  // cacurrent presentation time, max presentation time.
   // If the current presentation time is equal to kNoTimestamp(), the decoder
   // job skipped rendering of the decoded output and the callback target should
-  // ignore the timestamps provided.
-  typedef base::Callback<void(MediaCodecStatus, base::TimeDelta,
+  // ignore the timestamps provided. The late frame flag has no meaning in this
+  // case.
+  typedef base::Callback<void(MediaCodecStatus, bool, base::TimeDelta,
                               base::TimeDelta)> DecoderCallback;
   // Callback when a decoder job finishes releasing the output buffer.
-  // Args: current presentation time, max presentation time.
+  // Args: whether the frame is a late frame, current presentation time, max
+  // presentation time.
   // If the current presentation time is equal to kNoTimestamp(), the callback
-  // target should ignore the timestamps provided.
-  typedef base::Callback<void(base::TimeDelta, base::TimeDelta)>
+  // target should ignore the timestamps provided and whether it is late.
+  typedef base::Callback<void(bool, base::TimeDelta, base::TimeDelta)>
       ReleaseOutputCompletionCallback;
 
   virtual ~MediaDecoderJob();
@@ -118,16 +120,18 @@
   MediaDecoderJob(
       const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner,
       const base::Closure& request_data_cb,
-      const base::Closure& config_changed_cb,
-      FrameStatistics* frame_statistics);
+      const base::Closure& config_changed_cb);
 
   // Release the output buffer at index |output_buffer_index| and render it if
   // |render_output| is true. Upon completion, |callback| will be called.
+  // |is_late_frame| can be passed with the |callback| if the implementation
+  // does not calculate it itself.
   virtual void ReleaseOutputBuffer(
       int output_buffer_index,
       size_t offset,
       size_t size,
       bool render_output,
+      bool is_late_frame,
       base::TimeDelta current_presentation_timestamp,
       const ReleaseOutputCompletionCallback& callback) = 0;
 
@@ -156,8 +160,6 @@
 
   scoped_ptr<MediaCodecBridge> media_codec_bridge_;
 
-  FrameStatistics* frame_statistics_;
-
  private:
   friend class MediaSourcePlayerTest;
 
@@ -200,6 +202,7 @@
   // Completes any pending job destruction or any pending decode stop. If
   // destruction was not pending, passes its arguments to |decode_cb_|.
   void OnDecodeCompleted(MediaCodecStatus status,
+                         bool is_late_frame,
                          base::TimeDelta current_presentation_timestamp,
                          base::TimeDelta max_presentation_timestamp);
 
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index 4674191..d4408b3 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -54,15 +54,13 @@
                  base::Unretained(demuxer_.get()),
                  DemuxerStream::AUDIO),
       base::Bind(&MediaSourcePlayer::OnDemuxerConfigsChanged,
-                 weak_factory_.GetWeakPtr()),
-      &media_stat_->audio_frame_stats()));
+                 weak_factory_.GetWeakPtr())));
   video_decoder_job_.reset(new VideoDecoderJob(
       base::Bind(&DemuxerAndroid::RequestDemuxerData,
                  base::Unretained(demuxer_.get()),
                  DemuxerStream::VIDEO),
       base::Bind(&MediaSourcePlayer::OnDemuxerConfigsChanged,
-                 weak_factory_.GetWeakPtr()),
-      &media_stat_->video_frame_stats()));
+                 weak_factory_.GetWeakPtr())));
 
   demuxer_->Initialize(this);
   interpolator_.SetUpperBound(base::TimeDelta());
@@ -439,7 +437,9 @@
 }
 
 void MediaSourcePlayer::MediaDecoderCallback(
-    bool is_audio, MediaCodecStatus status,
+    bool is_audio,
+    MediaCodecStatus status,
+    bool is_late_frame,
     base::TimeDelta current_presentation_timestamp,
     base::TimeDelta max_presentation_timestamp) {
   DVLOG(1) << __FUNCTION__ << ": " << is_audio << ", " << status;
@@ -476,6 +476,15 @@
     return;
   }
 
+  // Increment frame counts for UMA.
+  if (current_presentation_timestamp != kNoTimestamp()) {
+    FrameStatistics& frame_stats = is_audio ? media_stat_->audio_frame_stats()
+                                            : media_stat_->video_frame_stats();
+    frame_stats.IncrementFrameCount();
+    if (is_late_frame)
+      frame_stats.IncrementLateFrameCount();
+  }
+
   DCHECK(!IsEventPending(PREFETCH_DONE_EVENT_PENDING));
 
   // Let |SEEK_EVENT_PENDING| (the highest priority event outside of
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index eb4e3fc..7e4d706 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -85,10 +85,11 @@
   void PlaybackCompleted(bool is_audio);
 
   // Called when the decoder finishes its task.
-  void MediaDecoderCallback(
-        bool is_audio, MediaCodecStatus status,
-        base::TimeDelta current_presentation_timestamp,
-        base::TimeDelta max_presentation_timestamp);
+  void MediaDecoderCallback(bool is_audio,
+                            MediaCodecStatus status,
+                            bool is_late_frame,
+                            base::TimeDelta current_presentation_timestamp,
+                            base::TimeDelta max_presentation_timestamp);
 
   bool IsPrerollFinished(bool is_audio) const;
 
diff --git a/media/base/android/video_decoder_job.cc b/media/base/android/video_decoder_job.cc
index 4c2cc76..d43f450 100644
--- a/media/base/android/video_decoder_job.cc
+++ b/media/base/android/video_decoder_job.cc
@@ -27,12 +27,10 @@
 
 VideoDecoderJob::VideoDecoderJob(
     const base::Closure& request_data_cb,
-    const base::Closure& on_demuxer_config_changed_cb,
-    FrameStatistics* frame_statistics)
+    const base::Closure& on_demuxer_config_changed_cb)
     : MediaDecoderJob(g_video_decoder_thread.Pointer()->task_runner(),
                       request_data_cb,
-                      on_demuxer_config_changed_cb,
-                      frame_statistics),
+                      on_demuxer_config_changed_cb),
       video_codec_(kUnknownVideoCodec),
       config_width_(0),
       config_height_(0),
@@ -81,10 +79,12 @@
     size_t offset,
     size_t size,
     bool render_output,
+    bool is_late_frame,
     base::TimeDelta current_presentation_timestamp,
     const ReleaseOutputCompletionCallback& callback) {
   media_codec_bridge_->ReleaseOutputBuffer(output_buffer_index, render_output);
-  callback.Run(current_presentation_timestamp, current_presentation_timestamp);
+  callback.Run(is_late_frame, current_presentation_timestamp,
+               current_presentation_timestamp);
 }
 
 bool VideoDecoderJob::ComputeTimeToRender() const {
diff --git a/media/base/android/video_decoder_job.h b/media/base/android/video_decoder_job.h
index 372fe1ab..29d5761 100644
--- a/media/base/android/video_decoder_job.h
+++ b/media/base/android/video_decoder_job.h
@@ -21,8 +21,7 @@
   // |on_demuxer_config_changed_cb| - Callback used to inform the caller that
   // demuxer config has changed.
   VideoDecoderJob(const base::Closure& request_data_cb,
-                  const base::Closure& on_demuxer_config_changed_cb,
-                  FrameStatistics* frame_statistics);
+                  const base::Closure& on_demuxer_config_changed_cb);
   ~VideoDecoderJob() override;
 
   // Passes a java surface object to the codec. Returns true if the surface
@@ -44,6 +43,7 @@
       size_t offset,
       size_t size,
       bool render_output,
+      bool is_late_frame,
       base::TimeDelta current_presentation_timestamp,
       const ReleaseOutputCompletionCallback& callback) override;
   bool ComputeTimeToRender() const override;
diff --git a/mojo/gles2/command_buffer_client_impl.cc b/mojo/gles2/command_buffer_client_impl.cc
index 3548c1f..ba16771 100644
--- a/mojo/gles2/command_buffer_client_impl.cc
+++ b/mojo/gles2/command_buffer_client_impl.cc
@@ -128,6 +128,8 @@
       last_put_offset_(-1),
       next_transfer_buffer_id_(0),
       next_image_id_(0),
+      next_fence_sync_release_(1),
+      flushed_fence_sync_release_(0),
       async_waiter_(async_waiter) {
   command_buffer_.Bind(mojo::InterfacePtrInfo<mojo::CommandBuffer>(
                            command_buffer_handle.Pass(), 0u),
@@ -190,6 +192,7 @@
 
   last_put_offset_ = put_offset;
   command_buffer_->Flush(put_offset);
+  flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
 }
 
 void CommandBufferClientImpl::OrderingBarrier(int32_t put_offset) {
@@ -408,4 +411,16 @@
   return 0;
 }
 
+uint64_t CommandBufferClientImpl::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool CommandBufferClientImpl::IsFenceSyncRelease(uint64_t release) {
+  return release != 0 && release < next_fence_sync_release_;
+}
+
+bool CommandBufferClientImpl::IsFenceSyncFlushed(uint64_t release) {
+  return release != 0 && release <= flushed_fence_sync_release_;
+}
+
 }  // namespace gles2
diff --git a/mojo/gles2/command_buffer_client_impl.h b/mojo/gles2/command_buffer_client_impl.h
index b5e2db322..2c3c925 100644
--- a/mojo/gles2/command_buffer_client_impl.h
+++ b/mojo/gles2/command_buffer_client_impl.h
@@ -76,6 +76,9 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
  private:
   class SyncClientImpl;
@@ -106,6 +109,9 @@
   // Image IDs are allocated in sequence.
   int next_image_id_;
 
+  uint64_t next_fence_sync_release_;
+  uint64_t flushed_fence_sync_release_;
+
   const MojoAsyncWaiter* async_waiter_;
 };
 
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc
index 5237b66..f256ba0 100644
--- a/mojo/gpu/mojo_gles2_impl_autogen.cc
+++ b/mojo/gpu/mojo_gles2_impl_autogen.cc
@@ -1657,6 +1657,19 @@
   MojoGLES2MakeCurrent(context_);
   glWaitSyncPointCHROMIUM(sync_point);
 }
+GLuint64 MojoGLES2Impl::InsertFenceSyncCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  return glInsertFenceSyncCHROMIUM();
+}
+void MojoGLES2Impl::GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                         GLbyte* sync_token) {
+  MojoGLES2MakeCurrent(context_);
+  glGenSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void MojoGLES2Impl::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+  MojoGLES2MakeCurrent(context_);
+  glWaitSyncTokenCHROMIUM(sync_token);
+}
 void MojoGLES2Impl::DrawBuffersEXT(GLsizei count, const GLenum* bufs) {
   MojoGLES2MakeCurrent(context_);
   glDrawBuffersEXT(count, bufs);
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h
index 872c4f7..6d9d0a8 100644
--- a/mojo/gpu/mojo_gles2_impl_autogen.h
+++ b/mojo/gpu/mojo_gles2_impl_autogen.h
@@ -765,6 +765,9 @@
   void LoseContextCHROMIUM(GLenum current, GLenum other) override;
   GLuint InsertSyncPointCHROMIUM() override;
   void WaitSyncPointCHROMIUM(GLuint sync_point) override;
+  GLuint64 InsertFenceSyncCHROMIUM() override;
+  void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+  void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
   void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
   void DiscardBackbufferCHROMIUM() override;
   void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
diff --git a/mojo/tools/mopy/gtest.py b/mojo/tools/mopy/gtest.py
index e037da3..1b3afbb 100644
--- a/mojo/tools/mopy/gtest.py
+++ b/mojo/tools/mopy/gtest.py
@@ -194,11 +194,19 @@
     timeout_exception = '\nError: Test timeout after %s seconds' % seconds
     logging.getLogger().debug('Killing the runner or shell for timeout.')
     try:
-      process_or_shell.kill()
-    except OSError:
-      pass  # The process may have ended after checking |is_alive|.
+      if sys.platform.startswith('win'):
+        # Taskkill is more reliable than Popen.kill() on Win; crbug.com/517661
+        killer = ['taskkill.exe', '/f', '/t', '/pid', str(process_or_shell.pid)]
+        logging.getLogger().debug(subprocess.check_output(killer))
+      else:
+        process_or_shell.kill()
+    except subprocess.CalledProcessError as e:
+      logging.getLogger().debug('CalledProcessError: %s' % e)
+    except OSError as e:
+      # The process may have ended after checking |thread.is_alive()|.
+      logging.getLogger().debug('OSError (likely ignorable): %s' % e)
+    thread.join(seconds)
 
-  thread.join(seconds)
   if thread.is_alive():
     raise Exception('Error: Test hung and could not be killed!')
   if result.empty():
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index ad2c8302..e53c4b0b 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -357,6 +357,12 @@
 // OK but was missing some of the fields.
 NET_ERROR(CT_STH_INCOMPLETE, -169)
 
+// The attempt to reuse a connection to send proxy auth credentials failed
+// before the AuthController was used to generate credentials. The caller should
+// reuse the controller with a new connection. This error is only used
+// internally by the network stack.
+NET_ERROR(UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH, -170)
+
 // Certificate error codes
 //
 // The values of certificate error codes must be consecutive.
diff --git a/net/base/dns_reloader.cc b/net/dns/dns_reloader.cc
similarity index 98%
rename from net/base/dns_reloader.cc
rename to net/dns/dns_reloader.cc
index 18041d9fc..8298c78 100644
--- a/net/base/dns_reloader.cc
+++ b/net/dns/dns_reloader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/base/dns_reloader.h"
+#include "net/dns/dns_reloader.h"
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \
     !defined(OS_ANDROID)
diff --git a/net/base/dns_reloader.h b/net/dns/dns_reloader.h
similarity index 86%
rename from net/base/dns_reloader.h
rename to net/dns/dns_reloader.h
index 889d404..4317d9ea 100644
--- a/net/base/dns_reloader.h
+++ b/net/dns/dns_reloader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef NET_BASE_DNS_RELOADER_H_
-#define NET_BASE_DNS_RELOADER_H_
+#ifndef NET_DNS_DNS_RELOADER_H_
+#define NET_DNS_DNS_RELOADER_H_
 
 #include "build/build_config.h"
 
@@ -20,4 +20,4 @@
 }  // namespace net
 #endif  // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
 
-#endif  // NET_BASE_DNS_RELOADER_H_
+#endif  // NET_DNS_DNS_RELOADER_H_
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 97345a0..417b3ae 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -35,7 +35,6 @@
 #include "base/values.h"
 #include "net/base/address_family.h"
 #include "net/base/address_list.h"
-#include "net/base/dns_reloader.h"
 #include "net/base/dns_util.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_endpoint.h"
@@ -45,6 +44,7 @@
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config_service.h"
 #include "net/dns/dns_protocol.h"
+#include "net/dns/dns_reloader.h"
 #include "net/dns/dns_response.h"
 #include "net/dns/dns_transaction.h"
 #include "net/dns/host_resolver_proc.h"
diff --git a/net/dns/host_resolver_proc.cc b/net/dns/host_resolver_proc.cc
index 98b9812..fbb40ca 100644
--- a/net/dns/host_resolver_proc.cc
+++ b/net/dns/host_resolver_proc.cc
@@ -9,10 +9,10 @@
 #include "base/logging.h"
 #include "base/sys_byteorder.h"
 #include "net/base/address_list.h"
-#include "net/base/dns_reloader.h"
 #include "net/base/dns_util.h"
 #include "net/base/net_errors.h"
 #include "net/base/sys_addrinfo.h"
+#include "net/dns/dns_reloader.h"
 
 #if defined(OS_OPENBSD)
 #define AI_ADDRCONFIG 0
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 37efedc..670db0b6 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -3319,6 +3319,446 @@
       NetLog::PHASE_NONE);
 }
 
+// Test a proxy auth scheme that allows default credentials and a proxy server
+// that uses non-persistent connections.
+TEST_P(HttpNetworkTransactionTest,
+       AuthAllowsDefaultCredentialsTunnelConnectionClose) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+
+  // Configure against proxy server "myproxy:70".
+  session_deps_.proxy_service =
+      ProxyService::CreateFixedFromPacResult("PROXY myproxy:70");
+
+  scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory(
+      new HttpAuthHandlerMock::Factory());
+  auth_handler_factory->set_do_init_from_challenge(true);
+  scoped_ptr<HttpAuthHandlerMock> mock_handler(new HttpAuthHandlerMock());
+  mock_handler->set_allows_default_credentials(true);
+  auth_handler_factory->AddMockHandler(mock_handler.release(),
+                                       HttpAuth::AUTH_PROXY);
+  session_deps_.http_auth_handler_factory = auth_handler_factory.Pass();
+
+  // Add NetLog just so can verify load timing information gets a NetLog ID.
+  NetLog net_log;
+  session_deps_.net_log = &net_log;
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Since we have proxy, should try to establish tunnel.
+  MockWrite data_writes1[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a non-persistent
+  // connection.
+  MockRead data_reads1[] = {
+      // No credentials.
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Mock\r\n"),
+      MockRead("Proxy-Connection: close\r\n\r\n"),
+  };
+
+  // Since the first connection couldn't be reused, need to establish another
+  // once given credentials.
+  MockWrite data_writes2[] = {
+      // After calling trans->RestartWithAuth(), this is the request we should
+      // be issuing -- the final header line contains the credentials.
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: auth_token\r\n\r\n"),
+
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads2[] = {
+      MockRead("HTTP/1.0 200 Connection Established\r\n\r\n"),
+
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+      MockRead("Content-Length: 5\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "hello"),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_FALSE(response->headers->IsKeepAlive());
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+  EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+  EXPECT_FALSE(response->auth_challenge.get());
+
+  LoadTimingInfo load_timing_info;
+  // CONNECT requests and responses are handled at the connect job level, so
+  // the transaction does not yet have a connection.
+  EXPECT_FALSE(trans->GetLoadTimingInfo(&load_timing_info));
+
+  rv = trans->RestartWithAuth(AuthCredentials(), callback.callback());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_TRUE(response->headers->IsKeepAlive());
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(5, response->headers->GetContentLength());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge);
+
+  EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
+  TestLoadTimingNotReusedWithPac(load_timing_info,
+                                 CONNECT_TIMING_HAS_SSL_TIMES);
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
+// Test a proxy auth scheme that allows default credentials and a proxy server
+// that hangs up when credentials are initially sent.
+TEST_P(HttpNetworkTransactionTest,
+       AuthAllowsDefaultCredentialsTunnelServerClosesConnection) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+
+  // Configure against proxy server "myproxy:70".
+  session_deps_.proxy_service =
+      ProxyService::CreateFixedFromPacResult("PROXY myproxy:70");
+
+  scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory(
+      new HttpAuthHandlerMock::Factory());
+  auth_handler_factory->set_do_init_from_challenge(true);
+  scoped_ptr<HttpAuthHandlerMock> mock_handler(new HttpAuthHandlerMock());
+  mock_handler->set_allows_default_credentials(true);
+  auth_handler_factory->AddMockHandler(mock_handler.release(),
+                                       HttpAuth::AUTH_PROXY);
+  session_deps_.http_auth_handler_factory = auth_handler_factory.Pass();
+
+  // Add NetLog just so can verify load timing information gets a NetLog ID.
+  NetLog net_log;
+  session_deps_.net_log = &net_log;
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Should try to establish tunnel.
+  MockWrite data_writes1[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: auth_token\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a non-persistent
+  // connection.
+  MockRead data_reads1[] = {
+      // No credentials.
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Mock\r\n\r\n"),
+      MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED),
+  };
+
+  // Since the first connection was closed, need to establish another once given
+  // credentials.
+  MockWrite data_writes2[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: auth_token\r\n\r\n"),
+
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads2[] = {
+      MockRead("HTTP/1.0 200 Connection Established\r\n\r\n"),
+
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+      MockRead("Content-Length: 5\r\n\r\n"),
+      MockRead(SYNCHRONOUS, "hello"),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_TRUE(response->headers->IsKeepAlive());
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+  EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+  EXPECT_FALSE(response->auth_challenge);
+
+  LoadTimingInfo load_timing_info;
+  // CONNECT requests and responses are handled at the connect job level, so
+  // the transaction does not yet have a connection.
+  EXPECT_FALSE(trans->GetLoadTimingInfo(&load_timing_info));
+
+  rv = trans->RestartWithAuth(AuthCredentials(), callback.callback());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_TRUE(response->headers->IsKeepAlive());
+  EXPECT_EQ(200, response->headers->response_code());
+  EXPECT_EQ(5, response->headers->GetContentLength());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+
+  // The password prompt info should not be set.
+  EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+  EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
+  TestLoadTimingNotReusedWithPac(load_timing_info,
+                                 CONNECT_TIMING_HAS_SSL_TIMES);
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
+// Test a proxy auth scheme that allows default credentials and a proxy server
+// that hangs up when credentials are initially sent, and hangs up again when
+// they are retried.
+TEST_P(HttpNetworkTransactionTest,
+       AuthAllowsDefaultCredentialsTunnelServerClosesConnectionTwice) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+
+  // Configure against proxy server "myproxy:70".
+  session_deps_.proxy_service =
+      ProxyService::CreateFixedFromPacResult("PROXY myproxy:70");
+
+  scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory(
+      new HttpAuthHandlerMock::Factory());
+  auth_handler_factory->set_do_init_from_challenge(true);
+  scoped_ptr<HttpAuthHandlerMock> mock_handler(new HttpAuthHandlerMock());
+  mock_handler->set_allows_default_credentials(true);
+  auth_handler_factory->AddMockHandler(mock_handler.release(),
+                                       HttpAuth::AUTH_PROXY);
+  session_deps_.http_auth_handler_factory = auth_handler_factory.Pass();
+
+  // Add NetLog just so can verify load timing information gets a NetLog ID.
+  NetLog net_log;
+  session_deps_.net_log = &net_log;
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Should try to establish tunnel.
+  MockWrite data_writes1[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: auth_token\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, and then hangs up after the
+  // second request is sent.
+  MockRead data_reads1[] = {
+      // No credentials.
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Content-Length: 0\r\n"),
+      MockRead("Proxy-Connection: keep-alive\r\n"),
+      MockRead("Proxy-Authenticate: Mock\r\n\r\n"),
+      MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED),
+  };
+
+  // HttpNetworkTransaction sees a reused connection that was closed with
+  // ERR_CONNECTION_CLOSED, realized it might have been a race, so retries the
+  // request.
+  MockWrite data_writes2[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+
+  // The proxy, having had more than enough of us, just hangs up.
+  MockRead data_reads2[] = {
+      // No credentials.
+      MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_TRUE(response->headers->IsKeepAlive());
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+  EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+  EXPECT_FALSE(response->auth_challenge);
+
+  LoadTimingInfo load_timing_info;
+  EXPECT_FALSE(trans->GetLoadTimingInfo(&load_timing_info));
+
+  rv = trans->RestartWithAuth(AuthCredentials(), callback.callback());
+  EXPECT_EQ(ERR_EMPTY_RESPONSE, callback.GetResult(rv));
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
+// Test a proxy auth scheme that allows default credentials and a proxy server
+// that hangs up when credentials are initially sent, and sends a challenge
+// again they are retried.
+TEST_P(HttpNetworkTransactionTest,
+       AuthAllowsDefaultCredentialsTunnelServerChallengesTwice) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+
+  // Configure against proxy server "myproxy:70".
+  session_deps_.proxy_service =
+      ProxyService::CreateFixedFromPacResult("PROXY myproxy:70");
+
+  scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory(
+      new HttpAuthHandlerMock::Factory());
+  auth_handler_factory->set_do_init_from_challenge(true);
+  scoped_ptr<HttpAuthHandlerMock> mock_handler(new HttpAuthHandlerMock());
+  mock_handler->set_allows_default_credentials(true);
+  auth_handler_factory->AddMockHandler(mock_handler.release(),
+                                       HttpAuth::AUTH_PROXY);
+  // Add another handler for the second challenge. It supports default
+  // credentials, but they shouldn't be used, since they were already tried.
+  mock_handler.reset(new HttpAuthHandlerMock());
+  mock_handler->set_allows_default_credentials(true);
+  auth_handler_factory->AddMockHandler(mock_handler.release(),
+                                       HttpAuth::AUTH_PROXY);
+  session_deps_.http_auth_handler_factory = auth_handler_factory.Pass();
+
+  // Add NetLog just so can verify load timing information gets a NetLog ID.
+  NetLog net_log;
+  session_deps_.net_log = &net_log;
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Should try to establish tunnel.
+  MockWrite data_writes1[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+
+  // The proxy responds to the connect with a 407, using a non-persistent
+  // connection.
+  MockRead data_reads1[] = {
+      // No credentials.
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Mock\r\n"),
+      MockRead("Proxy-Connection: close\r\n\r\n"),
+  };
+
+  // Since the first connection was closed, need to establish another once given
+  // credentials.
+  MockWrite data_writes2[] = {
+      MockWrite("CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: auth_token\r\n\r\n"),
+  };
+
+  MockRead data_reads2[] = {
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Mock\r\n"),
+      MockRead("Proxy-Connection: close\r\n\r\n"),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+  EXPECT_TRUE(trans->IsReadyToRestartForAuth());
+  EXPECT_FALSE(response->auth_challenge);
+
+  LoadTimingInfo load_timing_info;
+  EXPECT_FALSE(trans->GetLoadTimingInfo(&load_timing_info));
+
+  rv = trans->RestartWithAuth(AuthCredentials(), callback.callback());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_FALSE(trans->IsReadyToRestartForAuth());
+  EXPECT_TRUE(response->auth_challenge);
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
 // Test the load timing for HTTPS requests with an HTTP proxy.
 TEST_P(HttpNetworkTransactionTest, HttpProxyLoadTimingNoPacTwoRequests) {
   HttpRequestInfo request1;
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index 92a631e7..f188333 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -32,8 +32,7 @@
     const std::string& user_agent,
     const HostPortPair& endpoint,
     const HostPortPair& proxy_server,
-    HttpAuthCache* http_auth_cache,
-    HttpAuthHandlerFactory* http_auth_handler_factory,
+    HttpAuthController* http_auth_controller,
     bool tunnel,
     bool using_spdy,
     NextProto protocol_negotiated,
@@ -44,13 +43,7 @@
       next_state_(STATE_NONE),
       transport_(transport_socket),
       endpoint_(endpoint),
-      auth_(tunnel ?
-          new HttpAuthController(HttpAuth::AUTH_PROXY,
-                                 GURL((is_https_proxy ? "https://" : "http://")
-                                      + proxy_server.ToString()),
-                                 http_auth_cache,
-                                 http_auth_handler_factory)
-          : NULL),
+      auth_(http_auth_controller),
       tunnel_(tunnel),
       using_spdy_(using_spdy),
       protocol_negotiated_(protocol_negotiated),
@@ -273,7 +266,7 @@
       !http_stream_parser_->CanFindEndOfResponse() ||
       !transport_->socket()->IsConnectedAndIdle()) {
     transport_->socket()->Disconnect();
-    return ERR_CONNECTION_CLOSED;
+    return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
   }
 
   // If the auth request had a body, need to drain it before reusing the socket.
@@ -527,7 +520,7 @@
 
 int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
   if (result < 0)
-    return result;
+    return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
 
   if (!http_stream_parser_->IsResponseBodyComplete()) {
     // Keep draining.
@@ -538,4 +531,6 @@
   return DidDrainBodyForAuthRestart();
 }
 
+//----------------------------------------------------------------
+
 }  // namespace net
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index ab973cc..f154a88e 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -25,7 +25,6 @@
 class AddressList;
 class ClientSocketHandle;
 class GrowableIOBuffer;
-class HttpAuthCache;
 class HttpStream;
 class HttpStreamParser;
 class IOBuffer;
@@ -40,8 +39,7 @@
                         const std::string& user_agent,
                         const HostPortPair& endpoint,
                         const HostPortPair& proxy_server,
-                        HttpAuthCache* http_auth_cache,
-                        HttpAuthHandlerFactory* http_auth_handler_factory,
+                        HttpAuthController* http_auth_controller,
                         bool tunnel,
                         bool using_spdy,
                         NextProto protocol_negotiated,
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 1a60c6ae..8e0580e 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -13,7 +13,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/proxy_delegate.h"
 #include "net/http/http_network_session.h"
-#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_proxy_client_socket_wrapper.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool_base.h"
@@ -86,305 +86,66 @@
     SSLClientSocketPool* ssl_pool,
     Delegate* delegate,
     NetLog* net_log)
-    : ConnectJob(group_name, timeout_duration, priority, delegate,
+    : ConnectJob(group_name,
+                 base::TimeDelta() /* The socket takes care of timeouts */,
+                 priority,
+                 delegate,
                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
-      params_(params),
-      transport_pool_(transport_pool),
-      ssl_pool_(ssl_pool),
-      using_spdy_(false),
-      protocol_negotiated_(kProtoUnknown),
-      weak_ptr_factory_(this) {
-    callback_= base::Bind(&HttpProxyConnectJob::OnIOComplete,
-                           weak_ptr_factory_.GetWeakPtr());
-}
+      client_socket_(new HttpProxyClientSocketWrapper(
+          group_name,
+          priority,
+          timeout_duration,
+          base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds),
+          transport_pool,
+          ssl_pool,
+          params->transport_params(),
+          params->ssl_params(),
+          params->user_agent(),
+          params->endpoint(),
+          params->http_auth_cache(),
+          params->http_auth_handler_factory(),
+          params->spdy_session_pool(),
+          params->tunnel(),
+          params->proxy_delegate(),
+          this->net_log())) {}
 
 HttpProxyConnectJob::~HttpProxyConnectJob() {}
 
 LoadState HttpProxyConnectJob::GetLoadState() const {
-  switch (next_state_) {
-    case STATE_TCP_CONNECT:
-    case STATE_TCP_CONNECT_COMPLETE:
-    case STATE_SSL_CONNECT:
-    case STATE_SSL_CONNECT_COMPLETE:
-      return transport_socket_handle_->GetLoadState();
-    case STATE_HTTP_PROXY_CONNECT:
-    case STATE_HTTP_PROXY_CONNECT_COMPLETE:
-    case STATE_SPDY_PROXY_CREATE_STREAM:
-    case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
-      return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
-    default:
-      NOTREACHED();
-      return LOAD_STATE_IDLE;
-  }
+  return client_socket_->GetConnectLoadState();
 }
 
 void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) {
-  if (error_response_info_.cert_request_info.get()) {
-    handle->set_ssl_error_response_info(error_response_info_);
+  if (error_response_info_) {
+    handle->set_ssl_error_response_info(*error_response_info_);
     handle->set_is_ssl_error(true);
   }
 }
 
-void HttpProxyConnectJob::OnIOComplete(int result) {
-  int rv = DoLoop(result);
-  if (rv != ERR_IO_PENDING) {
-    NotifyProxyDelegateOfCompletion(rv);
-    NotifyDelegateOfCompletion(rv);  // Deletes |this|
-  }
+int HttpProxyConnectJob::ConnectInternal() {
+  int result = client_socket_->Connect(base::Bind(
+      &HttpProxyConnectJob::OnConnectComplete, base::Unretained(this)));
+  return HandleConnectResult(result);
 }
 
-int HttpProxyConnectJob::DoLoop(int result) {
-  DCHECK_NE(next_state_, STATE_NONE);
-
-  int rv = result;
-  do {
-    State state = next_state_;
-    next_state_ = STATE_NONE;
-    switch (state) {
-      case STATE_TCP_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoTransportConnect();
-        break;
-      case STATE_TCP_CONNECT_COMPLETE:
-        rv = DoTransportConnectComplete(rv);
-        break;
-      case STATE_SSL_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoSSLConnect();
-        break;
-      case STATE_SSL_CONNECT_COMPLETE:
-        rv = DoSSLConnectComplete(rv);
-        break;
-      case STATE_HTTP_PROXY_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoHttpProxyConnect();
-        break;
-      case STATE_HTTP_PROXY_CONNECT_COMPLETE:
-        rv = DoHttpProxyConnectComplete(rv);
-        break;
-      case STATE_SPDY_PROXY_CREATE_STREAM:
-        DCHECK_EQ(OK, rv);
-        rv = DoSpdyProxyCreateStream();
-        break;
-      case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
-        rv = DoSpdyProxyCreateStreamComplete(rv);
-        break;
-      default:
-        NOTREACHED() << "bad state";
-        rv = ERR_FAILED;
-        break;
-    }
-  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
-
-  return rv;
+void HttpProxyConnectJob::OnConnectComplete(int result) {
+  DCHECK_NE(ERR_IO_PENDING, result);
+  result = HandleConnectResult(result);
+  NotifyDelegateOfCompletion(result);
+  // |this| will have been deleted at this point.
 }
 
-int HttpProxyConnectJob::DoTransportConnect() {
-  next_state_ = STATE_TCP_CONNECT_COMPLETE;
-  transport_socket_handle_.reset(new ClientSocketHandle());
-  return transport_socket_handle_->Init(group_name(),
-                                        params_->transport_params(),
-                                        priority(),
-                                        callback_,
-                                        transport_pool_,
-                                        net_log());
-}
+int HttpProxyConnectJob::HandleConnectResult(int result) {
+  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
+    error_response_info_ = client_socket_->GetAdditionalErrorState();
 
-int HttpProxyConnectJob::DoTransportConnectComplete(int result) {
-  if (result != OK)
-    return ERR_PROXY_CONNECTION_FAILED;
-
-  // Reset the timer to just the length of time allowed for HttpProxy handshake
-  // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
-  // longer to timeout than it should.
-  ResetTimer(base::TimeDelta::FromSeconds(
-      kHttpProxyConnectJobTimeoutInSeconds));
-
-  next_state_ = STATE_HTTP_PROXY_CONNECT;
-  return result;
-}
-
-int HttpProxyConnectJob::DoSSLConnect() {
-  if (params_->tunnel()) {
-    SpdySessionKey key(params_->destination().host_port_pair(),
-                       ProxyServer::Direct(),
-                       PRIVACY_MODE_DISABLED);
-    if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) {
-      using_spdy_ = true;
-      next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
-      return OK;
-    }
-  }
-  next_state_ = STATE_SSL_CONNECT_COMPLETE;
-  transport_socket_handle_.reset(new ClientSocketHandle());
-  return transport_socket_handle_->Init(
-      group_name(), params_->ssl_params(), priority(), callback_,
-      ssl_pool_, net_log());
-}
-
-int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
-  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
-    error_response_info_ = transport_socket_handle_->ssl_error_response_info();
-    DCHECK(error_response_info_.cert_request_info.get());
-    error_response_info_.cert_request_info->is_proxy = true;
-    return result;
-  }
-  if (IsCertificateError(result)) {
-    if (params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS) {
-      result = OK;
-    } else {
-      // TODO(rch): allow the user to deal with proxy cert errors in the
-      // same way as server cert errors.
-      transport_socket_handle_->socket()->Disconnect();
-      return ERR_PROXY_CERTIFICATE_INVALID;
-    }
-  }
-  // A SPDY session to the proxy completed prior to resolving the proxy
-  // hostname. Surface this error, and allow the delegate to retry.
-  // See crbug.com/334413.
-  if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
-    DCHECK(!transport_socket_handle_->socket());
-    return ERR_SPDY_SESSION_ALREADY_EXISTS;
-  }
-  if (result < 0) {
-    if (transport_socket_handle_->socket())
-      transport_socket_handle_->socket()->Disconnect();
-    return ERR_PROXY_CONNECTION_FAILED;
-  }
-
-  SSLClientSocket* ssl =
-      static_cast<SSLClientSocket*>(transport_socket_handle_->socket());
-  protocol_negotiated_ = ssl->GetNegotiatedProtocol();
-  using_spdy_ = NextProtoIsSPDY(protocol_negotiated_);
-
-  // Reset the timer to just the length of time allowed for HttpProxy handshake
-  // so that a fast SSL connection plus a slow HttpProxy failure doesn't take
-  // longer to timeout than it should.
-  ResetTimer(base::TimeDelta::FromSeconds(
-      kHttpProxyConnectJobTimeoutInSeconds));
-  // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
-  // (one that we speak SPDY over SSL to, but to which we send HTTPS
-  // request directly instead of through CONNECT tunnels, then we
-  // need to add a predicate to this if statement so we fall through
-  // to the else case. (HttpProxyClientSocket currently acts as
-  // a "trusted" SPDY proxy).
-  if (using_spdy_ && params_->tunnel()) {
-    next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
-  } else {
-    next_state_ = STATE_HTTP_PROXY_CONNECT;
-  }
-  return result;
-}
-
-int HttpProxyConnectJob::DoHttpProxyConnect() {
-  next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
-  const HostResolver::RequestInfo& tcp_destination = params_->destination();
-  const HostPortPair& proxy_server = tcp_destination.host_port_pair();
-
-  // Add a HttpProxy connection on top of the tcp socket.
-  transport_socket_.reset(
-      new HttpProxyClientSocket(transport_socket_handle_.release(),
-                                params_->user_agent(),
-                                params_->endpoint(),
-                                proxy_server,
-                                params_->http_auth_cache(),
-                                params_->http_auth_handler_factory(),
-                                params_->tunnel(),
-                                using_spdy_,
-                                protocol_negotiated_,
-                                params_->proxy_delegate(),
-                                params_->ssl_params().get() != NULL));
-  return transport_socket_->Connect(callback_);
-}
-
-int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
   if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
       result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
-    SetSocket(transport_socket_.Pass());
+    SetSocket(client_socket_.Pass());
   }
-
-  if (result == ERR_HTTP_1_1_REQUIRED)
-    return ERR_PROXY_HTTP_1_1_REQUIRED;
-
   return result;
 }
 
-int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
-  DCHECK(using_spdy_);
-  DCHECK(params_->tunnel());
-  SpdySessionKey key(params_->destination().host_port_pair(),
-                     ProxyServer::Direct(),
-                     PRIVACY_MODE_DISABLED);
-  SpdySessionPool* spdy_pool = params_->spdy_session_pool();
-  base::WeakPtr<SpdySession> spdy_session =
-      spdy_pool->FindAvailableSession(key, net_log());
-  // It's possible that a session to the proxy has recently been created
-  if (spdy_session) {
-    if (transport_socket_handle_.get()) {
-      if (transport_socket_handle_->socket())
-        transport_socket_handle_->socket()->Disconnect();
-      transport_socket_handle_->Reset();
-    }
-  } else {
-    // Create a session direct to the proxy itself
-    spdy_session =
-        spdy_pool->CreateAvailableSessionFromSocket(
-            key, transport_socket_handle_.Pass(),
-            net_log(), OK, /*using_ssl_*/ true);
-    DCHECK(spdy_session);
-  }
-
-  next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
-  return spdy_stream_request_.StartRequest(
-      SPDY_BIDIRECTIONAL_STREAM, spdy_session,
-      GURL("https://" + params_->endpoint().ToString()), priority(),
-      spdy_session->net_log(), callback_);
-}
-
-int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
-  if (result < 0)
-    return result;
-
-  next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
-  base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
-  DCHECK(stream.get());
-  // |transport_socket_| will set itself as |stream|'s delegate.
-  transport_socket_.reset(
-      new SpdyProxyClientSocket(stream,
-                                params_->user_agent(),
-                                params_->endpoint(),
-                                params_->destination().host_port_pair(),
-                                net_log(),
-                                params_->http_auth_cache(),
-                                params_->http_auth_handler_factory()));
-  return transport_socket_->Connect(callback_);
-}
-
-void HttpProxyConnectJob::NotifyProxyDelegateOfCompletion(int result) {
-  if (!params_->proxy_delegate())
-    return;
-
-  const HostPortPair& proxy_server = params_->destination().host_port_pair();
-  params_->proxy_delegate()->OnTunnelConnectCompleted(params_->endpoint(),
-                                                      proxy_server,
-                                                      result);
-}
-
-int HttpProxyConnectJob::ConnectInternal() {
-  if (params_->transport_params().get()) {
-    next_state_ = STATE_TCP_CONNECT;
-  } else {
-    next_state_ = STATE_SSL_CONNECT;
-  }
-
-  int rv = DoLoop(OK);
-  if (rv != ERR_IO_PENDING) {
-    NotifyProxyDelegateOfCompletion(rv);
-  }
-
-  return rv;
-}
-
 HttpProxyClientSocketPool::
 HttpProxyConnectJobFactory::HttpProxyConnectJobFactory(
     TransportClientSocketPool* transport_pool,
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index 1440fc7..9e550b6 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -26,6 +26,7 @@
 
 class HttpAuthCache;
 class HttpAuthHandlerFactory;
+class HttpProxyClientSocketWrapper;
 class ProxyDelegate;
 class SSLClientSocketPool;
 class SSLSocketParams;
@@ -113,39 +114,6 @@
   void GetAdditionalErrorState(ClientSocketHandle* handle) override;
 
  private:
-  enum State {
-    STATE_TCP_CONNECT,
-    STATE_TCP_CONNECT_COMPLETE,
-    STATE_SSL_CONNECT,
-    STATE_SSL_CONNECT_COMPLETE,
-    STATE_HTTP_PROXY_CONNECT,
-    STATE_HTTP_PROXY_CONNECT_COMPLETE,
-    STATE_SPDY_PROXY_CREATE_STREAM,
-    STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE,
-    STATE_SPDY_PROXY_CONNECT_COMPLETE,
-    STATE_NONE,
-  };
-
-  void OnIOComplete(int result);
-
-  // Runs the state transition loop.
-  int DoLoop(int result);
-
-  // Connecting to HTTP Proxy
-  int DoTransportConnect();
-  int DoTransportConnectComplete(int result);
-  // Connecting to HTTPS Proxy
-  int DoSSLConnect();
-  int DoSSLConnectComplete(int result);
-
-  int DoHttpProxyConnect();
-  int DoHttpProxyConnectComplete(int result);
-
-  int DoSpdyProxyCreateStream();
-  int DoSpdyProxyCreateStreamComplete(int result);
-
-  void NotifyProxyDelegateOfCompletion(int result);
-
   // Begins the tcp connection and the optional Http proxy tunnel.  If the
   // request is not immediately servicable (likely), the request will return
   // ERR_IO_PENDING. An OK return from this function or the callback means
@@ -155,23 +123,13 @@
   // a standard net error code will be returned.
   int ConnectInternal() override;
 
-  scoped_refptr<HttpProxySocketParams> params_;
-  TransportClientSocketPool* const transport_pool_;
-  SSLClientSocketPool* const ssl_pool_;
+  void OnConnectComplete(int result);
 
-  State next_state_;
-  CompletionCallback callback_;
-  scoped_ptr<ClientSocketHandle> transport_socket_handle_;
-  scoped_ptr<ProxyClientSocket> transport_socket_;
-  bool using_spdy_;
-  // Protocol negotiated with the server.
-  NextProto protocol_negotiated_;
+  int HandleConnectResult(int result);
 
-  HttpResponseInfo error_response_info_;
+  scoped_ptr<HttpProxyClientSocketWrapper> client_socket_;
 
-  SpdyStreamRequest spdy_stream_request_;
-
-  base::WeakPtrFactory<HttpProxyConnectJob> weak_ptr_factory_;
+  scoped_ptr<HttpResponseInfo> error_response_info_;
 
   DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob);
 };
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc
new file mode 100644
index 0000000..40acc5c
--- /dev/null
+++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -0,0 +1,630 @@
+// 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 "net/http/http_proxy_client_socket_wrapper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/memory/weak_ptr.h"
+#include "base/profiler/scoped_tracker.h"
+#include "base/values.h"
+#include "net/base/net_util.h"
+#include "net/base/proxy_delegate.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_response_info.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/spdy/spdy_proxy_client_socket.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_stream.h"
+#include "net/ssl/ssl_cert_request_info.h"
+#include "url/gurl.h"
+
+namespace net {
+
+HttpProxyClientSocketWrapper::HttpProxyClientSocketWrapper(
+    const std::string& group_name,
+    RequestPriority priority,
+    base::TimeDelta connect_timeout_duration,
+    base::TimeDelta proxy_negotiation_timeout_duration,
+    TransportClientSocketPool* transport_pool,
+    SSLClientSocketPool* ssl_pool,
+    const scoped_refptr<TransportSocketParams>& transport_params,
+    const scoped_refptr<SSLSocketParams>& ssl_params,
+    const std::string& user_agent,
+    const HostPortPair& endpoint,
+    HttpAuthCache* http_auth_cache,
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    SpdySessionPool* spdy_session_pool,
+    bool tunnel,
+    ProxyDelegate* proxy_delegate,
+    const BoundNetLog& net_log)
+    : next_state_(STATE_NONE),
+      group_name_(group_name),
+      priority_(priority),
+      connect_timeout_duration_(connect_timeout_duration),
+      proxy_negotiation_timeout_duration_(proxy_negotiation_timeout_duration),
+      transport_pool_(transport_pool),
+      ssl_pool_(ssl_pool),
+      transport_params_(transport_params),
+      ssl_params_(ssl_params),
+      user_agent_(user_agent),
+      endpoint_(endpoint),
+      http_auth_cache_(http_auth_cache),
+      http_auth_handler_factory_(http_auth_handler_factory),
+      spdy_session_pool_(spdy_session_pool),
+      tunnel_(tunnel),
+      proxy_delegate_(proxy_delegate),
+      using_spdy_(false),
+      http_auth_controller_(
+          tunnel ? new HttpAuthController(
+                       HttpAuth::AUTH_PROXY,
+                       GURL((ssl_params_.get() ? "https://" : "http://") +
+                            GetDestination().host_port_pair().ToString()),
+                       http_auth_cache,
+                       http_auth_handler_factory)
+                 : nullptr),
+      net_log_(BoundNetLog::Make(net_log.net_log(),
+                                 NetLog::SOURCE_PROXY_CLIENT_SOCKET_WRAPPER)) {
+  net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
+                      net_log.source().ToEventParametersCallback());
+  DCHECK(transport_params || ssl_params);
+  DCHECK(!transport_params || !ssl_params);
+}
+
+HttpProxyClientSocketWrapper::~HttpProxyClientSocketWrapper() {
+  // Make sure no sockets are returned to the lower level socket pools.
+  Disconnect();
+
+  net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
+}
+
+LoadState HttpProxyClientSocketWrapper::GetConnectLoadState() const {
+  switch (next_state_) {
+    case STATE_BEGIN_CONNECT:
+    case STATE_TCP_CONNECT:
+    case STATE_TCP_CONNECT_COMPLETE:
+    case STATE_SSL_CONNECT:
+    case STATE_SSL_CONNECT_COMPLETE:
+      return transport_socket_handle_->GetLoadState();
+    case STATE_HTTP_PROXY_CONNECT:
+    case STATE_HTTP_PROXY_CONNECT_COMPLETE:
+    case STATE_SPDY_PROXY_CREATE_STREAM:
+    case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
+    case STATE_SPDY_PROXY_CONNECT_COMPLETE:
+    case STATE_RESTART_WITH_AUTH:
+    case STATE_RESTART_WITH_AUTH_COMPLETE:
+      return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
+    case STATE_NONE:
+      // May be possible for this method to be called after an error, shouldn't
+      // be called after a successful connect.
+      break;
+  }
+  return LOAD_STATE_IDLE;
+}
+
+scoped_ptr<HttpResponseInfo>
+HttpProxyClientSocketWrapper::GetAdditionalErrorState() {
+  return error_response_info_.Pass();
+}
+
+const HttpResponseInfo* HttpProxyClientSocketWrapper::GetConnectResponseInfo()
+    const {
+  if (transport_socket_)
+    return transport_socket_->GetConnectResponseInfo();
+  return nullptr;
+}
+
+HttpStream* HttpProxyClientSocketWrapper::CreateConnectResponseStream() {
+  if (transport_socket_)
+    return transport_socket_->CreateConnectResponseStream();
+  return nullptr;
+}
+
+int HttpProxyClientSocketWrapper::RestartWithAuth(
+    const CompletionCallback& callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(connect_callback_.is_null());
+  DCHECK(transport_socket_);
+  DCHECK_EQ(STATE_NONE, next_state_);
+
+  connect_callback_ = callback;
+  next_state_ = STATE_RESTART_WITH_AUTH;
+  return DoLoop(OK);
+}
+
+const scoped_refptr<HttpAuthController>&
+HttpProxyClientSocketWrapper::GetAuthController() const {
+  return http_auth_controller_;
+}
+
+bool HttpProxyClientSocketWrapper::IsUsingSpdy() const {
+  if (transport_socket_)
+    return transport_socket_->IsUsingSpdy();
+  return false;
+}
+
+NextProto HttpProxyClientSocketWrapper::GetProtocolNegotiated() const {
+  if (transport_socket_)
+    return transport_socket_->GetProtocolNegotiated();
+  return kProtoUnknown;
+}
+
+int HttpProxyClientSocketWrapper::Connect(const CompletionCallback& callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(connect_callback_.is_null());
+
+  // If connecting or previously connected and not disconnected, return OK, to
+  // match TCPClientSocket's behavior.
+  if (next_state_ != STATE_NONE || transport_socket_)
+    return OK;
+
+  next_state_ = STATE_BEGIN_CONNECT;
+  int rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING) {
+    connect_callback_ = callback;
+  } else {
+    connect_timer_.Stop();
+    NotifyProxyDelegateOfCompletion(rv);
+  }
+
+  return rv;
+}
+
+void HttpProxyClientSocketWrapper::Disconnect() {
+  connect_callback_.Reset();
+  connect_timer_.Stop();
+  next_state_ = STATE_NONE;
+  spdy_stream_request_.CancelRequest();
+  if (transport_socket_handle_) {
+    if (transport_socket_handle_->socket())
+      transport_socket_handle_->socket()->Disconnect();
+    transport_socket_handle_->Reset();
+    transport_socket_handle_.reset();
+  }
+
+  if (transport_socket_)
+    transport_socket_->Disconnect();
+}
+
+bool HttpProxyClientSocketWrapper::IsConnected() const {
+  if (transport_socket_)
+    return transport_socket_->IsConnected();
+  // Don't return true if still connecting.  Shouldn't really matter, either
+  // way.
+  return false;
+}
+
+bool HttpProxyClientSocketWrapper::IsConnectedAndIdle() const {
+  if (transport_socket_)
+    return transport_socket_->IsConnectedAndIdle();
+  return false;
+}
+
+const BoundNetLog& HttpProxyClientSocketWrapper::NetLog() const {
+  return net_log_;
+}
+
+void HttpProxyClientSocketWrapper::SetSubresourceSpeculation() {
+  // This flag isn't passed to reconnected sockets, as only the first connection
+  // can be a preconnect.
+  if (transport_socket_)
+    transport_socket_->SetSubresourceSpeculation();
+}
+
+void HttpProxyClientSocketWrapper::SetOmniboxSpeculation() {
+  // This flag isn't passed to reconnected sockets, as only the first connection
+  // can be a preconnect.
+  if (transport_socket_)
+    transport_socket_->SetOmniboxSpeculation();
+}
+
+bool HttpProxyClientSocketWrapper::WasEverUsed() const {
+  // TODO(mmenke):  This is a little weird.  Figure out if something else should
+  // be done.
+  if (transport_socket_)
+    return transport_socket_->WasEverUsed();
+  return false;
+}
+
+bool HttpProxyClientSocketWrapper::UsingTCPFastOpen() const {
+  if (transport_socket_)
+    return transport_socket_->UsingTCPFastOpen();
+  return false;
+}
+
+bool HttpProxyClientSocketWrapper::WasNpnNegotiated() const {
+  if (transport_socket_)
+    return transport_socket_->WasNpnNegotiated();
+  return false;
+}
+
+NextProto HttpProxyClientSocketWrapper::GetNegotiatedProtocol() const {
+  if (transport_socket_)
+    return transport_socket_->GetNegotiatedProtocol();
+  return kProtoUnknown;
+}
+
+bool HttpProxyClientSocketWrapper::GetSSLInfo(SSLInfo* ssl_info) {
+  if (transport_socket_)
+    return transport_socket_->GetSSLInfo(ssl_info);
+  return false;
+}
+
+void HttpProxyClientSocketWrapper::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  // TODO(mmenke): Not clear how reconnecting for auth fits into things.
+  if (transport_socket_) {
+    transport_socket_->GetConnectionAttempts(out);
+  } else {
+    out->clear();
+  }
+}
+
+void HttpProxyClientSocketWrapper::ClearConnectionAttempts() {
+  if (transport_socket_)
+    transport_socket_->ClearConnectionAttempts();
+}
+
+void HttpProxyClientSocketWrapper::AddConnectionAttempts(
+    const ConnectionAttempts& attempts) {
+  if (transport_socket_)
+    transport_socket_->AddConnectionAttempts(attempts);
+}
+
+int HttpProxyClientSocketWrapper::Read(IOBuffer* buf,
+                                       int buf_len,
+                                       const CompletionCallback& callback) {
+  if (transport_socket_)
+    return transport_socket_->Read(buf, buf_len, callback);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::Write(IOBuffer* buf,
+                                        int buf_len,
+                                        const CompletionCallback& callback) {
+  if (transport_socket_)
+    return transport_socket_->Write(buf, buf_len, callback);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::SetReceiveBufferSize(int32 size) {
+  // TODO(mmenke):  Should this persist across reconnects?  Seems a little
+  //     weird, and not done for normal reconnects.
+  if (transport_socket_)
+    return transport_socket_->SetReceiveBufferSize(size);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::SetSendBufferSize(int32 size) {
+  if (transport_socket_)
+    return transport_socket_->SetSendBufferSize(size);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::GetPeerAddress(IPEndPoint* address) const {
+  if (transport_socket_)
+    return transport_socket_->GetPeerAddress(address);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::GetLocalAddress(IPEndPoint* address) const {
+  if (transport_socket_)
+    return transport_socket_->GetLocalAddress(address);
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+void HttpProxyClientSocketWrapper::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING) {
+    connect_timer_.Stop();
+    NotifyProxyDelegateOfCompletion(rv);
+    // May delete |this|.
+    base::ResetAndReturn(&connect_callback_).Run(rv);
+  }
+}
+
+int HttpProxyClientSocketWrapper::DoLoop(int result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_BEGIN_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoBeginConnect();
+        break;
+      case STATE_TCP_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoTransportConnect();
+        break;
+      case STATE_TCP_CONNECT_COMPLETE:
+        rv = DoTransportConnectComplete(rv);
+        break;
+      case STATE_SSL_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoSSLConnect();
+        break;
+      case STATE_SSL_CONNECT_COMPLETE:
+        rv = DoSSLConnectComplete(rv);
+        break;
+      case STATE_HTTP_PROXY_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoHttpProxyConnect();
+        break;
+      case STATE_HTTP_PROXY_CONNECT_COMPLETE:
+        rv = DoHttpProxyConnectComplete(rv);
+        break;
+      case STATE_SPDY_PROXY_CREATE_STREAM:
+        DCHECK_EQ(OK, rv);
+        rv = DoSpdyProxyCreateStream();
+        break;
+      case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
+        rv = DoSpdyProxyCreateStreamComplete(rv);
+        break;
+      case STATE_RESTART_WITH_AUTH:
+        DCHECK_EQ(OK, rv);
+        rv = DoRestartWithAuth();
+        break;
+      case STATE_RESTART_WITH_AUTH_COMPLETE:
+        rv = DoRestartWithAuthComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int HttpProxyClientSocketWrapper::DoBeginConnect() {
+  SetConnectTimer(connect_timeout_duration_);
+  if (transport_params_) {
+    next_state_ = STATE_TCP_CONNECT;
+  } else {
+    next_state_ = STATE_SSL_CONNECT;
+  }
+
+  return OK;
+}
+
+int HttpProxyClientSocketWrapper::DoTransportConnect() {
+  next_state_ = STATE_TCP_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  return transport_socket_handle_->Init(
+      group_name_, transport_params_, priority_,
+      base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete,
+                 base::Unretained(this)),
+      transport_pool_, net_log_);
+}
+
+int HttpProxyClientSocketWrapper::DoTransportConnectComplete(int result) {
+  if (result != OK)
+    return ERR_PROXY_CONNECTION_FAILED;
+
+  // Reset the timer to just the length of time allowed for HttpProxy handshake
+  // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+  // longer to timeout than it should.
+  SetConnectTimer(proxy_negotiation_timeout_duration_);
+
+  next_state_ = STATE_HTTP_PROXY_CONNECT;
+  return result;
+}
+
+int HttpProxyClientSocketWrapper::DoSSLConnect() {
+  if (tunnel_) {
+    SpdySessionKey key(GetDestination().host_port_pair(), ProxyServer::Direct(),
+                       PRIVACY_MODE_DISABLED);
+    if (spdy_session_pool_->FindAvailableSession(key, net_log_)) {
+      using_spdy_ = true;
+      next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
+      return OK;
+    }
+  }
+  next_state_ = STATE_SSL_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  return transport_socket_handle_->Init(
+      group_name_, ssl_params_, priority_,
+      base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete,
+                 base::Unretained(this)),
+      ssl_pool_, net_log_);
+}
+
+int HttpProxyClientSocketWrapper::DoSSLConnectComplete(int result) {
+  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+    DCHECK(
+        transport_socket_handle_->ssl_error_response_info().cert_request_info);
+    error_response_info_.reset(new HttpResponseInfo(
+        transport_socket_handle_->ssl_error_response_info()));
+    error_response_info_->cert_request_info->is_proxy = true;
+    return result;
+  }
+
+  if (IsCertificateError(result)) {
+    if (ssl_params_->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS) {
+      result = OK;
+    } else {
+      // TODO(rch): allow the user to deal with proxy cert errors in the
+      // same way as server cert errors.
+      transport_socket_handle_->socket()->Disconnect();
+      return ERR_PROXY_CERTIFICATE_INVALID;
+    }
+  }
+  // A SPDY session to the proxy completed prior to resolving the proxy
+  // hostname. Surface this error, and allow the delegate to retry.
+  // See crbug.com/334413.
+  if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
+    DCHECK(!transport_socket_handle_->socket());
+    return ERR_SPDY_SESSION_ALREADY_EXISTS;
+  }
+  if (result < 0) {
+    if (transport_socket_handle_->socket())
+      transport_socket_handle_->socket()->Disconnect();
+    return ERR_PROXY_CONNECTION_FAILED;
+  }
+
+  SSLClientSocket* ssl =
+      static_cast<SSLClientSocket*>(transport_socket_handle_->socket());
+  protocol_negotiated_ = ssl->GetNegotiatedProtocol();
+  using_spdy_ = NextProtoIsSPDY(protocol_negotiated_);
+
+  // Reset the timer to just the length of time allowed for HttpProxy handshake
+  // so that a fast SSL connection plus a slow HttpProxy failure doesn't take
+  // longer to timeout than it should.
+  SetConnectTimer(proxy_negotiation_timeout_duration_);
+
+  // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
+  // (one that we speak SPDY over SSL to, but to which we send HTTPS
+  // request directly instead of through CONNECT tunnels, then we
+  // need to add a predicate to this if statement so we fall through
+  // to the else case. (HttpProxyClientSocket currently acts as
+  // a "trusted" SPDY proxy).
+  if (using_spdy_ && tunnel_) {
+    next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
+  } else {
+    next_state_ = STATE_HTTP_PROXY_CONNECT;
+  }
+  return result;
+}
+
+int HttpProxyClientSocketWrapper::DoHttpProxyConnect() {
+  next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+
+  // Add a HttpProxy connection on top of the tcp socket.
+  transport_socket_.reset(new HttpProxyClientSocket(
+      transport_socket_handle_.release(), user_agent_, endpoint_,
+      GetDestination().host_port_pair(), http_auth_controller_.get(), tunnel_,
+      using_spdy_, protocol_negotiated_, proxy_delegate_,
+      ssl_params_.get() != nullptr));
+  return transport_socket_->Connect(base::Bind(
+      &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this)));
+}
+
+int HttpProxyClientSocketWrapper::DoHttpProxyConnectComplete(int result) {
+  if (result == ERR_HTTP_1_1_REQUIRED)
+    return ERR_PROXY_HTTP_1_1_REQUIRED;
+
+  return result;
+}
+
+int HttpProxyClientSocketWrapper::DoSpdyProxyCreateStream() {
+  DCHECK(using_spdy_);
+  DCHECK(tunnel_);
+  SpdySessionKey key(GetDestination().host_port_pair(), ProxyServer::Direct(),
+                     PRIVACY_MODE_DISABLED);
+  base::WeakPtr<SpdySession> spdy_session =
+      spdy_session_pool_->FindAvailableSession(key, net_log_);
+  // It's possible that a session to the proxy has recently been created
+  if (spdy_session) {
+    if (transport_socket_handle_.get()) {
+      if (transport_socket_handle_->socket())
+        transport_socket_handle_->socket()->Disconnect();
+      transport_socket_handle_->Reset();
+    }
+  } else {
+    // Create a session direct to the proxy itself
+    spdy_session = spdy_session_pool_->CreateAvailableSessionFromSocket(
+        key, transport_socket_handle_.Pass(), net_log_, OK,
+        /*using_ssl_*/ true);
+    DCHECK(spdy_session);
+  }
+
+  next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
+  return spdy_stream_request_.StartRequest(
+      SPDY_BIDIRECTIONAL_STREAM, spdy_session,
+      GURL("https://" + endpoint_.ToString()), priority_,
+      spdy_session->net_log(),
+      base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete,
+                 base::Unretained(this)));
+}
+
+int HttpProxyClientSocketWrapper::DoSpdyProxyCreateStreamComplete(int result) {
+  if (result < 0)
+    return result;
+
+  next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+  base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
+  DCHECK(stream.get());
+  // |transport_socket_| will set itself as |stream|'s delegate.
+  transport_socket_.reset(new SpdyProxyClientSocket(
+      stream, user_agent_, endpoint_, GetDestination().host_port_pair(),
+      net_log_, http_auth_controller_.get()));
+  return transport_socket_->Connect(base::Bind(
+      &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this)));
+}
+
+int HttpProxyClientSocketWrapper::DoRestartWithAuth() {
+  DCHECK(transport_socket_);
+
+  next_state_ = STATE_RESTART_WITH_AUTH_COMPLETE;
+  return transport_socket_->RestartWithAuth(base::Bind(
+      &HttpProxyClientSocketWrapper::OnIOComplete, base::Unretained(this)));
+}
+
+int HttpProxyClientSocketWrapper::DoRestartWithAuthComplete(int result) {
+  DCHECK_NE(ERR_IO_PENDING, result);
+  // If the connection could not be reused to attempt to send proxy auth
+  // credentials, try reconnecting. If auth credentials were sent, pass the
+  // error on to caller, even if the credentials may have passed a close message
+  // from the server in flight.
+  if (result == ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH) {
+    // If can't reuse the connection, attempt to create a new one.
+    transport_socket_.reset();
+    // Reconnect with HIGHEST priority to get in front of other requests that
+    // don't yet have the information |http_auth_controller_| does.
+    // TODO(mmenke): This may still result in waiting in line, if there are
+    //               other HIGHEST priority requests. Consider a workaround for
+    //               that. Starting the new request before releasing the old
+    //               socket and using LOAD_IGNORE_LIMITS would do the trick,
+    //               without exceding the the socket pool limits (Since the old
+    //               socket would free up the extra socket slot when destroyed).
+    priority_ = HIGHEST;
+    next_state_ = STATE_BEGIN_CONNECT;
+    return OK;
+  }
+
+  return result;
+}
+
+void HttpProxyClientSocketWrapper::NotifyProxyDelegateOfCompletion(int result) {
+  if (!proxy_delegate_)
+    return;
+
+  const HostPortPair& proxy_server = GetDestination().host_port_pair();
+  proxy_delegate_->OnTunnelConnectCompleted(endpoint_, proxy_server, result);
+}
+
+void HttpProxyClientSocketWrapper::SetConnectTimer(base::TimeDelta delay) {
+  connect_timer_.Stop();
+  connect_timer_.Start(FROM_HERE, delay, this,
+                       &HttpProxyClientSocketWrapper::ConnectTimeout);
+}
+
+void HttpProxyClientSocketWrapper::ConnectTimeout() {
+  // Timer shouldn't be running if next_state_ is STATE_NONE.
+  DCHECK_NE(STATE_NONE, next_state_);
+  DCHECK(!connect_callback_.is_null());
+
+  NotifyProxyDelegateOfCompletion(ERR_CONNECTION_TIMED_OUT);
+
+  CompletionCallback callback = connect_callback_;
+  Disconnect();
+  callback.Run(ERR_CONNECTION_TIMED_OUT);
+}
+
+const HostResolver::RequestInfo&
+HttpProxyClientSocketWrapper::GetDestination() {
+  if (transport_params_) {
+    return transport_params_->destination();
+  } else {
+    return ssl_params_->GetDirectConnectionParams()->destination();
+  }
+}
+
+}  // namespace net
diff --git a/net/http/http_proxy_client_socket_wrapper.h b/net/http/http_proxy_client_socket_wrapper.h
new file mode 100644
index 0000000..b3bfa29
--- /dev/null
+++ b/net/http/http_proxy_client_socket_wrapper.h
@@ -0,0 +1,211 @@
+// 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 NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_
+#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/load_timing_info.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/proxy_client_socket.h"
+#include "net/log/net_log.h"
+#include "net/socket/next_proto.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/ssl_client_socket_pool.h"
+#include "net/socket/transport_client_socket_pool.h"
+#include "net/spdy/spdy_session.h"
+
+namespace net {
+
+class ClientSocketHandle;
+class IOBuffer;
+class HttpAuthCache;
+class HttpResponseInfo;
+class HttpStream;
+class IOBuffer;
+class ProxyDelegate;
+class SpdySessionPool;
+class SSLClientSocketPool;
+class TransportClientSocketPool;
+
+// Class that establishes connections by calling into the lower layer socket
+// pools, creates a HttpProxyClientSocket / SpdyProxyClientSocket, and then
+// wraps the resulting socket.
+//
+// This class is needed to handle auth state across multiple connection.  On
+// auth challenge, this class retains auth state in its AuthController, and can
+// either send the auth response to the old connection, or establish a new
+// connection and send the response there.
+//
+// TODO(mmenke): Ideally, we'd have a central location store auth state across
+// multiple connections to the same server instead.
+class HttpProxyClientSocketWrapper : public ProxyClientSocket {
+ public:
+  HttpProxyClientSocketWrapper(
+      const std::string& group_name,
+      RequestPriority priority,
+      base::TimeDelta connect_timeout_duration,
+      base::TimeDelta proxy_negotiation_timeout_duration,
+      TransportClientSocketPool* transport_pool,
+      SSLClientSocketPool* ssl_pool,
+      const scoped_refptr<TransportSocketParams>& transport_params,
+      const scoped_refptr<SSLSocketParams>& ssl_params,
+      const std::string& user_agent,
+      const HostPortPair& endpoint,
+      HttpAuthCache* http_auth_cache,
+      HttpAuthHandlerFactory* http_auth_handler_factory,
+      SpdySessionPool* spdy_session_pool,
+      bool tunnel,
+      ProxyDelegate* proxy_delegate,
+      const BoundNetLog& net_log);
+
+  // On destruction Disconnect() is called.
+  ~HttpProxyClientSocketWrapper() override;
+
+  // Returns load state while establishing a connection.  Returns
+  // LOAD_STATE_IDLE at other times.
+  LoadState GetConnectLoadState() const;
+
+  scoped_ptr<HttpResponseInfo> GetAdditionalErrorState();
+
+  // ProxyClientSocket implementation.
+  const HttpResponseInfo* GetConnectResponseInfo() const override;
+  HttpStream* CreateConnectResponseStream() override;
+  int RestartWithAuth(const CompletionCallback& callback) override;
+  const scoped_refptr<HttpAuthController>& GetAuthController() const override;
+  bool IsUsingSpdy() const override;
+  NextProto GetProtocolNegotiated() const override;
+
+  // StreamSocket implementation.
+  int Connect(const CompletionCallback& callback) override;
+  void Disconnect() override;
+  bool IsConnected() const override;
+  bool IsConnectedAndIdle() const override;
+  const BoundNetLog& NetLog() const override;
+  void SetSubresourceSpeculation() override;
+  void SetOmniboxSpeculation() override;
+  bool WasEverUsed() const override;
+  bool UsingTCPFastOpen() const override;
+  bool WasNpnNegotiated() const override;
+  NextProto GetNegotiatedProtocol() const override;
+  bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override;
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override;
+
+  // Socket implementation.
+  int Read(IOBuffer* buf,
+           int buf_len,
+           const CompletionCallback& callback) override;
+  int Write(IOBuffer* buf,
+            int buf_len,
+            const CompletionCallback& callback) override;
+  int SetReceiveBufferSize(int32 size) override;
+  int SetSendBufferSize(int32 size) override;
+  int GetPeerAddress(IPEndPoint* address) const override;
+  int GetLocalAddress(IPEndPoint* address) const override;
+
+ private:
+  enum State {
+    STATE_BEGIN_CONNECT,
+    STATE_TCP_CONNECT,
+    STATE_TCP_CONNECT_COMPLETE,
+    STATE_SSL_CONNECT,
+    STATE_SSL_CONNECT_COMPLETE,
+    STATE_HTTP_PROXY_CONNECT,
+    STATE_HTTP_PROXY_CONNECT_COMPLETE,
+    STATE_SPDY_PROXY_CREATE_STREAM,
+    STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE,
+    STATE_SPDY_PROXY_CONNECT_COMPLETE,
+    STATE_RESTART_WITH_AUTH,
+    STATE_RESTART_WITH_AUTH_COMPLETE,
+    STATE_NONE,
+  };
+
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  // Determine if need to go through TCP or SSL path.
+  int DoBeginConnect();
+  // Connecting to HTTP Proxy
+  int DoTransportConnect();
+  int DoTransportConnectComplete(int result);
+  // Connecting to HTTPS Proxy
+  int DoSSLConnect();
+  int DoSSLConnectComplete(int result);
+
+  int DoHttpProxyConnect();
+  int DoHttpProxyConnectComplete(int result);
+
+  int DoSpdyProxyCreateStream();
+  int DoSpdyProxyCreateStreamComplete(int result);
+
+  int DoRestartWithAuth();
+  int DoRestartWithAuthComplete(int result);
+
+  void NotifyProxyDelegateOfCompletion(int result);
+
+  void SetConnectTimer(base::TimeDelta duration);
+  void ConnectTimeout();
+
+  const HostResolver::RequestInfo& GetDestination();
+
+  State next_state_;
+
+  const std::string group_name_;
+  RequestPriority priority_;
+  const base::TimeDelta connect_timeout_duration_;
+  const base::TimeDelta proxy_negotiation_timeout_duration_;
+
+  TransportClientSocketPool* const transport_pool_;
+  SSLClientSocketPool* const ssl_pool_;
+  const scoped_refptr<TransportSocketParams> transport_params_;
+  const scoped_refptr<SSLSocketParams> ssl_params_;
+
+  const std::string user_agent_;
+  const HostPortPair endpoint_;
+  HttpAuthCache* const http_auth_cache_;
+  HttpAuthHandlerFactory* const http_auth_handler_factory_;
+  SpdySessionPool* const spdy_session_pool_;
+
+  const bool tunnel_;
+  ProxyDelegate* const proxy_delegate_;
+
+  bool using_spdy_;
+  NextProto protocol_negotiated_;
+
+  scoped_ptr<HttpResponseInfo> error_response_info_;
+
+  scoped_ptr<ClientSocketHandle> transport_socket_handle_;
+  scoped_ptr<ProxyClientSocket> transport_socket_;
+
+  // Called when a connection is established. Also used when restarting with
+  // AUTH, which will invoke this when ready to restart, after reconnecting
+  // if necessary.
+  CompletionCallback connect_callback_;
+
+  SpdyStreamRequest spdy_stream_request_;
+
+  scoped_refptr<HttpAuthController> http_auth_controller_;
+
+  BoundNetLog net_log_;
+
+  base::OneShotTimer connect_timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocketWrapper);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_WRAPPER_H_
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index b27b44e..3625e4a 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -1293,15 +1293,6 @@
     return OK;
   }
 
-  if (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET ||
-      result == ERR_SOCKET_NOT_CONNECTED) {
-    // The server may have closed the connection while waiting for auth data.
-    // Automatically try to use a new connection.
-    establishing_tunnel_ = false;
-    ReturnToStateInitConnection(true /* close connection */);
-    return OK;
-  }
-
   return ReconsiderProxyAfterError(result);
 }
 
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h
index 79af389..63feed8 100644
--- a/net/log/net_log_source_type_list.h
+++ b/net/log/net_log_source_type_list.h
@@ -25,6 +25,7 @@
 SOURCE_TYPE(FILESTREAM)
 SOURCE_TYPE(DNS_PROBER)
 SOURCE_TYPE(PROXY_CLIENT_SOCKET)
+SOURCE_TYPE(PROXY_CLIENT_SOCKET_WRAPPER)
 SOURCE_TYPE(DATA_REDUCTION_PROXY)
 SOURCE_TYPE(IOS_WEB_VIEW_CERT_VERIFIER)
 SOURCE_TYPE(SAFE_BROWSING)
diff --git a/net/net.gypi b/net/net.gypi
index 1c2ec2d..a6e27b3 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -409,8 +409,6 @@
       'base/crypto_module_openssl.cc',
       'base/data_url.cc',
       'base/data_url.h',
-      'base/dns_reloader.cc',
-      'base/dns_reloader.h',
       'base/elements_upload_data_stream.cc',
       'base/elements_upload_data_stream.h',
       'base/expiring_cache.h',
@@ -742,6 +740,8 @@
       'dns/dns_protocol.h',
       'dns/dns_query.cc',
       'dns/dns_query.h',
+      'dns/dns_reloader.cc',
+      'dns/dns_reloader.h',
       'dns/dns_response.cc',
       'dns/dns_response.h',
       'dns/dns_session.cc',
@@ -845,6 +845,8 @@
       'http/http_proxy_client_socket.h',
       'http/http_proxy_client_socket_pool.cc',
       'http/http_proxy_client_socket_pool.h',
+      'http/http_proxy_client_socket_wrapper.cc',
+      'http/http_proxy_client_socket_wrapper.h',
       'http/http_request_info.cc',
       'http/http_request_info.h',
       'http/http_response_body_drainer.cc',
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index e6d521e..6c28026d 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -34,15 +34,11 @@
     const HostPortPair& endpoint,
     const HostPortPair& proxy_server,
     const BoundNetLog& source_net_log,
-    HttpAuthCache* auth_cache,
-    HttpAuthHandlerFactory* auth_handler_factory)
+    HttpAuthController* auth_controller)
     : next_state_(STATE_DISCONNECTED),
       spdy_stream_(spdy_stream),
       endpoint_(endpoint),
-      auth_(new HttpAuthController(HttpAuth::AUTH_PROXY,
-                                   GURL("https://" + proxy_server.ToString()),
-                                   auth_cache,
-                                   auth_handler_factory)),
+      auth_(auth_controller),
       user_agent_(user_agent),
       user_buffer_len_(0),
       write_buffer_len_(0),
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index af38767..c3dd2a3 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -45,9 +45,7 @@
                         const HostPortPair& endpoint,
                         const HostPortPair& proxy_server,
                         const BoundNetLog& source_net_log,
-                        HttpAuthCache* auth_cache,
-                        HttpAuthHandlerFactory* auth_handler_factory);
-
+                        HttpAuthController* auth_controller);
 
   // On destruction Disconnect() is called.
   ~SpdyProxyClientSocket() override;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index b8f62cf..de9cb0d 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -201,8 +201,10 @@
   // Create the SpdyProxyClientSocket.
   sock_.reset(new SpdyProxyClientSocket(
       spdy_stream, user_agent_, endpoint_host_port_pair_, proxy_host_port_,
-      net_log_.bound(), session_->http_auth_cache(),
-      session_->http_auth_handler_factory()));
+      net_log_.bound(),
+      new HttpAuthController(
+          HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()),
+          session_->http_auth_cache(), session_->http_auth_handler_factory())));
 }
 
 scoped_refptr<IOBufferWithSize> SpdyProxyClientSocketTest::CreateBuffer(
diff --git a/net/tools/epoll_server/epoll_server.cc b/net/tools/epoll_server/epoll_server.cc
index 7b60ce2..aa1c2da 100644
--- a/net/tools/epoll_server/epoll_server.cc
+++ b/net/tools/epoll_server/epoll_server.cc
@@ -221,12 +221,8 @@
   cb->OnRegistration(this, fd, event_mask);
 }
 
-int EpollServer::GetFlags(int fd) {
-  return fcntl(fd, F_GETFL, 0);
-}
-
 void EpollServer::SetNonblocking(int fd) {
-  int flags = GetFlags(fd);
+  int flags = fcntl(fd, F_GETFL, 0);
   if (flags == -1) {
     int saved_errno = errno;
     char buf[kErrorBufferSize];
@@ -236,7 +232,7 @@
   }
   if (!(flags & O_NONBLOCK)) {
     int saved_flags = flags;
-    flags = SetFlags(fd, flags | O_NONBLOCK);
+    flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
     if (flags == -1) {
       // bad.
       int saved_errno = errno;
diff --git a/net/tools/epoll_server/epoll_server.h b/net/tools/epoll_server/epoll_server.h
index 139ae27..b241861 100644
--- a/net/tools/epoll_server/epoll_server.h
+++ b/net/tools/epoll_server/epoll_server.h
@@ -487,11 +487,6 @@
   void CallReadyListCallbacks();
 
  protected:
-  virtual int GetFlags(int fd);
-  inline int SetFlags(int fd, int flags) {
-    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-  }
-
   virtual void SetNonblocking(int fd);
 
   // This exists here so that we can override this function in unittests
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index f23a3bbd..641c07b 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -401,7 +401,9 @@
 
   // This should not be called on error, and the job type should have cleared
   // IO_PENDING state before calling this method.
-  DCHECK(request_->status().is_success());
+  // TODO(mmenke): Change this to a DCHECK once https://crbug.com/508900 is
+  // resolved.
+  CHECK(request_->status().is_success());
 
   // Initialize to the current time, and let the subclass optionally override
   // the time stamps if it has that information.  The default request_time is
@@ -792,9 +794,11 @@
     // An error status should never be replaced by a non-error status by a
     // URLRequestJob.  URLRequest has some retry paths, but it resets the status
     // itself, if needed.
-    DCHECK(request_->status().is_io_pending() ||
-           request_->status().is_success() ||
-           (!status.is_success() && !status.is_io_pending()));
+    // TODO(mmenke): Change this to a DCHECK once https://crbug.com/508900 is
+    // resolved.
+    CHECK(request_->status().is_io_pending() ||
+          request_->status().is_success() ||
+          (!status.is_success() && !status.is_io_pending()));
     request_->set_status(status);
   }
 }
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.cc b/ppapi/proxy/ppapi_command_buffer_proxy.cc
index c430c78..56e7486 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.cc
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.cc
@@ -22,7 +22,10 @@
     : command_buffer_id_(command_buffer_id),
       capabilities_(capabilities),
       resource_(resource),
-      dispatcher_(dispatcher) {
+      dispatcher_(dispatcher),
+      next_fence_sync_release_(1),
+      pending_fence_sync_release_(0),
+      flushed_fence_sync_release_(0) {
   shared_state_shm_.reset(
       new base::SharedMemory(shared_state.shmem(), false));
   shared_state_shm_->Map(shared_state.size());
@@ -62,12 +65,14 @@
   if (last_state_.error != gpu::error::kNoError)
     return;
 
-  if (flush_info_->flush_pending && flush_info_->resource != resource_)
+  if (flush_info_->flush_pending && flush_info_->resource != resource_) {
     FlushInternal();
+  }
 
   flush_info_->flush_pending = true;
   flush_info_->resource = resource_;
   flush_info_->put_offset = put_offset;
+  pending_fence_sync_release_ = next_fence_sync_release_ - 1;
 }
 
 void PpapiCommandBufferProxy::WaitForTokenInRange(int32 start, int32 end) {
@@ -187,6 +192,18 @@
   return command_buffer_id_;
 }
 
+uint64_t PpapiCommandBufferProxy::GenerateFenceSyncRelease() {
+  return next_fence_sync_release_++;
+}
+
+bool PpapiCommandBufferProxy::IsFenceSyncRelease(uint64_t release) {
+  return release != 0 && release < next_fence_sync_release_;
+}
+
+bool PpapiCommandBufferProxy::IsFenceSyncFlushed(uint64_t release) {
+  return release <= flushed_fence_sync_release_;
+}
+
 uint32 PpapiCommandBufferProxy::InsertSyncPoint() {
   uint32 sync_point = 0;
   if (last_state_.error == gpu::error::kNoError) {
@@ -294,6 +311,7 @@
   DCHECK(last_state_.error == gpu::error::kNoError);
 
   DCHECK(flush_info_->flush_pending);
+  DCHECK_GE(pending_fence_sync_release_, flushed_fence_sync_release_);
 
   IPC::Message* message = new PpapiHostMsg_PPBGraphics3D_AsyncFlush(
       ppapi::API_ID_PPB_GRAPHICS_3D, flush_info_->resource,
@@ -307,6 +325,7 @@
 
   flush_info_->flush_pending = false;
   flush_info_->resource.SetHostResource(0, 0);
+  flushed_fence_sync_release_ = pending_fence_sync_release_;
 }
 
 }  // namespace proxy
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.h b/ppapi/proxy/ppapi_command_buffer_proxy.h
index ddb3838..dc480579 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.h
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.h
@@ -70,6 +70,9 @@
   bool IsGpuChannelLost() override;
   gpu::CommandBufferNamespace GetNamespaceID() const override;
   uint64_t GetCommandBufferID() const override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncRelease(uint64_t release) override;
+  bool IsFenceSyncFlushed(uint64_t release) override;
 
  private:
   bool Send(IPC::Message* msg);
@@ -96,6 +99,10 @@
 
   InstanceData::FlushInfo *flush_info_;
 
+  uint64_t next_fence_sync_release_;
+  uint64_t pending_fence_sync_release_;
+  uint64_t flushed_fence_sync_release_;
+
   DISALLOW_COPY_AND_ASSIGN(PpapiCommandBufferProxy);
 };
 
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index a080136e..b18e540e 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -97,6 +97,7 @@
       'webapp/base/js/identity_unittest.js',
       'webapp/base/js/ipc_unittest.js',
       'webapp/base/js/l10n_unittest.js',
+      'webapp/base/js/network_connectivity_detector_unittest.js',
       'webapp/base/js/platform_unittest.js',
       'webapp/base/js/protocol_extension_manager_unittest.js',
       'webapp/base/js/session_logger_unittest.js',
@@ -182,7 +183,6 @@
       'webapp/base/js/credentials_provider.js',
       'webapp/base/js/experiments.js',
       'webapp/base/js/host_desktop.js',
-      'webapp/base/js/smart_reconnector.js',
       'webapp/base/js/telemetry_event_writer.js',
       'webapp/base/js/xmpp_error_cache.js',
     ],
@@ -198,6 +198,7 @@
       'webapp/base/js/protocol_extension_manager.js',
       'webapp/base/js/protocol_extension.js',
       'webapp/base/js/error.js',
+      'webapp/base/js/network_connectivity_detector.js',
       'webapp/base/js/plugin_settings.js',
       'webapp/base/js/suspend_detector.js',
       'webapp/base/js/typecheck.js',
diff --git a/remoting/webapp/base/js/error.js b/remoting/webapp/base/js/error.js
index 28d79c6..fa1684d 100644
--- a/remoting/webapp/base/js/error.js
+++ b/remoting/webapp/base/js/error.js
@@ -100,11 +100,11 @@
   // not normally cause the error text to be shown to the user, so the
   // i18n-content prefix is not needed in this case.
   CANCELLED: '__CANCELLED__',
+
   // Used to signify that the local computer was suspended for long enough that
   // the connection is expected to drop, allowing a reconnect attempt to be
-  // scheduled sooner. This is not shown to the user so i18n-content prefix is
-  // not needed in this case.
-  CLIENT_SUSPENDED: '__CLIENT_SUSPENDED__',
+  // scheduled sooner.
+  CLIENT_SUSPENDED: /*i18n-content*/ 'ERROR_NETWORK_FAILURE',
 
   INVALID_ACCESS_CODE: /*i18n-content*/ 'ERROR_INVALID_ACCESS_CODE',
   MISSING_PLUGIN: /*i18n-content*/ 'ERROR_MISSING_PLUGIN',
diff --git a/remoting/webapp/base/js/network_connectivity_detector.js b/remoting/webapp/base/js/network_connectivity_detector.js
new file mode 100644
index 0000000..cc8f66e
--- /dev/null
+++ b/remoting/webapp/base/js/network_connectivity_detector.js
@@ -0,0 +1,125 @@
+// 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.
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function () {
+
+'use strict';
+
+/**
+ * remoting.NetworkConnectivityDetector provides a reliable way to detect
+ * whether the current computer has connectivity.
+ *
+ * The waitForOnline() method returns a promise that resolves when the machine
+ * has network connectivity or rejects with remoting.Error.Tag.CANCELLED when
+ * cancel() is called.
+ *
+ * @constructor
+ * @implements {base.Disposable}
+ */
+remoting.NetworkConnectivityDetector = function() {
+  /** @private {base.Deferred} */
+  this.deferred_ = null;
+
+  /** @private {base.Disposable}*/
+  this.pendingXhr_ = null;
+};
+
+remoting.NetworkConnectivityDetector.prototype.dispose = function() {
+  this.cancel();
+};
+
+remoting.NetworkConnectivityDetector.prototype.cancel = function() {
+  base.dispose(this.pendingXhr_);
+  this.pendingXhr_ = null;
+
+  if (this.deferred_) {
+    this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
+  }
+  this.deferred_ = null;
+};
+
+/**
+ * @return {Promise} A promise that resolves when the device is online or
+ *     rejects with the error tag remoting.Error.Tag.Cancelled when cancel() is
+ *     called.
+ */
+remoting.NetworkConnectivityDetector.prototype.waitForOnline = function() {
+  if (this.deferred_) {
+    return this.deferred_.promise();
+  }
+
+  this.deferred_ = new base.Deferred();
+
+  var that = this;
+
+  this.waitForOnlineEvent_().then(
+    this.waitForConnectivity_.bind(this)
+  ).then(function() {
+    that.deferred_.resolve();
+    that.cancel();
+  }).catch(function(){
+    that.cancel();
+  });
+
+  return this.deferred_.promise();
+};
+
+/**
+ * @private
+ * @return {Promise}
+ */
+remoting.NetworkConnectivityDetector.prototype.waitForOnlineEvent_ = function(){
+  if (base.isOnline()) {
+    return Promise.resolve();
+  }
+
+  var pending = new base.Deferred();
+  function onOnline() {
+    pending.resolve();
+    window.removeEventListener('online', onOnline, false);
+  }
+
+  window.addEventListener('online', onOnline, false);
+  return pending.promise();
+};
+
+/**
+ * @private
+ * @return {Promise}
+ */
+remoting.NetworkConnectivityDetector.prototype.waitForConnectivity_=
+    function() {
+  console.assert(this.pendingXhr_ == null, 'Unexpected pending request');
+
+  if (!this.deferred_) {
+    // It is canceled.
+    return Promise.reject();
+  }
+
+  this.pendingXhr_ = new remoting.AutoRetryXhr({
+    method: 'GET',
+    url: remoting.NetworkConnectivityDetector.getUrlForTesting(),
+  });
+  return this.pendingXhr_.start();
+};
+
+/**
+ * Factory method for stubbing in unittests
+ *
+ * @return {remoting.NetworkConnectivityDetector}
+ */
+remoting.NetworkConnectivityDetector.create = function() {
+  return new remoting.NetworkConnectivityDetector();
+};
+
+/** @return {string} */
+remoting.NetworkConnectivityDetector.getUrlForTesting = function() {
+  return remoting.settings.TALK_GADGET_URL +
+         '/oauth/chrome-remote-desktop-client';
+};
+
+})();
diff --git a/remoting/webapp/base/js/network_connectivity_detector_unittest.js b/remoting/webapp/base/js/network_connectivity_detector_unittest.js
new file mode 100644
index 0000000..0a01af6d
--- /dev/null
+++ b/remoting/webapp/base/js/network_connectivity_detector_unittest.js
@@ -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.
+
+(function() {
+
+'use strict';
+
+/** @type {remoting.NetworkConnectivityDetector} */
+var detector;
+
+/** @type {sinon.TestStub} */
+var onlineStub;
+
+function setXhrStatus(/** number */ status) {
+  remoting.MockXhr.setEmptyResponseFor(
+      'GET', remoting.NetworkConnectivityDetector.getUrlForTesting(), status);
+
+}
+
+QUnit.module('NetworkConnectivityDetector', {
+  beforeEach: function() {
+    remoting.settings = new remoting.Settings();
+    remoting.MockXhr.activate();
+    onlineStub = sinon.stub(base, 'isOnline');
+    detector = remoting.NetworkConnectivityDetector.create();
+  },
+  afterEach: function() {
+    onlineStub.restore();
+    base.dispose(detector);
+    detector = null;
+    remoting.MockXhr.restore();
+    remoting.settings = null;
+  }
+});
+
+QUnit.test('waitForOnline() window.onLine = true', function(assert){
+  onlineStub.returns(true);
+  setXhrStatus(200);
+  return detector.waitForOnline().then(function() {
+    assert.ok(true);
+  });
+});
+
+QUnit.test('waitForOnline() window.onLine = false', function(assert){
+  onlineStub.returns(false);
+  setXhrStatus(200);
+  var promise = detector.waitForOnline().then(function() {
+    assert.ok(true);
+  });
+
+  Promise.resolve().then(function() {
+    onlineStub.returns(true);
+    window.dispatchEvent(new CustomEvent('online'));
+  });
+  return promise;
+});
+
+QUnit.test('waitForOnline() use one single XHR for multiple clients',
+    function(assert){
+  onlineStub.returns(true);
+
+  // We only set one single Xhr response. The next Xhr will fail.
+  setXhrStatus(200);
+
+  var promise1 = detector.waitForOnline();
+  var promise2 = detector.waitForOnline();
+  var promise3 = detector.waitForOnline();
+  return Promise.all([promise1, promise2, promise3]).then(function(){
+    assert.ok(true);
+  });
+});
+
+QUnit.test('cancel() rejects the promise', function(assert){
+  onlineStub.returns(false);
+  setXhrStatus(200);
+  var promise = detector.waitForOnline().then(function() {
+    assert.ok(true);
+  }).then(function(){
+    assert.ok(false, 'Expects the promise to reject with Canceled');
+  }).catch(function(/** * */ reason) {
+    var error = /** @type {remoting.Error} */ (reason);
+    assert.ok(error.hasTag(remoting.Error.Tag.CANCELLED));
+  });
+
+  detector.cancel();
+  Promise.resolve().then(function() {
+    onlineStub.returns(true);
+    window.dispatchEvent(new CustomEvent('online'));
+  });
+
+  return promise;
+});
+
+})();
diff --git a/remoting/webapp/base/js/smart_reconnector.js b/remoting/webapp/base/js/smart_reconnector.js
deleted file mode 100644
index afb6975..0000000
--- a/remoting/webapp/base/js/smart_reconnector.js
+++ /dev/null
@@ -1,126 +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.
-
-/**
- * @fileoverview
- * Class handling reconnecting the session when it is disconnected due to
- * network failure.
- *
- * The SmartReconnector listens for changes in connection state of
- * |clientSession| to determine if a reconnection is needed.  It then calls into
- * |connector| to reconnect the session.
- */
-
-/** @suppress {duplicate} */
-var remoting = remoting || {};
-
-(function () {
-
-'use strict';
-
-/**
- * @constructor
- * @param {remoting.ConnectingDialog} connectingDialog
- * @param {function()} reconnectCallback
- * @param {function()} disconnectCallback
- * @param {remoting.ClientSession} clientSession This represents the current
- *    remote desktop connection.  It is used to monitor the changes in
- *    connection state.
- * @implements {base.Disposable}
- */
-remoting.SmartReconnector = function(connectingDialog, reconnectCallback,
-                                     disconnectCallback, clientSession) {
-  /** @private */
-  this.reconnectCallback_ = reconnectCallback;
-
-  /** @private */
-  this.disconnectCallback_ = disconnectCallback;
-
-  /** @private */
-  this.clientSession_ = clientSession;
-
-  /**
-   * Placeholder of any pending reconnect operations, e.g. Waiting for online,
-   * or a timeout to reconnect.
-   *
-   * @private {base.Disposable}
-   */
-  this.pending_ = null;
-
-  /** @private */
-  this.connectingDialog_ = connectingDialog;
-
-  var Events = remoting.ClientSession.Events;
-  /** @private */
-  this.eventHook_ =
-      new base.EventHook(clientSession, Events.videoChannelStateChanged,
-                         this.videoChannelStateChanged_.bind(this));
-};
-
-// The online event only means the network adapter is enabled, but
-// it doesn't necessarily mean that we have a working internet connection.
-// Therefore, delay the connection by |RECONNECT_DELAY_MS| to allow for the
-// network to connect.
-var RECONNECT_DELAY_MS = 2000;
-
-// If the video channel is inactive for 10 seconds reconnect the session.
-var CONNECTION_TIMEOUT_MS = 10000;
-
-remoting.SmartReconnector.prototype.reconnect_ = function() {
-  this.cancelPending_();
-  this.disconnectCallback_();
-  this.reconnectCallback_();
-};
-
-remoting.SmartReconnector.prototype.reconnectAsync_ = function() {
-  this.cancelPending_();
-  this.connectingDialog_.show();
-  this.pending_ =
-      new base.OneShotTimer(this.reconnect_.bind(this), RECONNECT_DELAY_MS);
-};
-
-/**
- * @param {!remoting.Error} reason
- */
-remoting.SmartReconnector.prototype.onConnectionDropped = function(reason) {
-  this.cancelPending_();
-  if (navigator.onLine) {
-    this.reconnect_();
-  } else {
-    this.pending_ = new base.DomEventHook(
-        window, 'online', this.reconnectAsync_.bind(this), false);
-  }
-};
-
-/**
- * @param {boolean=} active  True if the video channel is active.
- */
-remoting.SmartReconnector.prototype.videoChannelStateChanged_ =
-    function (active) {
-  this.cancelPending_();
-  if (!active) {
-    this.pending_ = new base.DomEventHook(
-        window, 'online', this.startReconnectTimeout_.bind(this), false);
-  }
-};
-
-remoting.SmartReconnector.prototype.startReconnectTimeout_ = function() {
-  this.cancelPending_();
-  this.pending_ =
-      new base.OneShotTimer(this.reconnect_.bind(this), CONNECTION_TIMEOUT_MS);
-};
-
-/** @private */
-remoting.SmartReconnector.prototype.cancelPending_ = function() {
-  base.dispose(this.pending_);
-  this.pending_ = null;
-};
-
-remoting.SmartReconnector.prototype.dispose = function() {
-  this.cancelPending_();
-  base.dispose(this.eventHook_);
-  this.eventHook_ = null;
-};
-
-})();
diff --git a/remoting/webapp/base/js/xhr.js b/remoting/webapp/base/js/xhr.js
index 4815ec1..dc50fd1 100644
--- a/remoting/webapp/base/js/xhr.js
+++ b/remoting/webapp/base/js/xhr.js
@@ -355,6 +355,8 @@
  *
  * @param {remoting.Xhr.Params} params
  * @param {number=} opt_maxRetryAttempts
+ * @implements {base.Disposable}
+  *
  * @constructor
  */
 remoting.AutoRetryXhr = function(params, opt_maxRetryAttempts) {
@@ -370,6 +372,11 @@
   this.deferred_ = new base.Deferred();
 };
 
+remoting.AutoRetryXhr.prototype.dispose = function() {
+  this.retryAttemptsRemaining_ = 0;
+  this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
+};
+
 /**
  * Calling this method multiple times will return the same promise and will not
  * start a new request.
diff --git a/remoting/webapp/crd/js/me2me_activity.js b/remoting/webapp/crd/js/me2me_activity.js
index 9f742e0..57b1fd7 100644
--- a/remoting/webapp/crd/js/me2me_activity.js
+++ b/remoting/webapp/crd/js/me2me_activity.js
@@ -31,8 +31,8 @@
   /** @private */
   this.retryOnHostOffline_ = true;
 
-  /** @private {remoting.SmartReconnector} */
-  this.reconnector_ = null;
+  /** @private {remoting.NetworkConnectivityDetector} */
+  this.networkDetector_ = null;
 
   /** @private {remoting.SessionLogger} */
   this.logger_ = null;
@@ -44,6 +44,8 @@
 remoting.Me2MeActivity.prototype.dispose = function() {
   base.dispose(this.desktopActivity_);
   this.desktopActivity_ = null;
+  base.dispose(this.networkDetector_);
+  this.networkDetector_ = null;
 };
 
 remoting.Me2MeActivity.prototype.start = function() {
@@ -227,26 +229,27 @@
   plugin.extensions().register(new remoting.GnubbyAuthHandler());
   this.pinDialog_.requestPairingIfNecessary(connectionInfo.plugin());
 
-  var SessionEntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
-
-  base.dispose(this.reconnector_);
-  this.reconnector_ = new remoting.SmartReconnector(
-      this.desktopActivity_.getConnectingDialog(),
-      this.reconnect_.bind(
-          this, SessionEntryPoint.AUTO_RECONNECT_ON_CONNECTION_DROPPED),
-      this.stop.bind(this), connectionInfo.session());
+  // Drop the session after 30s of suspension.  If this timeout is too short, we
+  // risk dropping a connection that is self-recoverable. If this timeout is too
+  // long, the user may lose his/her patience and just manually reconnects.
+  this.desktopActivity_.getSession().dropSessionOnSuspend(30 * 1000);
 };
 
 remoting.Me2MeActivity.prototype.onDisconnected = function(error) {
+  base.dispose(this.desktopActivity_);
+  this.desktopActivity_ = null;
+
   if (error.isNone()) {
     this.showFinishDialog_(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
   } else {
-    this.reconnector_.onConnectionDropped(error);
     this.showErrorMessage_(error);
+    var SessionEntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+    base.dispose(this.networkDetector_);
+    this.networkDetector_ = remoting.NetworkConnectivityDetector.create();
+    this.networkDetector_.waitForOnline().then(
+      this.reconnect_.bind(
+          this, SessionEntryPoint.AUTO_RECONNECT_ON_CONNECTION_DROPPED));
   }
-
-  base.dispose(this.desktopActivity_);
-  this.desktopActivity_ = null;
 };
 
 /**
@@ -274,6 +277,9 @@
   var that = this;
 
   dialog.show().then(function(/** Result */result) {
+    base.dispose(that.networkDetector_);
+    that.networkDetector_ = null;
+
     if (result === Result.PRIMARY) {
       remoting.setMode(remoting.AppMode.HOME);
     } else {
diff --git a/remoting/webapp/crd/js/me2me_telemetry_integration_test.js b/remoting/webapp/crd/js/me2me_telemetry_integration_test.js
index 8db3a32..b7d36cf 100644
--- a/remoting/webapp/crd/js/me2me_telemetry_integration_test.js
+++ b/remoting/webapp/crd/js/me2me_telemetry_integration_test.js
@@ -30,35 +30,21 @@
 /**
  * @param {remoting.Me2MeTestDriver} testDriver
  * @param {Object} baseEvent
+ * @param {Array<remoting.ChromotingEvent.SessionState>} sequence
  */
-var expectSucceeded = function(testDriver, baseEvent) {
-  var ChromotingEvent = remoting.ChromotingEvent;
-  var sequence = [{
-    session_state: ChromotingEvent.SessionState.STARTED,
-  },{
-    session_state: ChromotingEvent.SessionState.SIGNALING,
-    previous_session_state: ChromotingEvent.SessionState.STARTED
-  },{
-    session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
-    previous_session_state: ChromotingEvent.SessionState.SIGNALING
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTING,
-    previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
-  },{
-    session_state: ChromotingEvent.SessionState.AUTHENTICATED,
-    previous_session_state: ChromotingEvent.SessionState.CONNECTING
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTED,
-    previous_session_state: ChromotingEvent.SessionState.AUTHENTICATED
-  },{
-    session_state: ChromotingEvent.SessionState.CLOSED,
-    previous_session_state: ChromotingEvent.SessionState.CONNECTED
-  }];
-
-  var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
-    var event = /** @type {Object} */ (base.deepCopy(baseEvent));
-    base.mix(event, sequenceValue);
-    return event;
+var expectSequence = function(testDriver, baseEvent, sequence) {
+  var expectedEvents = sequence.map(
+    /**
+     * @param {remoting.ChromotingEvent.SessionState} state
+     * @param {number} i
+     */
+    function(state, i) {
+      var event = /** @type {Object} */ (base.deepCopy(baseEvent));
+      event.session_state = state;
+      if (i > 0) {
+        event.previous_session_state = sequence[i -1];
+      }
+      return event;
   });
   testDriver.expectEvents(expectedEvents);
 };
@@ -67,30 +53,32 @@
  * @param {remoting.Me2MeTestDriver} testDriver
  * @param {Object} baseEvent
  */
+var expectSucceeded = function(testDriver, baseEvent) {
+  var ChromotingEvent = remoting.ChromotingEvent;
+  expectSequence(testDriver, baseEvent, [
+    ChromotingEvent.SessionState.STARTED,
+    ChromotingEvent.SessionState.SIGNALING,
+    ChromotingEvent.SessionState.CREATING_PLUGIN,
+    ChromotingEvent.SessionState.CONNECTING,
+    ChromotingEvent.SessionState.AUTHENTICATED,
+    ChromotingEvent.SessionState.CONNECTED,
+    ChromotingEvent.SessionState.CLOSED
+  ]);
+};
+
+/**
+ * @param {remoting.Me2MeTestDriver} testDriver
+ * @param {Object} baseEvent
+ */
 var expectCanceled = function(testDriver, baseEvent) {
   var ChromotingEvent = remoting.ChromotingEvent;
-  var sequence = [{
-    session_state: ChromotingEvent.SessionState.STARTED,
-  },{
-    session_state: ChromotingEvent.SessionState.SIGNALING,
-    previous_session_state: ChromotingEvent.SessionState.STARTED
-  },{
-    session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
-    previous_session_state: ChromotingEvent.SessionState.SIGNALING
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTING,
-    previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTION_CANCELED,
-    previous_session_state: ChromotingEvent.SessionState.CONNECTING
-  }];
-
-  var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
-    var event = /** @type {Object} */ (base.deepCopy(baseEvent));
-    base.mix(event, sequenceValue);
-    return event;
-  });
-  testDriver.expectEvents(expectedEvents);
+  expectSequence(testDriver, baseEvent, [
+    ChromotingEvent.SessionState.STARTED,
+    ChromotingEvent.SessionState.SIGNALING,
+    ChromotingEvent.SessionState.CREATING_PLUGIN,
+    ChromotingEvent.SessionState.CONNECTING,
+    ChromotingEvent.SessionState.CONNECTION_CANCELED
+  ]);
 };
 
 /**
@@ -100,29 +88,18 @@
  */
 var expectFailed = function(testDriver, baseEvent, error) {
   var ChromotingEvent = remoting.ChromotingEvent;
-  var sequence = [{
-    session_state: ChromotingEvent.SessionState.STARTED,
-  },{
-    session_state: ChromotingEvent.SessionState.SIGNALING,
-    previous_session_state: ChromotingEvent.SessionState.STARTED
-  },{
-    session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
-    previous_session_state: ChromotingEvent.SessionState.SIGNALING
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTING,
-    previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
-  },{
-    session_state: ChromotingEvent.SessionState.CONNECTION_FAILED,
-    previous_session_state: ChromotingEvent.SessionState.CONNECTING,
-    connection_error: error
-  }];
+  expectSequence(testDriver, baseEvent, [
+    ChromotingEvent.SessionState.STARTED,
+    ChromotingEvent.SessionState.SIGNALING,
+    ChromotingEvent.SessionState.CREATING_PLUGIN,
+    ChromotingEvent.SessionState.CONNECTING,
+  ]);
 
-  var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
-    var event = /** @type {Object} */ (base.deepCopy(baseEvent));
-    base.mix(event, sequenceValue);
-    return event;
-  });
-  testDriver.expectEvents(expectedEvents);
+  var failedEvent = /** @type {Object} */ (base.deepCopy(baseEvent));
+  failedEvent.session_state = ChromotingEvent.SessionState.CONNECTION_FAILED;
+  failedEvent.previous_session_state = ChromotingEvent.SessionState.CONNECTING;
+  failedEvent.connection_error = error;
+  testDriver.expectEvents([failedEvent]);
 };
 
 QUnit.test('Connection succeeded', function() {
@@ -378,4 +355,55 @@
   return testDriver.startTest();
 });
 
+
+QUnit.test('Connection dropped - Auto Reconnect', function() {
+  var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+  expectSequence(testDriver, {
+    session_entry_point: EntryPoint.CONNECT_BUTTON,
+    role: remoting.ChromotingEvent.Role.CLIENT,
+    mode: remoting.ChromotingEvent.Mode.ME2ME,
+  }, [
+    remoting.ChromotingEvent.SessionState.STARTED,
+    remoting.ChromotingEvent.SessionState.SIGNALING,
+    remoting.ChromotingEvent.SessionState.CREATING_PLUGIN,
+    remoting.ChromotingEvent.SessionState.CONNECTING,
+    remoting.ChromotingEvent.SessionState.AUTHENTICATED,
+    remoting.ChromotingEvent.SessionState.CONNECTED,
+    remoting.ChromotingEvent.SessionState.CONNECTION_DROPPED,
+  ]);
+
+  expectSucceeded(testDriver, {
+    session_entry_point: EntryPoint.AUTO_RECONNECT_ON_CONNECTION_DROPPED,
+    role: remoting.ChromotingEvent.Role.CLIENT,
+    mode: remoting.ChromotingEvent.Mode.ME2ME,
+  });
+
+  var count = 0;
+
+  /**
+   * @param {remoting.MockClientPlugin} plugin
+   * @param {remoting.ClientSession.State} state
+   */
+  function onStatusChanged(plugin, state) {
+    if (state == remoting.ClientSession.State.CONNECTED) {
+      count++;
+      if (count == 1) {
+        // On first CONNECTED, fake network failure.
+        plugin.mock$setConnectionStatus(
+            remoting.ClientSession.State.FAILED,
+            remoting.ClientSession.ConnectionError.NETWORK_FAILURE);
+      } else if (count == 2) {
+        // On second CONNECTED, disconnect and finish the test.
+        testDriver.me2meActivity().stop();
+        testDriver.endTest();
+      }
+    }
+  }
+
+  testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+      onStatusChanged);
+
+  return testDriver.startTest();
+});
+
 })();
diff --git a/remoting/webapp/crd/js/remoting_activity_test_driver.js b/remoting/webapp/crd/js/remoting_activity_test_driver.js
index 08767ba8..f45fa48 100644
--- a/remoting/webapp/crd/js/remoting_activity_test_driver.js
+++ b/remoting/webapp/crd/js/remoting_activity_test_driver.js
@@ -53,6 +53,21 @@
 MockDesktopConnectedView.prototype.setRemapKeys = function() {};
 
 /**
+ * @constructor
+ * @extends {remoting.NetworkConnectivityDetector}
+ */
+var MockNetworkConnectivityDetector = function() {};
+/** @override */
+MockNetworkConnectivityDetector.prototype.waitForOnline = function() {
+  return Promise.resolve();
+};
+
+/** @override */
+MockNetworkConnectivityDetector.prototype.cancel = function() {};
+/** @override */
+MockNetworkConnectivityDetector.prototype.dispose = function() {};
+
+/**
  * A test driver that mocks out the UI components that are required by the
  * DesktopRemotingActivity.
  *
@@ -77,6 +92,12 @@
   this.eventWriterMock_ = sinon.mock(remoting.TelemetryEventWriter.Client);
   /** @private */
   this.setModeStub_ = sinon.stub(remoting, 'setMode');
+  /** @private */
+  this.createNetworkConnectivityDetectorStub_ =
+      sinon.stub(remoting.NetworkConnectivityDetector, 'create', function(){
+        return new MockNetworkConnectivityDetector();
+      });
+
   /**
    * Use fake timers to prevent the generation of session ID expiration events.
    * @private
@@ -103,7 +124,7 @@
   this.setModeStub_.restore();
   this.eventWriterMock_.restore();
   this.desktopConnectedViewCreateStub_.restore();
-
+  this.createNetworkConnectivityDetectorStub_.restore();
   if (Boolean(this.mockConnection_)) {
     this.mockConnection_.restore();
     this.mockConnection_ = null;
@@ -205,6 +226,9 @@
   return this.me2meActivity_;
 };
 
+remoting.Me2MeTestDriver.prototype.mockOnline = function() {
+};
+
 /** @return {Promise} */
 remoting.Me2MeTestDriver.prototype.startTest = function() {
   var host = new remoting.Host('fake_host_id');
diff --git a/remoting/webapp/files.gni b/remoting/webapp/files.gni
index 070c9d45..261d5a6 100644
--- a/remoting/webapp/files.gni
+++ b/remoting/webapp/files.gni
@@ -94,6 +94,7 @@
   "base/js/identity_unittest.js",
   "base/js/ipc_unittest.js",
   "base/js/l10n_unittest.js",
+  "base/js/network_connectivity_detector_unittest.js",
   "base/js/platform_unittest.js",
   "base/js/protocol_extension_manager_unittest.js",
   "base/js/session_logger_unittest.js",
@@ -174,7 +175,6 @@
   "base/js/credentials_provider.js",
   "base/js/experiments.js",
   "base/js/host_desktop.js",
-  "base/js/smart_reconnector.js",
   "base/js/telemetry_event_writer.js",
   "base/js/xmpp_error_cache.js",
 ]
@@ -191,6 +191,7 @@
   "base/js/protocol_extension_manager.js",
   "base/js/protocol_extension.js",
   "base/js/error.js",
+  "base/js/network_connectivity_detector.js",
   "base/js/plugin_settings.js",
   "base/js/suspend_detector.js",
   "base/js/typecheck.js",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 5fea2373..b7c6aeb 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -114,6 +114,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "components_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "components_unittests"
       },
       {
@@ -126,6 +132,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "content_unittests"
       },
       {
@@ -243,6 +255,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "mojo_public_bindings_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "mojo_public_environment_unittests"
       },
       {
@@ -324,6 +342,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "url_unittests"
       },
       {
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index f68b16aab..3569680a 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -161,8 +161,6 @@
 crbug.com/490511 imported/web-platform-tests/html/syntax/parsing/html5lib_tests19.html [ Slow ]
 crbug.com/490511 imported/web-platform-tests/html/syntax/parsing/html5lib_tests2.html [ Slow ]
 
-crbug.com/528421 [ Win ] fast/repaint/fixed-in-page-scale.html [ Slow ]
-
 # FIXME: These tests might still be buggy and time out. They were marked as Slow on 9/20/2013.
 # Double-check the data after they've been running another week or so.
 webkit.org/b/58193 [ Win7 ] http/tests/local/fileapi/send-sliced-dragged-file.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 0eafdcb..7841a9b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -405,9 +405,6 @@
 
 crbug.com/539623 [ SnowLeopard ] fast/repaint/selection-change-in-iframe-with-relative-parent.html [ Pass Failure ImageOnlyFailure ]
 
-# Temporarily disabled until Chromium side supports meaningful MediaStreamTracks.
-crbug.com/532509 fast/mediarecorder/MediaRecorder-basic-video.html [ Skip ]
-
 crbug.com/387740 imported/web-platform-tests/mediacapture-streams/stream-api/introduction/disabled-audio-silence.html [ Skip ]
 crbug.com/387740 imported/web-platform-tests/mediacapture-streams/stream-api/introduction/disabled-video-black.html [ Skip ]
 crbug.com/387740 imported/web-platform-tests/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-end.html [ Skip ]
@@ -419,12 +416,6 @@
 crbug.com/387740 imported/web-platform-tests/mediacapture-streams/stream-api/mediastream/mediastream-idl.html [ Skip ]
 crbug.com/387740 imported/web-platform-tests/mediacapture-streams/stream-api/mediastream/mediastream-removetrack.html [ Skip ]
 
-crbug.com/487028 editing/selection/vertical-rl-rtl-extend-line-backward-p.html [ NeedsRebaseline ]
-crbug.com/487028 editing/selection/vertical-rl-rtl-extend-line-forward-p.html [ NeedsRebaseline ]
-crbug.com/487028 fast/forms/cursor-at-editable-content-boundary.html [ NeedsRebaseline ]
-crbug.com/487028 editing/selection/vertical-rl-rtl-extend-line-backward-br.html [ NeedsRebaseline ]
-crbug.com/487028 editing/selection/vertical-rl-rtl-extend-line-forward-br.html [ NeedsRebaseline ]
-
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/empty-option-param.html [ Failure ]
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-optional-constraint.html [ Failure ]
 crbug.com/412381 imported/web-platform-tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-trivial-constraint.html [ Failure ]
@@ -555,8 +546,6 @@
 crbug.com/505151 imported/csswg-test/css-writing-modes-3/abs-pos-non-replaced-vrl-220.xht [ ImageOnlyFailure ]
 crbug.com/505151 imported/csswg-test/css-writing-modes-3/abs-pos-non-replaced-vrl-224.xht [ ImageOnlyFailure ]
 
-crbug.com/535897 [ Mac Linux ] fast/text/international/complex-joining-using-gpos.html [ NeedsRebaseline ]
-
 crbug.com/492664 imported/csswg-test/css-writing-modes-3/inline-block-alignment-003.xht [ ImageOnlyFailure ]
 crbug.com/492664 imported/csswg-test/css-writing-modes-3/inline-block-alignment-005.xht [ ImageOnlyFailure ]
 crbug.com/492664 imported/csswg-test/css-writing-modes-3/inline-block-alignment-007.xht [ ImageOnlyFailure ]
@@ -1136,10 +1125,6 @@
 # Unclear semantics of ToString (actually ToPrimitive) across iframes.
 crbug.com/532469 http/tests/security/cross-frame-access-custom.html [ NeedsManualRebaseline ]
 
-crbug.com/538692 css3/filters/effect-reference-hw.html [ NeedsRebaseline ]
-crbug.com/538692 css3/filters/effect-reference-subregion-hw.html [ NeedsRebaseline ]
-crbug.com/538692 css3/filters/effect-reference-tile-hw.html [ NeedsRebaseline ]
-
 # Win10 specific failures that still need triaging.
 crbug.com/521730 [ Win10 ] fast/dom/Window/property-access-on-cached-properties-after-frame-navigated.html [ Failure ]
 crbug.com/521730 [ Win10 ] fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-basic-video-expected.txt b/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-basic-video-expected.txt
deleted file mode 100644
index d3482d8..0000000
--- a/third_party/WebKit/LayoutTests/fast/mediarecorder/MediaRecorder-basic-video-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL checks the video-only MediaRecorder API. assert_unreached: Exception while creating MediaRecorder: NotSupportedError: Failed to construct 'MediaRecorder': No MediaRecorder handler can be created. Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 3341cf8e..4765b076 100644
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -4,6 +4,9 @@
     method close
     method constructor
     method slice
+interface ByteLengthQueuingStrategy
+    method constructor
+    method size
 interface Cache
     method add
     method addAll
diff --git a/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.html b/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.html
new file mode 100644
index 0000000..5c02eb5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/serviceworker/resources/test-helpers.js"></script>
+
+<script src="byte-length-queuing-strategy.js"></script>
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('byte-length-queuing-strategy.js'));
+fetch_tests_from_worker(new SharedWorker('byte-length-queuing-strategy.js'));
+service_worker_test('byte-length-queuing-strategy.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.js b/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.js
new file mode 100644
index 0000000..1aaccfe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/streams/byte-length-queuing-strategy.js
@@ -0,0 +1,95 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+
+}, 'Can construct a ByteLengthQueuingStrategy with a valid high water mark');
+
+test(() => {
+
+  for (const highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
+    const strategy = new ByteLengthQueuingStrategy({ highWaterMark });
+    assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
+  }
+
+}, 'Can construct a ByteLengthQueuingStrategy with any value as its high water mark');
+
+test(() => {
+
+  const highWaterMark = 1;
+  const highWaterMarkObjectGetter = {
+    get highWaterMark() { return highWaterMark; },
+  };
+  const error = new Error('wow!');
+  const highWaterMarkObjectGetterThrowing = {
+    get highWaterMark() { throw error; },
+  };
+
+  assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(), 'construction fails with undefined');
+  assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(null), 'construction fails with null');
+  assert_throws({ name: 'Error' }, () => new ByteLengthQueuingStrategy(highWaterMarkObjectGetterThrowing),
+    'construction fails with an object with a throwing highWaterMark getter');
+
+  // Should not fail:
+  new ByteLengthQueuingStrategy('potato');
+  new ByteLengthQueuingStrategy({});
+  new ByteLengthQueuingStrategy(highWaterMarkObjectGetter);
+
+}, 'ByteLengthQueuingStrategy constructor behaves as expected with strange arguments');
+
+test(() => {
+
+  const size = 1024;
+  const chunk = { byteLength: size };
+  const chunkGetter = {
+    get byteLength() { return size; },
+  }
+  const error = new Error('wow!');
+  const chunkGetterThrowing = {
+    get byteLength() { throw error; },
+  }
+  assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(), 'size fails with undefined');
+  assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(null), 'size fails with null');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size('potato'), undefined,
+    'size succeeds with undefined with a random non-object type');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size({}), undefined,
+    'size succeeds with undefined with an object without hwm property');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size(chunk), size,
+    'size succeeds with the right amount with an object with a hwm');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size(chunkGetter), size,
+    'size succeeds with the right amount with an object with a hwm getter');
+  assert_throws({ name: 'Error' }, () => ByteLengthQueuingStrategy.prototype.size(chunkGetterThrowing),
+    'size fails with the error thrown by the getter');
+
+}, 'ByteLengthQueuingStrategy size behaves as expected with strange arguments');
+
+test(() => {
+
+  const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+
+  assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
+    { value: 4, writable: true, enumerable: true, configurable: true },
+    'highWaterMark property should be a data property with the value passed the constructor');
+  assert_equals(typeof strategy.size, 'function');
+
+}, 'ByteLengthQueuingStrategy instances have the correct properties');
+
+test(() => {
+
+  const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+  assert_equals(strategy.highWaterMark, 4);
+
+  strategy.highWaterMark = 10;
+  assert_equals(strategy.highWaterMark, 10);
+
+  strategy.highWaterMark = "banana";
+  assert_equals(strategy.highWaterMark, "banana");
+
+}, 'ByteLengthQueuingStrategy\'s highWaterMark property can be set to anything');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getAXNode-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getAXNode-expected.txt
index 2c3e447c..714f02fd 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getAXNode-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getAXNode-expected.txt
@@ -36,7 +36,33 @@
           "value": false
         }
       }
-    ]
+    ],
+    "name": {
+      "type": "computedString",
+      "value": "",
+      "sources": [
+        {
+          "type": "relatedElement",
+          "attribute": "aria-labelledby"
+        },
+        {
+          "type": "attribute",
+          "attribute": "aria-label"
+        },
+        {
+          "type": "relatedElement",
+          "nativeSource": "label"
+        },
+        {
+          "type": "placeholder",
+          "attribute": "placeholder"
+        },
+        {
+          "type": "attribute",
+          "attribute": "title"
+        }
+      ]
+    }
   }
 }
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships-expected.txt
index 9ef77ec..825de93 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships-expected.txt
@@ -49,16 +49,44 @@
                 "idref": "rg1_label",
                 "nodeResult": "h3#rg1_label"
               }
-            ]
+            ],
+            "value": "rg1_label"
           }
         }
       ],
       "name": {
-        "type": "string",
-        "value": "Lunch Options"
+        "type": "computedString",
+        "value": "Lunch Options",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "value": {
+              "type": "idrefList",
+              "relatedNodeArrayValue": [
+                {
+                  "idref": "rg1_label",
+                  "text": "Lunch Options",
+                  "nodeResult": "h3#rg1_label"
+                }
+              ],
+              "value": "rg1_label"
+            },
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label",
+            "superseded": true
+          },
+          {
+            "type": "attribute",
+            "attribute": "title",
+            "superseded": true
+          }
+        ]
       },
       "description": {
-        "type": "string",
+        "type": "computedString",
         "value": "Lunch Options"
       }
     }
@@ -91,16 +119,44 @@
                 "idref": "rg2_label",
                 "nodeResult": "h3#rg2_label"
               }
-            ]
+            ],
+            "value": "rg2_label"
           }
         }
       ],
       "name": {
-        "type": "string",
-        "value": "Drink Options"
+        "type": "computedString",
+        "value": "Drink Options",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "value": {
+              "type": "idrefList",
+              "relatedNodeArrayValue": [
+                {
+                  "idref": "rg2_label",
+                  "text": "Drink Options",
+                  "nodeResult": "h3#rg2_label"
+                }
+              ],
+              "value": "rg2_label"
+            },
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label",
+            "superseded": true
+          },
+          {
+            "type": "attribute",
+            "attribute": "title",
+            "superseded": true
+          }
+        ]
       },
       "description": {
-        "type": "string",
+        "type": "computedString",
         "value": "Drink Options"
       }
     }
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships.html b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships.html
index 815a158e..39b0239 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-getRelationships.html
@@ -99,6 +99,21 @@
         InspectorTest.completeTest();
     }
 
+    function rewriteValue(value, promises)
+    {
+        if (value.type === "idrefList") {
+            checkExists("relatedNodeArrayValue", value);
+            var relatedNodeArray = value.relatedNodeArrayValue;
+            check(Array.isArray(relatedNodeArray), "relatedNodeArrayValue should be an array", JSON.stringify(value));
+            for (var relatedNode of relatedNodeArray) {
+               promises.push(rewriteNode(relatedNode));
+            }
+        } else if (value.type === "idref") {
+           checkExists("relatedNodeValue", value);
+           var relatedNode = value.relatedNodeValue;
+           promises.push(rewriteNode(relatedNode));
+        }
+    }
     function rewriteNodes(msg)
     {
         if (msg.error) {
@@ -108,22 +123,17 @@
         }
 
         checkExists("result.accessibilityNode.properties", msg);
-        var properties = msg.result.accessibilityNode.properties;
+        var node = msg.result.accessibilityNode;
+        var properties = node.properties;
         var promises = [];
-        for (var property of properties) {
-            if (property.value.type === "idrefList") {
-                checkExists("value.relatedNodeArrayValue", property);
-                var relatedNodeArray = property.value.relatedNodeArrayValue;
-                check(Array.isArray(relatedNodeArray), "value.relatedNodeArrayValue should be an array", JSON.stringify(property));
-                 for (var relatedNode of relatedNodeArray) {
-                     promises.push(rewriteNode(relatedNode));
-                 }
-            } else if (property.value.type === "idref") {
-                checkExists("value.relatedNodeValue", property);
-                var relatedNode = property.value.relatedNodeValue;
-                promises.push(rewriteNode(relatedNode));
-            }
+        if (node.name && node.name.sources) {
+           for (var source of node.name.sources) {
+               if (source.value)
+                   rewriteValue(source.value, promises);
+           }
         }
+        for (var property of properties)
+            rewriteValue(property.value, promises);
 
         function onSuccess()
         {
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
index 1d81777..640ea0f 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
@@ -17,7 +17,25 @@
         "type": "internalRole",
         "value": "Div"
       },
-      "properties": []
+      "properties": [],
+      "name": {
+        "type": "computedString",
+        "value": "",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "attribute",
+            "attribute": "title"
+          }
+        ]
+      }
     }
   }
 }
@@ -53,7 +71,33 @@
             "value": false
           }
         }
-      ]
+      ],
+      "name": {
+        "type": "computedString",
+        "value": "non-hidden treeitem",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "contents",
+            "value": {
+              "type": "string",
+              "value": "non-hidden treeitem"
+            }
+          },
+          {
+            "type": "attribute",
+            "attribute": "title",
+            "superseded": true
+          }
+        ]
+      }
     }
   }
 }
@@ -186,7 +230,34 @@
         "type": "role",
         "value": "img"
       },
-      "properties": []
+      "properties": [],
+      "name": {
+        "type": "computedString",
+        "value": "",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "attribute",
+            "value": {
+              "type": "string",
+              "value": ""
+            },
+            "attribute": "alt"
+          },
+          {
+            "type": "attribute",
+            "attribute": "title",
+            "superseded": true
+          }
+        ]
+      }
     }
   }
 }
@@ -247,7 +318,25 @@
         "type": "internalRole",
         "value": "Div"
       },
-      "properties": []
+      "properties": [],
+      "name": {
+        "type": "computedString",
+        "value": "",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "attribute",
+            "attribute": "title"
+          }
+        ]
+      }
     }
   }
 }
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodesModal-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodesModal-expected.txt
index 563dc097..fc7eb82 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodesModal-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-ignoredNodesModal-expected.txt
@@ -36,7 +36,25 @@
         "type": "role",
         "value": "dialog"
       },
-      "properties": []
+      "properties": [],
+      "name": {
+        "type": "computedString",
+        "value": "",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "attribute",
+            "attribute": "title"
+          }
+        ]
+      }
     }
   }
 }
@@ -50,7 +68,25 @@
         "type": "internalRole",
         "value": "Div"
       },
-      "properties": []
+      "properties": [],
+      "name": {
+        "type": "computedString",
+        "value": "",
+        "sources": [
+          {
+            "type": "relatedElement",
+            "attribute": "aria-labelledby"
+          },
+          {
+            "type": "attribute",
+            "attribute": "aria-label"
+          },
+          {
+            "type": "attribute",
+            "attribute": "title"
+          }
+        ]
+      }
     }
   }
 }
diff --git a/third_party/WebKit/LayoutTests/platform/android/css3/filters/effect-reference-tile-hw-expected.png b/third_party/WebKit/LayoutTests/platform/android/css3/filters/effect-reference-tile-hw-expected.png
deleted file mode 100644
index 61c416bf..0000000
--- a/third_party/WebKit/LayoutTests/platform/android/css3/filters/effect-reference-tile-hw-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux-precise/fast/text/international/complex-joining-using-gpos-expected.png b/third_party/WebKit/LayoutTests/platform/linux-precise/fast/text/international/complex-joining-using-gpos-expected.png
new file mode 100644
index 0000000..a089d96
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux-precise/fast/text/international/complex-joining-using-gpos-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-hw-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-hw-expected.png
index 522a240..90547ba 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-subregion-hw-expected.png
index 6b6ba9a..79b5f7d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.png
index e62623e..ddb81d68 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.txt
index d8447d6..ba526ef 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/international/complex-joining-using-gpos-expected.txt
@@ -10,11 +10,11 @@
         LayoutText {#text} at (0,0) size 679x19
           text run at (0,0) width 679: "The backslash-looking mark should connect into the the character on the center, not be positioned off to the side."
       LayoutBlockFlow {DIV} at (0,72) size 784x123
-        LayoutText {#text} at (0,16) size 279x80
-          text run at (0,16) width 279: "\x{915}+\x{947} = \x{915}\x{947}"
+        LayoutText {#text} at (0,16) size 297x80
+          text run at (0,16) width 297: "\x{915}+ \x{947} = \x{915}\x{947}"
       LayoutBlockFlow {DIV} at (0,195) size 784x53
-        LayoutText {#text} at (0,7) size 121x34
-          text run at (0,7) width 121: "\x{915}+\x{947} = \x{915}\x{947}"
+        LayoutText {#text} at (0,7) size 129x34
+          text run at (0,7) width 129: "\x{915}+ \x{947} = \x{915}\x{947}"
       LayoutBlockFlow {P} at (0,264) size 784x20
         LayoutText {#text} at (0,0) size 586x19
           text run at (0,0) width 586: "The three words should be separated by spaces, and there should be no marks above the spaces."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-hw-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-hw-expected.png
index 1ff35c89..6f12dc5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
index 0baa4ba..c926577 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-tile-hw-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-tile-hw-expected.png
index 48c392c..18802e352 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-tile-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-hw-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-hw-expected.png
index 480a3e4..d15c7545 100644
--- a/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-subregion-hw-expected.png
index 9f671ce1..c99a015 100644
--- a/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win-xp/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-hw-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-hw-expected.png
index 88c5ebf..ed98a8f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
index 078c5574..e0966ff 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-tile-hw-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-tile-hw-expected.png
index 53bca2c0..61c416bf 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-tile-hw-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-compositor-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-compositor-worker-expected.txt
index d86fbbd2..f4d04e47 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-compositor-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-compositor-worker-expected.txt
@@ -13,6 +13,9 @@
 
 Starting worker: resources/global-interface-listing.js
 [Worker] [INTERFACES]
+[Worker] interface ByteLengthQueuingStrategy
+[Worker]     method constructor
+[Worker]     method size
 [Worker] interface CompositorProxy
 [Worker]     getter opacity
 [Worker]     getter scrollLeft
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 1bc4056..81587e03 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -19,6 +19,9 @@
 [Worker]     method close
 [Worker]     method constructor
 [Worker]     method slice
+[Worker] interface ByteLengthQueuingStrategy
+[Worker]     method constructor
+[Worker]     method size
 [Worker] interface Cache
 [Worker]     method add
 [Worker]     method addAll
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index b1b36da..c33e12a 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -317,6 +317,9 @@
     method getCharacteristic
 interface BluetoothUUID
     method constructor
+interface ByteLengthQueuingStrategy
+    method constructor
+    method size
 interface CDATASection
     method constructor
 interface CSS
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
index 7d46f1a..3c27077e 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -11,6 +11,9 @@
 [Worker]     method close
 [Worker]     method constructor
 [Worker]     method slice
+[Worker] interface ByteLengthQueuingStrategy
+[Worker]     method constructor
+[Worker]     method size
 [Worker] interface Cache
 [Worker]     method add
 [Worker]     method addAll
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
index 73e61700..9accf18 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
@@ -145,16 +145,22 @@
     disposeContext(DetachGlobal);
 }
 
-void WindowProxy::takeGlobalFrom(WindowProxy* windowProxy)
+v8::Local<v8::Object> WindowProxy::releaseGlobal()
 {
-    v8::HandleScope handleScope(m_isolate);
-    ASSERT(!windowProxy->isContextInitialized());
+    ASSERT(!isContextInitialized());
     // If a ScriptState was created, the context was initialized at some point.
     // Make sure the global object was detached from the proxy by calling clearForNavigation().
-    if (windowProxy->m_scriptState)
-        ASSERT(windowProxy->m_scriptState->isGlobalObjectDetached());
-    m_global.set(m_isolate, windowProxy->m_global.newLocal(m_isolate));
-    windowProxy->m_global.clear();
+    if (m_scriptState)
+        ASSERT(m_scriptState->isGlobalObjectDetached());
+    v8::Local<v8::Object> global = m_global.newLocal(m_isolate);
+    m_global.clear();
+    return global;
+}
+
+void WindowProxy::setGlobal(v8::Local<v8::Object> global)
+{
+    m_global.set(m_isolate, global);
+
     // Initialize the window proxy now, to re-establish the connection between
     // the global object and the v8::Context. This is really only needed for a
     // RemoteDOMWindow, since it has no scripting environment of its own.
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
index 6c6368e..3ae03af 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
@@ -80,7 +80,8 @@
     void clearForNavigation();
     void clearForClose();
 
-    void takeGlobalFrom(WindowProxy*);
+    v8::Local<v8::Object> releaseGlobal();
+    void setGlobal(v8::Local<v8::Object>);
 
     DOMWrapperWorld& world() { return *m_world; }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
index 4ee62ee0..970faaf3 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
@@ -83,11 +83,17 @@
     }
 }
 
-void WindowProxyManager::takeGlobalFrom(WindowProxyManager* other)
+void WindowProxyManager::releaseGlobals(HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>& map)
 {
-    m_windowProxy->takeGlobalFrom(other->m_windowProxy.get());
-    for (auto& entry : other->m_isolatedWorlds)
-        windowProxy(entry.value->world())->takeGlobalFrom(entry.value.get());
+    map.add(&m_windowProxy->world(), m_windowProxy->releaseGlobal());
+    for (auto& entry : m_isolatedWorlds)
+        map.add(&entry.value->world(), windowProxy(entry.value->world())->releaseGlobal());
+}
+
+void WindowProxyManager::setGlobals(const HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>& map)
+{
+    for (auto& entry : map)
+        windowProxy(*entry.key)->setGlobal(entry.value);
 }
 
 WindowProxyManager::WindowProxyManager(Frame& frame)
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
index 71ac000..ec76b60 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
@@ -5,6 +5,7 @@
 #ifndef WindowProxyManager_h
 #define WindowProxyManager_h
 
+#include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 #include "wtf/Vector.h"
 #include <utility>
@@ -18,7 +19,7 @@
 class SecurityOrigin;
 class WindowProxy;
 
-class WindowProxyManager final : public NoBaseWillBeGarbageCollectedFinalized<WindowProxyManager> {
+class CORE_EXPORT WindowProxyManager final : public NoBaseWillBeGarbageCollectedFinalized<WindowProxyManager> {
     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(WindowProxyManager);
 public:
     static PassOwnPtrWillBeRawPtr<WindowProxyManager> create(Frame&);
@@ -39,7 +40,8 @@
     WindowProxy* existingWindowProxy(DOMWrapperWorld&);
     void collectIsolatedContexts(Vector<std::pair<ScriptState*, SecurityOrigin*>>&);
 
-    void takeGlobalFrom(WindowProxyManager*);
+    void releaseGlobals(HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>&);
+    void setGlobals(const HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>&);
 
 private:
     typedef WillBeHeapHashMap<int, OwnPtrWillBeMember<WindowProxy>> IsolatedWorldMap;
diff --git a/third_party/WebKit/Source/core/animation/Animation.cpp b/third_party/WebKit/Source/core/animation/Animation.cpp
index dc4aeca..4c831eb 100644
--- a/third_party/WebKit/Source/core/animation/Animation.cpp
+++ b/third_party/WebKit/Source/core/animation/Animation.cpp
@@ -994,11 +994,11 @@
         bool wasActive = oldPlayState == Pending || oldPlayState == Running;
         bool isActive = newPlayState == Pending || newPlayState == Running;
         if (!wasActive && isActive)
-            TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("blink.animations,devtools.timeline", "Animation", m_animation, "data", InspectorAnimationEvent::data(*m_animation));
+            TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("blink.animations,devtools.timeline,benchmark", "Animation", m_animation, "data", InspectorAnimationEvent::data(*m_animation));
         else if (wasActive && !isActive)
-            TRACE_EVENT_NESTABLE_ASYNC_END1("blink.animations,devtools.timeline", "Animation", m_animation, "endData", InspectorAnimationStateEvent::data(*m_animation));
+            TRACE_EVENT_NESTABLE_ASYNC_END1("blink.animations,devtools.timeline,benchmark", "Animation", m_animation, "endData", InspectorAnimationStateEvent::data(*m_animation));
         else
-            TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("blink.animations,devtools.timeline", "Animation", m_animation, "data", InspectorAnimationStateEvent::data(*m_animation));
+            TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("blink.animations,devtools.timeline,benchmark", "Animation", m_animation, "data", InspectorAnimationStateEvent::data(*m_animation));
     }
 
     // Ordering is important, the ready promise should resolve/reject before
diff --git a/third_party/WebKit/Source/core/core.gypi b/third_party/WebKit/Source/core/core.gypi
index 52dab96..0320ed7 100644
--- a/third_party/WebKit/Source/core/core.gypi
+++ b/third_party/WebKit/Source/core/core.gypi
@@ -1656,6 +1656,8 @@
             'inspector/InjectedScriptHost.h',
             'inspector/InjectedScriptManager.cpp',
             'inspector/InjectedScriptManager.h',
+            'inspector/InspectedFrames.cpp',
+            'inspector/InspectedFrames.h',
             'inspector/InspectorAnimationAgent.cpp',
             'inspector/InspectorAnimationAgent.h',
             'inspector/InspectorApplicationCacheAgent.cpp',
@@ -1966,6 +1968,7 @@
             'paint/NinePieceImageGrid.h',
             'paint/NinePieceImagePainter.cpp',
             'paint/NinePieceImagePainter.h',
+            'paint/ObjectPaintProperties.h',
             'paint/ObjectPainter.cpp',
             'paint/ObjectPainter.h',
             'paint/PaintInfo.h',
diff --git a/third_party/WebKit/Source/core/frame/Frame.cpp b/third_party/WebKit/Source/core/frame/Frame.cpp
index 6a1fdbc..37419ea 100644
--- a/third_party/WebKit/Source/core/frame/Frame.cpp
+++ b/third_party/WebKit/Source/core/frame/Frame.cpp
@@ -30,7 +30,6 @@
 #include "config.h"
 #include "core/frame/Frame.h"
 
-#include "bindings/core/v8/WindowProxyManager.h"
 #include "core/dom/DocumentType.h"
 #include "core/events/Event.h"
 #include "core/frame/LocalDOMWindow.h"
@@ -161,13 +160,6 @@
     return emptyChromeClient();
 }
 
-void Frame::finishSwapFrom(Frame* old)
-{
-    WindowProxyManager* oldManager = old->windowProxyManager();
-    oldManager->clearForNavigation();
-    windowProxyManager()->takeGlobalFrom(oldManager);
-}
-
 Frame* Frame::findFrameForNavigation(const AtomicString& name, Frame& activeFrame)
 {
     Frame* frame = tree().find(name);
diff --git a/third_party/WebKit/Source/core/frame/Frame.h b/third_party/WebKit/Source/core/frame/Frame.h
index 02c1d310..7bdcb06 100644
--- a/third_party/WebKit/Source/core/frame/Frame.h
+++ b/third_party/WebKit/Source/core/frame/Frame.h
@@ -111,7 +111,6 @@
     // Returns true if the frame is ready to receive the next commit, or false
     // otherwise.
     virtual bool prepareForCommit() = 0;
-    void finishSwapFrom(Frame*);
 
     bool canNavigate(const Frame&);
     virtual void printNavigationErrorMessage(const Frame&, const char* reason) = 0;
@@ -129,11 +128,11 @@
     void setIsLoading(bool isLoading) { m_isLoading = isLoading; }
     bool isLoading() const { return m_isLoading; }
 
+    virtual WindowProxyManager* windowProxyManager() const = 0;
+
 protected:
     Frame(FrameClient*, FrameHost*, FrameOwner*);
 
-    virtual WindowProxyManager* windowProxyManager() const = 0;
-
     mutable FrameTree m_treeNode;
 
     RawPtrWillBeMember<FrameHost> m_host;
diff --git a/third_party/WebKit/Source/core/frame/VisualViewport.cpp b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
index 384faf6..cc6e5b0 100644
--- a/third_party/WebKit/Source/core/frame/VisualViewport.cpp
+++ b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
@@ -370,8 +370,6 @@
         ScrollbarOrientation webcoreOrientation = isHorizontal ? HorizontalScrollbar : VerticalScrollbar;
         webScrollbarLayer = coordinator->createSolidColorScrollbarLayer(webcoreOrientation, thumbThickness, scrollbarMargin, false);
 
-        webScrollbarLayer->setClipLayer(m_innerViewportContainerLayer->platformLayer());
-
         // The compositor will control the scrollbar's visibility. Set to invisible by defualt
         // so scrollbars don't show up in layout tests.
         webScrollbarLayer->layer()->setOpacity(0);
diff --git a/third_party/WebKit/Source/core/html/PluginDocument.cpp b/third_party/WebKit/Source/core/html/PluginDocument.cpp
index e64e048..05fb404 100644
--- a/third_party/WebKit/Source/core/html/PluginDocument.cpp
+++ b/third_party/WebKit/Source/core/html/PluginDocument.cpp
@@ -103,11 +103,13 @@
     m_embedElement->setAttribute(widthAttr, "100%");
     m_embedElement->setAttribute(heightAttr, "100%");
     m_embedElement->setAttribute(nameAttr, "plugin");
+    m_embedElement->setAttribute(idAttr, "plugin");
     m_embedElement->setAttribute(srcAttr, AtomicString(document()->url().string()));
     m_embedElement->setAttribute(typeAttr, document()->loader()->mimeType());
     body->appendChild(m_embedElement);
 
     toPluginDocument(document())->setPluginNode(m_embedElement.get());
+    m_embedElement->focus();
 
     document()->updateLayout();
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectedFrames.cpp b/third_party/WebKit/Source/core/inspector/InspectedFrames.cpp
new file mode 100644
index 0000000..12911049
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InspectedFrames.cpp
@@ -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 "config.h"
+#include "core/inspector/InspectedFrames.h"
+
+#include "core/frame/LocalFrame.h"
+
+namespace blink {
+
+InspectedFrames::InspectedFrames(LocalFrame* root)
+    : m_root(root)
+{
+}
+
+InspectedFrames::Iterator InspectedFrames::begin()
+{
+    return Iterator(m_root, m_root);
+}
+
+InspectedFrames::Iterator InspectedFrames::end()
+{
+    return Iterator(m_root, nullptr);
+}
+
+InspectedFrames::Iterator::Iterator(LocalFrame* root, LocalFrame* current)
+    : m_root(root)
+    , m_current(current)
+{
+}
+
+InspectedFrames::Iterator& InspectedFrames::Iterator::operator++()
+{
+    if (!m_current)
+        return *this;
+    Frame* frame = m_current->tree().traverseNext(m_root);
+    m_current = nullptr;
+    for (; frame; frame = frame->tree().traverseNext(m_root)) {
+        if (!frame->isLocalFrame())
+            continue;
+        LocalFrame* local = toLocalFrame(frame);
+        if (local->instrumentingAgents() == m_root->instrumentingAgents()) {
+            m_current = local;
+            break;
+        }
+    }
+    return *this;
+}
+
+InspectedFrames::Iterator InspectedFrames::Iterator::operator++(int)
+{
+    LocalFrame* old = m_current;
+    ++*this;
+    return Iterator(m_root, old);
+}
+
+bool InspectedFrames::Iterator::operator==(const Iterator& other)
+{
+    return m_current == other.m_current && m_root == other.m_root;
+}
+
+bool InspectedFrames::Iterator::operator!=(const Iterator& other)
+{
+    return !(*this == other);
+}
+
+} // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InspectedFrames.h b/third_party/WebKit/Source/core/inspector/InspectedFrames.h
new file mode 100644
index 0000000..b2f1cad
--- /dev/null
+++ b/third_party/WebKit/Source/core/inspector/InspectedFrames.h
@@ -0,0 +1,44 @@
+// 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 InspectedFrames_h
+#define InspectedFrames_h
+
+#include "core/CoreExport.h"
+#include "wtf/Noncopyable.h"
+
+namespace blink {
+
+class LocalFrame;
+
+class CORE_EXPORT InspectedFrames {
+    WTF_MAKE_NONCOPYABLE(InspectedFrames);
+public:
+    class Iterator {
+    public:
+        Iterator operator++(int);
+        Iterator& operator++();
+        bool operator==(const Iterator& other);
+        bool operator!=(const Iterator& other);
+        LocalFrame* operator*() { return m_current; }
+        LocalFrame* operator->() { return m_current; }
+    private:
+        friend class InspectedFrames;
+        Iterator(LocalFrame* root, LocalFrame* current);
+        LocalFrame* m_root;
+        LocalFrame* m_current;
+    };
+
+    explicit InspectedFrames(LocalFrame* root);
+    LocalFrame* root() { return m_root; }
+    Iterator begin();
+    Iterator end();
+
+private:
+    LocalFrame* m_root;
+};
+
+} // namespace blink
+
+#endif // InspectedFrames_h
diff --git a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
index 94e4385f..2cf9bef 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
@@ -20,6 +20,7 @@
 #include "core/css/resolver/StyleResolver.h"
 #include "core/dom/DOMNodeIds.h"
 #include "core/inspector/InjectedScriptManager.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorDOMAgent.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/inspector/InspectorState.h"
@@ -188,10 +189,8 @@
 
 void InspectorAnimationAgent::setPlaybackRate(ErrorString*, double playbackRate)
 {
-    for (Frame* frame = m_pageAgent->inspectedFrame(); frame; frame = frame->tree().traverseNext(m_pageAgent->inspectedFrame())) {
-        if (frame->isLocalFrame())
-            toLocalFrame(frame)->document()->timeline().setPlaybackRate(playbackRate);
-    }
+    for (LocalFrame* frame : InspectedFrames(m_pageAgent->inspectedFrame()))
+        frame->document()->timeline().setPlaybackRate(playbackRate);
 }
 
 void InspectorAnimationAgent::getCurrentTime(ErrorString* errorString, const String& id, double* currentTime)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorApplicationCacheAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorApplicationCacheAgent.cpp
index e3b0943..d5530be 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorApplicationCacheAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorApplicationCacheAgent.cpp
@@ -28,6 +28,7 @@
 
 #include "core/frame/LocalFrame.h"
 #include "core/inspector/IdentifiersFactory.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/inspector/InspectorState.h"
 #include "core/inspector/InstrumentingAgents.h"
@@ -94,20 +95,17 @@
 {
     result = TypeBuilder::Array<TypeBuilder::ApplicationCache::FrameWithManifest>::create();
 
-    LocalFrame* inspectedFrame = m_pageAgent->inspectedFrame();
-    for (Frame* frame = inspectedFrame; frame; frame = frame->tree().traverseNext(inspectedFrame)) {
-        if (!frame->isLocalFrame())
-            continue;
-        DocumentLoader* documentLoader = toLocalFrame(frame)->loader().documentLoader();
+    for (LocalFrame* frame : InspectedFrames(m_pageAgent->inspectedFrame())) {
+        DocumentLoader* documentLoader = frame->loader().documentLoader();
         if (!documentLoader)
-            continue;
+            return;
 
         ApplicationCacheHost* host = documentLoader->applicationCacheHost();
         ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo();
         String manifestURL = info.m_manifest.string();
         if (!manifestURL.isEmpty()) {
             RefPtr<TypeBuilder::ApplicationCache::FrameWithManifest> value = TypeBuilder::ApplicationCache::FrameWithManifest::create()
-                .setFrameId(IdentifiersFactory::frameId(toLocalFrame(frame)))
+                .setFrameId(IdentifiersFactory::frameId(frame))
                 .setManifestURL(manifestURL)
                 .setStatus(static_cast<int>(host->status()));
             result->addItem(value);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
index 38f8be7..71742fce 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
@@ -68,6 +68,7 @@
 #include "core/inspector/IdentifiersFactory.h"
 #include "core/inspector/InjectedScriptHost.h"
 #include "core/inspector/InjectedScriptManager.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorHighlight.h"
 #include "core/inspector/InspectorHistory.h"
 #include "core/inspector/InspectorPageAgent.h"
@@ -303,13 +304,11 @@
 WillBeHeapVector<RawPtrWillBeMember<Document> > InspectorDOMAgent::documents()
 {
     WillBeHeapVector<RawPtrWillBeMember<Document> > result;
-    for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
-        if (!frame->isLocalFrame())
-            continue;
-        Document* document = toLocalFrame(frame)->document();
-        if (!document)
-            continue;
-        result.append(document);
+    if (m_document) {
+        for (LocalFrame* frame : InspectedFrames(m_pageAgent->inspectedFrame())) {
+            if (Document* document = frame->document())
+                result.append(document);
+        }
     }
     return result;
 }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
index 09772def..ed16c08d 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
@@ -207,7 +207,7 @@
 
 PassRefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> > InspectorLayerTreeAgent::buildLayerTree()
 {
-    PaintLayerCompositor* compositor = deprecatedPaintLayerCompositor();
+    PaintLayerCompositor* compositor = paintLayerCompositor();
     if (!compositor || !compositor->inCompositingMode())
         return nullptr;
 
@@ -254,7 +254,7 @@
     return DOMNodeIds::idForNode(node);
 }
 
-PaintLayerCompositor* InspectorLayerTreeAgent::deprecatedPaintLayerCompositor()
+PaintLayerCompositor* InspectorLayerTreeAgent::paintLayerCompositor()
 {
     LayoutView* layoutView = m_pageAgent->inspectedFrame()->contentLayoutObject();
     PaintLayerCompositor* compositor = layoutView ? layoutView->compositor() : nullptr;
@@ -289,7 +289,7 @@
         *errorString = "Invalid layer id";
         return nullptr;
     }
-    PaintLayerCompositor* compositor = deprecatedPaintLayerCompositor();
+    PaintLayerCompositor* compositor = paintLayerCompositor();
     if (!compositor) {
         *errorString = "Not in compositing mode";
         return nullptr;
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.h b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.h
index 4f758dd..fbd1b82b 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.h
@@ -94,7 +94,7 @@
 
     GraphicsLayer* rootGraphicsLayer();
 
-    PaintLayerCompositor* deprecatedPaintLayerCompositor();
+    PaintLayerCompositor* paintLayerCompositor();
     GraphicsLayer* layerById(ErrorString*, const String& layerId);
     const PictureSnapshot* snapshotById(ErrorString*, const String& snapshotId);
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectorOverlayPage.html b/third_party/WebKit/Source/core/inspector/InspectorOverlayPage.html
index 9b83ed24..4f473b03 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorOverlayPage.html
+++ b/third_party/WebKit/Source/core/inspector/InspectorOverlayPage.html
@@ -1275,11 +1275,6 @@
 </script>
 </head>
 <body class="fill">
-<div class="controls-line">
-    <div class="message-box"><div id="paused-in-debugger"></div></div>
-    <div class="button" id="resume-button" title="Resume script execution (F8)."><div class="glyph"></div></div>
-    <div class="button" id="step-over-button" title="Step over next function call (F10)."><div class="glyph"></div></div>
-</div>
 </body>
 <canvas id="canvas" class="fill"></canvas>
 <div id="element-title">
@@ -1288,5 +1283,10 @@
 </div>
 <div id="tooltip-container"></div>
 <div id="editor" class="fill"></div>
+<div class="controls-line">
+    <div class="message-box"><div id="paused-in-debugger"></div></div>
+    <div class="button" id="resume-button" title="Resume script execution (F8)."><div class="glyph"></div></div>
+    <div class="button" id="step-over-button" title="Step over next function call (F10)."><div class="glyph"></div></div>
+</div>
 <div id="log"></div>
 </html>
diff --git a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
index e2ac51b3..7926014 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
@@ -53,6 +53,7 @@
 #include "core/inspector/ContentSearchUtils.h"
 #include "core/inspector/DOMPatchSupport.h"
 #include "core/inspector/IdentifiersFactory.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorCSSAgent.h"
 #include "core/inspector/InspectorDebuggerAgent.h"
 #include "core/inspector/InspectorInstrumentation.h"
@@ -652,12 +653,9 @@
 
 LocalFrame* InspectorPageAgent::findFrameWithSecurityOrigin(const String& originRawString)
 {
-    for (Frame* frame = inspectedFrame(); frame; frame = frame->tree().traverseNext(inspectedFrame())) {
-        if (!frame->isLocalFrame())
-            continue;
-        RefPtr<SecurityOrigin> documentOrigin = toLocalFrame(frame)->document()->securityOrigin();
-        if (documentOrigin->toRawString() == originRawString)
-            return toLocalFrame(frame);
+    for (LocalFrame* frame : InspectedFrames(inspectedFrame())) {
+        if (frame->document()->securityOrigin()->toRawString() == originRawString)
+            return frame;
     }
     return nullptr;
 }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp
index b4ada728..56a07c2 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp
@@ -50,6 +50,7 @@
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/ConsoleMessageStorage.h"
 #include "core/inspector/IdentifiersFactory.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/inspector/InspectorState.h"
 #include "core/inspector/InstrumentingAgents.h"
@@ -1024,10 +1025,8 @@
     m_state->setBoolean(ResourceAgentState::cacheDisabled, cacheDisabled);
     if (cacheDisabled)
         memoryCache()->evictResources();
-    for (Frame* frame = m_pageAgent->inspectedFrame(); frame; frame = frame->tree().traverseNext()) {
-        if (frame->isLocalFrame())
-            toLocalFrame(frame)->document()->fetcher()->garbageCollectDocumentResources();
-    }
+    for (LocalFrame* frame : InspectedFrames(m_pageAgent->inspectedFrame()))
+        frame->document()->fetcher()->garbageCollectDocumentResources();
 }
 
 void InspectorResourceAgent::emulateNetworkConditions(ErrorString*, bool, double, double, double)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp b/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
index 85c904b..c9a9f038 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
@@ -16,6 +16,7 @@
 #include "core/fetch/ResourcePtr.h"
 #include "core/fetch/StyleSheetResourceClient.h"
 #include "core/frame/LocalFrame.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorCSSAgent.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/page/Page.h"
@@ -84,12 +85,9 @@
 {
     m_started = true;
     WillBeHeapVector<RawPtrWillBeMember<Document>> documents;
-    for (Frame* frame = m_inspectedFrame; frame; frame = frame->tree().traverseNext(m_inspectedFrame)) {
-        if (!frame->isLocalFrame())
-            continue;
-        LocalFrame* localFrame = toLocalFrame(frame);
-        documents.append(localFrame->document());
-        documents.appendVector(InspectorPageAgent::importsForFrame(localFrame));
+    for (LocalFrame* frame : InspectedFrames(m_inspectedFrame)) {
+        documents.append(frame->document());
+        documents.appendVector(InspectorPageAgent::importsForFrame(frame));
     }
     for (Document* document : documents) {
         HashSet<String> urlsToFetch;
diff --git a/third_party/WebKit/Source/core/inspector/PageRuntimeAgent.cpp b/third_party/WebKit/Source/core/inspector/PageRuntimeAgent.cpp
index be8b355..fcac1cd 100644
--- a/third_party/WebKit/Source/core/inspector/PageRuntimeAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/PageRuntimeAgent.cpp
@@ -39,6 +39,7 @@
 #include "core/inspector/IdentifiersFactory.h"
 #include "core/inspector/InjectedScript.h"
 #include "core/inspector/InjectedScriptManager.h"
+#include "core/inspector/InspectedFrames.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/inspector/InstrumentingAgents.h"
 #include "core/page/Page.h"
@@ -154,20 +155,17 @@
 
 void PageRuntimeAgent::reportExecutionContextCreation()
 {
-    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
-    for (Frame* frame = m_pageAgent->inspectedFrame(); frame; frame = frame->tree().traverseNext(m_pageAgent->inspectedFrame())) {
-        if (!frame->isLocalFrame())
+    Vector<std::pair<ScriptState*, SecurityOrigin*>> isolatedContexts;
+    for (LocalFrame* frame : InspectedFrames(m_pageAgent->inspectedFrame())) {
+        if (!frame->script().canExecuteScripts(NotAboutToExecuteScript))
             continue;
-        LocalFrame* localFrame = toLocalFrame(frame);
-        if (!localFrame->script().canExecuteScripts(NotAboutToExecuteScript))
-            continue;
-        String frameId = IdentifiersFactory::frameId(localFrame);
+        String frameId = IdentifiersFactory::frameId(frame);
 
         // Ensure execution context is created.
         // If initializeMainWorld returns true, then is registered by didCreateScriptContext
-        if (!localFrame->script().initializeMainWorld())
-            reportExecutionContext(ScriptState::forMainWorld(localFrame), true, "", frameId);
-        localFrame->script().collectIsolatedContexts(isolatedContexts);
+        if (!frame->script().initializeMainWorld())
+            reportExecutionContext(ScriptState::forMainWorld(frame), true, "", frameId);
+        frame->script().collectIsolatedContexts(isolatedContexts);
         if (isolatedContexts.isEmpty())
             continue;
         for (const auto& pair : isolatedContexts) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 44a9fa5..cc1ea91 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -2040,9 +2040,12 @@
 
 LayoutUnit LayoutBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
 {
+    ASSERT(availableLogicalWidth >= 0);
     marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth);
     marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth);
-    return availableLogicalWidth - marginStart - marginEnd;
+    LayoutUnit available = availableLogicalWidth - marginStart - marginEnd;
+    available = std::max(available, LayoutUnit());
+    return available;
 }
 
 LayoutUnit LayoutBox::computeIntrinsicLogicalWidthUsing(const Length& logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index c0998aa..7ec9481 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -80,6 +80,94 @@
     LayoutUnit m_paginationStrut;
 };
 
+// LayoutBox implements the full CSS box model.
+//
+// LayoutBoxModelObject only introduces some abstractions for LayoutInline and
+// LayoutBox. The logic for the model is in LayoutBox, e.g. the storage for the
+// rectangle and offset forming the CSS box (m_frameRect) and the getters for
+// the different boxes.
+//
+// LayoutBox is also the uppermost class to support scrollbars, however the
+// logic is delegated to PaintLayerScrollableArea.
+// Per the CSS specification, scrollbars should "be inserted between the inner
+// border edge and the outer padding edge".
+// (see http://www.w3.org/TR/CSS21/visufx.html#overflow)
+// Also the scrollbar width / height are removed from the content box. Taking
+// the following example:
+//
+// <!DOCTYPE html>
+// <style>
+// ::-webkit-scrollbar {
+//     /* Force non-overlay scrollbars */
+//     width: 10px;
+//     height: 20px;
+// }
+// </style>
+// <div style="overflow:scroll; width: 100px; height: 100px">
+//
+// The <div>'s content box is not 100x100 as specified in the style but 90x80 as
+// we remove the scrollbars from the box.
+//
+// The presence of scrollbars is determined by the 'overflow' property and can
+// be conditioned on having layout overflow (see OverflowModel for more details
+// on how we track overflow).
+//
+// There are 2 types of scrollbars:
+// - non-overlay scrollbars take space from the content box.
+// - overlay scrollbars don't and just overlay hang off from the border box,
+//   potentially overlapping with the padding box's content.
+//
+//
+// ***** THE BOX MODEL *****
+// The CSS box model is based on a series of nested boxes:
+// http://www.w3.org/TR/CSS21/box.html
+//
+//       |----------------------------------------------------|
+//       |                                                    |
+//       |                   margin-top                       |
+//       |                                                    |
+//       |     |-----------------------------------------|    |
+//       |     |                                         |    |
+//       |     |             border-top                  |    |
+//       |     |                                         |    |
+//       |     |    |--------------------------|----|    |    |
+//       |     |    |                          |    |    |    |
+//       |     |    |       padding-top        |####|    |    |
+//       |     |    |                          |####|    |    |
+//       |     |    |    |----------------|    |####|    |    |
+//       |     |    |    |                |    |    |    |    |
+//       | ML  | BL | PL |  content box   | PR | SW | BR | MR |
+//       |     |    |    |                |    |    |    |    |
+//       |     |    |    |----------------|    |    |    |    |
+//       |     |    |                          |    |    |    |
+//       |     |    |      padding-bottom      |    |    |    |
+//       |     |    |--------------------------|----|    |    |
+//       |     |    |                      ####|    |    |    |
+//       |     |    |     scrollbar height ####| SC |    |    |
+//       |     |    |                      ####|    |    |    |
+//       |     |    |-------------------------------|    |    |
+//       |     |                                         |    |
+//       |     |           border-bottom                 |    |
+//       |     |                                         |    |
+//       |     |-----------------------------------------|    |
+//       |                                                    |
+//       |                 margin-bottom                      |
+//       |                                                    |
+//       |----------------------------------------------------|
+//
+// BL = border-left
+// BR = border-right
+// ML = margin-left
+// MR = margin-right
+// PL = padding-left
+// PR = padding-right
+// SC = scroll corner (contains UI for resizing (see the 'resize' property)
+// SW = scrollbar width
+//
+// Those are just the boxes from the CSS model. Extra boxes are tracked by Blink
+// (e.g. the overflows). Thus it is paramount to know which box a function is
+// manipulating. Also of critical importance is the coordinate system used (see
+// the COORDINATE SYSTEMS section in LayoutBoxModelObject).
 class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
 public:
     explicit LayoutBox(ContainerNode*);
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
index 1ee9b3d..04a47f3 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
@@ -65,8 +65,8 @@
 
 // This class is the base class for all CSS objects.
 //
-// All CSS objects follow the box model object:
-// http://www.w3.org/TR/CSS21/box.html
+// All CSS objects follow the box model object. See THE BOX MODEL section in
+// LayoutBox for more information.
 //
 // This class actually doesn't have the box model but it exposes some common
 // functions or concepts that sub-classes can extend upon. For example, there
@@ -86,6 +86,9 @@
 // design as it limits code sharing and prevents hardware accelerating SVG
 // (the current design require a PaintLayer for compositing).
 //
+//
+// ***** COORDINATE SYSTEMS *****
+//
 // In order to fully understand LayoutBoxModelObject and the inherited classes,
 // we need to introduce the concept of coordinate systems.
 // There is 3 main coordinate systems:
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
index 232d9ec..9a6b052 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
@@ -869,6 +869,7 @@
     LayoutUnit maxExtent = -1;
     if (max.isSpecifiedOrIntrinsic()) {
         maxExtent = computeMainAxisExtentForChild(child, MaxSize, max);
+        ASSERT(maxExtent >= -1);
         if (maxExtent != -1 && childSize > maxExtent)
             childSize = maxExtent;
     }
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 9187c5d3..f358ae92 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -2144,8 +2144,7 @@
         dirtyRect.intersect(paintInfo.localClipRectForSquashedLayer);
         {
             ASSERT(context->displayItemList());
-            if (!context->displayItemList()->displayItemConstructionIsDisabled())
-                context->displayItemList()->createAndAppend<ClipDisplayItem>(*this, DisplayItem::ClipLayerOverflowControls, dirtyRect);
+            context->displayItemList()->createAndAppend<ClipDisplayItem>(*this, DisplayItem::ClipLayerOverflowControls, dirtyRect);
         }
         PaintLayerPainter(*paintInfo.paintLayer).paintLayer(context, paintingInfo, paintLayerFlags);
         {
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 5c470ee..6cd6c80 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -310,7 +310,7 @@
     scrollbarGraphicsLayer->setDrawsContent(true);
 }
 
-static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer, WebLayer* containerLayer)
+static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer)
 {
     ASSERT(scrollbarGraphicsLayer);
     ASSERT(scrollbarLayer);
@@ -320,7 +320,6 @@
         return;
     }
     scrollbarLayer->setScrollLayer(scrollLayer);
-    scrollbarLayer->setClipLayer(containerLayer);
     scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer());
     scrollbarGraphicsLayer->setDrawsContent(false);
 }
@@ -381,8 +380,7 @@
         }
 
         WebLayer* scrollLayer = toWebLayer(scrollableArea->layerForScrolling());
-        WebLayer* scrollbarContainerLayer = toWebLayer(scrollableArea->layerForContainer());
-        setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollLayer, scrollbarContainerLayer);
+        setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollLayer);
 
         // Root layer non-overlay scrollbars should be marked opaque to disable
         // blending.
@@ -431,13 +429,13 @@
     if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) {
         GraphicsLayer* horizontalScrollbarLayer = scrollableArea->layerForHorizontalScrollbar();
         if (horizontalScrollbarLayer)
-            setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
+            setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer);
     }
     if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) {
         GraphicsLayer* verticalScrollbarLayer = scrollableArea->layerForVerticalScrollbar();
 
         if (verticalScrollbarLayer)
-            setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
+            setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer);
     }
 
     // Update the viewport layer registration if the outer viewport may have changed.
diff --git a/third_party/WebKit/Source/core/paint/CompositingRecorder.cpp b/third_party/WebKit/Source/core/paint/CompositingRecorder.cpp
index dea972f..0fb894e 100644
--- a/third_party/WebKit/Source/core/paint/CompositingRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/CompositingRecorder.cpp
@@ -28,8 +28,6 @@
 void CompositingRecorder::beginCompositing(GraphicsContext& graphicsContext, const DisplayItemClientWrapper& client, const SkXfermode::Mode xferMode, const float opacity, const FloatRect* bounds, ColorFilter colorFilter)
 {
     ASSERT(graphicsContext.displayItemList());
-    if (graphicsContext.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     graphicsContext.displayItemList()->createAndAppend<BeginCompositingDisplayItem>(client, xferMode, opacity, bounds, colorFilter);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/FloatClipRecorder.cpp b/third_party/WebKit/Source/core/paint/FloatClipRecorder.cpp
index 22c079c..5e534ed 100644
--- a/third_party/WebKit/Source/core/paint/FloatClipRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/FloatClipRecorder.cpp
@@ -17,8 +17,6 @@
     , m_clipType(DisplayItem::paintPhaseToFloatClipType(paintPhase))
 {
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<FloatClipDisplayItem>(m_client, m_clipType, clipRect);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/LayerClipRecorder.cpp b/third_party/WebKit/Source/core/paint/LayerClipRecorder.cpp
index afd686d..91579e11 100644
--- a/third_party/WebKit/Source/core/paint/LayerClipRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/LayerClipRecorder.cpp
@@ -29,8 +29,6 @@
     }
 
     ASSERT(m_graphicsContext.displayItemList());
-    if (m_graphicsContext.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_graphicsContext.displayItemList()->createAndAppend<ClipDisplayItem>(layoutObject, m_clipType, snappedClipRect, roundedRects);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/LayerFixedPositionRecorder.cpp b/third_party/WebKit/Source/core/paint/LayerFixedPositionRecorder.cpp
index 41c086e..8ef3fc1 100644
--- a/third_party/WebKit/Source/core/paint/LayerFixedPositionRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/LayerFixedPositionRecorder.cpp
@@ -23,9 +23,6 @@
     if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
         return;
 
-    if (m_graphicsContext.displayItemList()->displayItemConstructionIsDisabled())
-        return;
-
     if (m_isFixedPosition)
         m_graphicsContext.displayItemList()->createAndAppend<BeginFixedPositionDisplayItem>(m_layoutObject);
 
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
new file mode 100644
index 0000000..5bce088
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
@@ -0,0 +1,49 @@
+// 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 ObjectPaintProperties_h
+#define ObjectPaintProperties_h
+
+#include "platform/graphics/paint/TransformPaintPropertyNode.h"
+#include "wtf/PassRefPtr.h"
+#include "wtf/RefPtr.h"
+
+namespace blink {
+
+// The minimal set of paint properties created by a |LayoutObject|. These
+// properties encode a hierachy of transforms, clips, effects, etc, both between
+// LayoutObjects (each property has a parent) and among the properties of a
+// single LayoutObject (e.g., transform and perspective with the correct parent
+// relationship to represent ordering).
+//
+// This differs from |PaintChunkProperties| because it can store multiple
+// properties of the same type (e.g., transform and perspective which are both
+// transforms).
+class ObjectPaintProperties {
+    WTF_MAKE_NONCOPYABLE(ObjectPaintProperties);
+    WTF_MAKE_FAST_ALLOCATED(ObjectPaintProperties);
+public:
+    static PassOwnPtr<ObjectPaintProperties> create()
+    {
+        return adoptPtr(new ObjectPaintProperties());
+    }
+
+    bool hasTransform() const { return m_transform; }
+    void setTransform(PassRefPtr<TransformPaintPropertyNode> transform) { m_transform = transform; }
+    const TransformPaintPropertyNode* transform() const { return m_transform.get(); }
+
+    bool hasPerspective() const { return m_perspective; }
+    void setPerspective(PassRefPtr<TransformPaintPropertyNode> perspective) { m_perspective = perspective; }
+    const TransformPaintPropertyNode* perspective() const { return m_perspective.get(); }
+
+private:
+    ObjectPaintProperties() { }
+
+    RefPtr<TransformPaintPropertyNode> m_transform;
+    RefPtr<TransformPaintPropertyNode> m_perspective;
+};
+
+} // namespace blink
+
+#endif // ObjectPaintProperties_h
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 7bdb7351..14d04fe 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -72,6 +72,7 @@
 #include "core/page/Page.h"
 #include "core/page/scrolling/ScrollingCoordinator.h"
 #include "core/paint/FilterEffectBuilder.h"
+#include "core/paint/ObjectPaintProperties.h"
 #include "platform/LengthFunctions.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/TraceEvent.h"
@@ -2714,6 +2715,22 @@
     }
 }
 
+ObjectPaintProperties& PaintLayer::mutablePaintProperties()
+{
+    ASSERT(RuntimeEnabledFeatures::slimmingPaintV2Enabled());
+    ASSERT(layoutObject()->document().lifecycle().state() == DocumentLifecycle::InUpdatePaintProperties);
+    if (!m_paintProperties)
+        m_paintProperties = ObjectPaintProperties::create();
+    return *m_paintProperties;
+}
+
+const ObjectPaintProperties* PaintLayer::paintProperties() const
+{
+    ASSERT(RuntimeEnabledFeatures::slimmingPaintV2Enabled());
+    ASSERT(layoutObject()->document().lifecycle().state() == DocumentLifecycle::InPaint);
+    return m_paintProperties.get();
+}
+
 DisableCompositingQueryAsserts::DisableCompositingQueryAsserts()
     : m_disabler(gCompositingQueryMode, CompositingQueriesAreAllowed) { }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index 57a380e..a9a83600 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -66,6 +66,7 @@
 class HitTestRequest;
 class HitTestResult;
 class HitTestingTransformState;
+class ObjectPaintProperties;
 class PaintLayerCompositor;
 class CompositedLayerMapping;
 class ComputedStyle;
@@ -89,28 +90,21 @@
 };
 
 // PaintLayer is an old object that handles lots of unrelated operations.
-// We want it to die at some point and be replaced by more focused objects. Removing
-// a lot of unneeded complexity.
-// Complex painting operations (opacity, clipping, filters, reflections, ...),
-// hardware acceleration (through PaintLayerCompositor),
-// scrolling (through PaintLayerScrollableArea)
-// along with some optimizations are all handled by PaintLayer.
+// We want it to die at some point and be replaced by more focused objects.
+// Removing a lot of unneeded complexity. Complex painting operations (opacity,
+// clipping, filters, reflections, ...), hardware acceleration (through
+// PaintLayerCompositor), scrolling (through PaintLayerScrollableArea) along
+// with some optimizations are all handled by PaintLayer.
 //
 // The class is central to painting and hit-testing: it implements the painting
-// order through PaintLayerStackingNode and handle lots of complex
-// graphics operations LayoutObjects don't handle (e.g. 'filter' and 'opacity').
+// order through PaintLayerStackingNode and handle lots of complex graphics
+// operations LayoutObjects don't handle (e.g. 'filter' and 'opacity').
 //
-// The compositing code is also based on PaintLayer. The entry to it
-// is the PaintLayerCompositor, which fills
-// |m_compositedLayerMapping| for hardware accelerated layers.
+// The compositing code is also based on PaintLayer. The entry to it is the
+// PaintLayerCompositor, which fills |m_compositedLayerMapping| for hardware
+// accelerated layers.
 //
 // TODO(jchaffraix): Expand the documentation about hardware acceleration.
-//
-// The class is DEPRECATED, which means that we would like to remove it. The
-// reason for removal is that it has been a dumping ground for features for too
-// long and is the wrong level of abstraction, bearing no correspondence to any
-// CSS concept. Its associated objects and some of its feature need to be
-// migrated to LayoutObject (or the appropriate sub-class).
 class CORE_EXPORT PaintLayer {
     WTF_MAKE_NONCOPYABLE(PaintLayer);
 public:
@@ -575,6 +569,12 @@
     // For subsequence display items.
     DisplayItemClient displayItemClient() const { return toDisplayItemClient(this); }
 
+    // Paint properties encode the hierarchical transform, clip, scroll, and
+    // effect information used for painting. Properties should only be changed
+    // during UpdatePaintProperties, and should only be used during Paint.
+    ObjectPaintProperties& mutablePaintProperties();
+    const ObjectPaintProperties* paintProperties() const;
+
 private:
     // Bounding box in the coordinates of this layer.
     LayoutRect logicalBoundingBox() const;
@@ -759,6 +759,11 @@
     OwnPtr<PaintLayerStackingNode> m_stackingNode;
     OwnPtr<PaintLayerReflectionInfo> m_reflectionInfo;
 
+    // Lazily-allocated paint properties for m_layoutObject, stored here to
+    // reduce memory usage as PaintLayers are created for all paint property
+    // layout objects.
+    OwnPtr<ObjectPaintProperties> m_paintProperties;
+
     LayoutSize m_subpixelAccumulation; // The accumulated subpixel offset of a composited layer's composited bounds compared to absolute coordinates.
 
     IntSize m_previousScrollOffsetAccumulationForPainting;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
index 5e5c494..44d2eef6 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
@@ -152,9 +152,6 @@
 // #container and #fixed are siblings in the paint tree but #container does
 // clip #fixed. This is the reason why we compute the painting clip rects during
 // a layout tree walk and cache them for painting.
-//
-// This class is NOT DEPRECATED, PaintLayer is and we match its
-// naming.
 class PaintLayerClipper {
     DISALLOW_ALLOCATION();
     WTF_MAKE_NONCOPYABLE(PaintLayerClipper);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerStackingNode.h b/third_party/WebKit/Source/core/paint/PaintLayerStackingNode.h
index 1c548212..b1d794f8 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerStackingNode.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerStackingNode.h
@@ -82,9 +82,6 @@
 // To implement any z-order list iterations, use
 // PaintLayerStackingNodeIterator and
 // PaintLayerStackingNodeReverseIterator.
-//
-// This class is NOT DEPRECATED, PaintLayer is and we match its
-// naming.
 class CORE_EXPORT PaintLayerStackingNode {
     WTF_MAKE_FAST_ALLOCATED(PaintLayerStackingNode);
     WTF_MAKE_NONCOPYABLE(PaintLayerStackingNode);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerStackingNodeIterator.cpp b/third_party/WebKit/Source/core/paint/PaintLayerStackingNodeIterator.cpp
index d1fbff76..ebce038 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerStackingNodeIterator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerStackingNodeIterator.cpp
@@ -32,7 +32,7 @@
 #include "core/paint/PaintLayerStackingNodeIterator.h"
 
 // FIXME: We should build our primitive on top of
-// DeprecatedLayerStackingNode and remove this include.
+// PaintLayerStackingNode and remove this include.
 #include "core/paint/PaintLayer.h"
 #include "core/paint/PaintLayerStackingNode.h"
 
diff --git a/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp b/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
index e7b454d..d8ed29b 100644
--- a/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
@@ -50,8 +50,6 @@
 
     if (m_useDisplayItemList) {
         ASSERT(m_paintInfo.context->displayItemList());
-        if (m_paintInfo.context->displayItemList()->displayItemConstructionIsDisabled())
-            return;
         m_paintInfo.context->displayItemList()->createAndAppend<ClipDisplayItem>(layoutObject, m_clipType, LayoutRect::infiniteIntRect(), roundedRectClips);
     } else {
         ClipDisplayItem clipDisplayItem(layoutObject, m_clipType, LayoutRect::infiniteIntRect(), roundedRectClips);
diff --git a/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp b/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
index a34eb298..5738e22 100644
--- a/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
@@ -60,8 +60,7 @@
     if (m_clip.asPath(animatedLocalTransform, targetBoundingBox, clipPath)) {
         clipperState = ClipperAppliedPath;
         ASSERT(context->displayItemList());
-        if (!context->displayItemList()->displayItemConstructionIsDisabled())
-            context->displayItemList()->createAndAppend<BeginClipPathDisplayItem>(target, clipPath);
+        context->displayItemList()->createAndAppend<BeginClipPathDisplayItem>(target, clipPath);
         return true;
     }
 
diff --git a/third_party/WebKit/Source/core/paint/SVGMaskPainter.cpp b/third_party/WebKit/Source/core/paint/SVGMaskPainter.cpp
index f8494be..6beb4ca 100644
--- a/third_party/WebKit/Source/core/paint/SVGMaskPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/SVGMaskPainter.cpp
@@ -29,10 +29,7 @@
         return false;
 
     ASSERT(context->displayItemList());
-    if (context->displayItemList()->displayItemConstructionIsDisabled())
-        return true;
     context->displayItemList()->createAndAppend<BeginCompositingDisplayItem>(object, SkXfermode::kSrcOver_Mode, 1, &paintInvalidationRect);
-
     return true;
 }
 
diff --git a/third_party/WebKit/Source/core/paint/ScrollRecorder.cpp b/third_party/WebKit/Source/core/paint/ScrollRecorder.cpp
index 323b89f..ad539ee2 100644
--- a/third_party/WebKit/Source/core/paint/ScrollRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/ScrollRecorder.cpp
@@ -17,8 +17,6 @@
     , m_context(context)
 {
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<BeginScrollDisplayItem>(m_client, m_beginItemType, currentOffset);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/Transform3DRecorder.cpp b/third_party/WebKit/Source/core/paint/Transform3DRecorder.cpp
index 3ac856e4..d8de8e1 100644
--- a/third_party/WebKit/Source/core/paint/Transform3DRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/Transform3DRecorder.cpp
@@ -28,8 +28,6 @@
         return;
 
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<BeginTransform3DDisplayItem>(m_client, m_type, transform, transformOrigin);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/TransformRecorder.cpp b/third_party/WebKit/Source/core/paint/TransformRecorder.cpp
index e8f27f1..4f66ea3 100644
--- a/third_party/WebKit/Source/core/paint/TransformRecorder.cpp
+++ b/third_party/WebKit/Source/core/paint/TransformRecorder.cpp
@@ -21,8 +21,6 @@
         return;
 
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<BeginTransformDisplayItem>(m_client, transform);
 }
 
diff --git a/third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js b/third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js
new file mode 100644
index 0000000..5fdb7c9
--- /dev/null
+++ b/third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js
@@ -0,0 +1,24 @@
+(function(global, binding, v8) {
+  'use strict';
+
+  const defineProperty = global.Object.defineProperty;
+
+  class ByteLengthQueuingStrategy {
+    constructor(options) {
+      defineProperty(this, 'highWaterMark', {
+        value: options.highWaterMark,
+        enumerable: true,
+        configurable: true,
+        writable: true
+      });
+    }
+    size(chunk) { return chunk.byteLength; }
+  }
+
+  defineProperty(global, 'ByteLengthQueuingStrategy', {
+    value: ByteLengthQueuingStrategy,
+    enumerable: false,
+    configurable: true,
+    writable: true
+  });
+});
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilitySidebarView.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilitySidebarView.js
index cf767943..af35e20 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilitySidebarView.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilitySidebarView.js
@@ -132,6 +132,18 @@
 };
 
 /**
+ * @param {string} tooltip
+ * @return {!Element}
+ */
+WebInspector.AccessibilitySidebarView.createExclamationMark = function(tooltip)
+{
+    var exclamationElement = createElement("label", "dt-icon-label");
+    exclamationElement.type = "warning-icon";
+    exclamationElement.title = tooltip;
+    return exclamationElement;
+};
+
+/**
  * @constructor
  * @extends {WebInspector.SidebarPane}
  * @param {string} name
@@ -177,9 +189,7 @@
     createInfo: function(textContent, className)
     {
         var classNameOrDefault = className || "info";
-        var info = createElementWithClass("div", classNameOrDefault);
-        info.textContent = textContent;
-        this.element.appendChild(info);
+        this.element.createChild("div", classNameOrDefault).textContent = info;
         return info;
     },
 
@@ -192,6 +202,7 @@
         var treeOutline = new TreeOutlineInShadow(className);
         treeOutline.registerRequiredCSS("accessibility/accessibilityNode.css");
         treeOutline.registerRequiredCSS("components/objectValue.css");
+
         treeOutline.element.classList.add("hidden");
         this.element.appendChild(treeOutline.element);
         return treeOutline;
@@ -251,10 +262,10 @@
         function addProperty(property)
         {
             foundProperty = true;
-            treeOutline.appendChild(new WebInspector.AXNodePropertyTreeElement(property, target));
+            treeOutline.appendChild(new WebInspector.AXNodePropertyTreePropertyElement(property, target));
         }
 
-        if ("value" in axNode && axNode.value.type === "string")
+        if (axNode.value && axNode.value.type === AccessibilityAgent.AXValueType.String)
             addProperty(/** @type {!AccessibilityAgent.AXProperty} */ ({name: "value", value: axNode.value}));
 
         var propertiesArray = /** @type {!Array.<!AccessibilityAgent.AXProperty> } */ (axNode.properties);
@@ -348,7 +359,7 @@
          */
         function addProperty(property)
         {
-            treeOutline.appendChild(new WebInspector.AXNodePropertyTreeElement(property, target));
+            treeOutline.appendChild(new WebInspector.AXNodePropertyTreePropertyElement(property, target));
         }
 
         for (var propertyName of ["name", "description", "help", "value"]) {
@@ -379,139 +390,29 @@
 };
 
 /**
- * @constructor
- * @extends {TreeElement}
- * @param {!AccessibilityAgent.AXProperty} property
- * @param {!WebInspector.Target} target
- */
-WebInspector.AXNodePropertyTreeElement = function(property, target)
-{
-    this._property = property;
-    this._target = target;
-
-    // Pass an empty title, the title gets made later in onattach.
-    TreeElement.call(this, "");
-    this.toggleOnClick = true;
-    this.selectable = false;
-}
-
-WebInspector.AXNodePropertyTreeElement.prototype = {
-    /**
-     * @override
-     */
-    onattach: function()
-    {
-        this._update();
-    },
-
-
-    _update: function()
-    {
-        this._nameElement = WebInspector.AXNodePropertyTreeElement.createNameElement(this._property.name);
-
-        var value = this._property.value;
-        if (value.type === "idref") {
-            this._valueElement = WebInspector.AXNodePropertyTreeElement.createRelationshipValueElement(value, this._target);
-        } else if (value.type === "idrefList") {
-            var relatedNodes = value.relatedNodeArrayValue;
-            var numNodes = relatedNodes.length;
-            var description = "(" + numNodes + (numNodes == 1 ? " node" : " nodes") + ")";
-            value.value = description;
-            for (var i = 0; i < relatedNodes.length; i++) {
-                var backendId = relatedNodes[i].backendNodeId;
-                var deferredNode = new WebInspector.DeferredDOMNode(this._target, relatedNodes[i].backendNodeId);
-                var child = new WebInspector.AXRelatedNodeTreeElement(deferredNode);
-                this.appendChild(child);
-            }
-            this._valueElement = WebInspector.AXNodePropertyTreeElement.createValueElement(value, this.listItemElement);
-            if (relatedNodes.length <= 3)
-                this.expand();
-            else
-                this.collapse();
-        } else {
-            this._valueElement = WebInspector.AXNodePropertyTreeElement.createValueElement(value, this.listItemElement);
-        }
-
-        var separatorElement = createElementWithClass("span", "separator");
-        separatorElement.textContent = ": ";
-
-        this.listItemElement.removeChildren();
-        this.listItemElement.appendChildren(this._nameElement, separatorElement, this._valueElement);
-    },
-
-    __proto__: TreeElement.prototype
-}
-
-/**
- * @param {!TreeElement} treeNode
- * @param {?AccessibilityAgent.AXNode} axNode
- * @param {!WebInspector.Target} target
- */
-WebInspector.AXNodePropertyTreeElement.populateWithNode = function(treeNode, axNode, target)
-{
-}
-
-/**
- * @param {?string} name
+ * @param {!AccessibilityAgent.AXValueType} type
+ * @param {string} value
  * @return {!Element}
  */
-WebInspector.AXNodePropertyTreeElement.createNameElement = function(name)
+WebInspector.AccessibilitySidebarView.createSimpleValueElement = function(type, value)
 {
-    var nameElement = createElement("span");
-    var AXAttributes = WebInspector.AccessibilityStrings.AXAttributes;
-    if (name in AXAttributes) {
-        nameElement.textContent = WebInspector.UIString(AXAttributes[name].name);
-        nameElement.title = AXAttributes[name].description;
-        nameElement.classList.add("ax-readable-name");
-    } else {
-        nameElement.textContent = name;
-        nameElement.classList.add("ax-name");
-    }
-    return nameElement;
-}
-
-/**
- * @param {!AccessibilityAgent.AXValue} value
- * @param {!WebInspector.Target} target
- * @return {?Element}
- */
-WebInspector.AXNodePropertyTreeElement.createRelationshipValueElement = function(value, target)
-{
-    var deferredNode = new WebInspector.DeferredDOMNode(target, value.relatedNodeValue.backendNodeId);
-    var valueElement = createElement("span");
-
-    /**
-     * @param {?WebInspector.DOMNode} node
-     */
-    function onNodeResolved(node)
-    {
-        valueElement.appendChild(WebInspector.DOMPresentationUtils.linkifyNodeReference(node));
-    }
-    deferredNode.resolve(onNodeResolved);
-
-    return valueElement;
-}
-
-/**
- * @param {!AccessibilityAgent.AXValue} value
- * @param {!Element} parentElement
- * @return {!Element}
- */
-WebInspector.AXNodePropertyTreeElement.createValueElement = function(value, parentElement)
-{
-    var valueElement = createElementWithClass("span", "monospace");
-    var type = value.type;
+    var valueElement;
+    var AXValueType = AccessibilityAgent.AXValueType;
+    if (type === AXValueType.ValueUndefined || type === AXValueType.ComputedString)
+        valueElement = createElement("span");
+    else
+        valueElement = createElementWithClass("span", "monospace");
     var prefix;
     var valueText;
     var suffix;
-    if (type === "string") {
-        // Render \n as a nice unicode cr symbol.
+    if (type === AXValueType.String || type === AXValueType.ComputedString || type === AXValueType.IdrefList || type === AXValueType.Idref) {
         prefix = "\"";
-        valueText = value.value.replace(/\n/g, "\u21B5");
+        // Render \n as a nice unicode cr symbol.
+        valueText = value.replace(/\n/g, "\u21B5");
         suffix = "\"";
-        valueElement._originalTextContent = "\"" + value.value + "\"";
+        valueElement._originalTextContent = "\"" + value + "\"";
     } else {
-        valueText = String(value.value);
+        valueText = String(value);
     }
 
     if (type in WebInspector.AXNodePropertyTreeElement.TypeStyles)
@@ -523,7 +424,7 @@
     if (suffix)
         valueElement.createTextChild(suffix);
 
-    valueElement.title = String(value.value) || "";
+    valueElement.title = String(value) || "";
 
     return valueElement;
 }
@@ -531,11 +432,277 @@
 /**
  * @constructor
  * @extends {TreeElement}
- * @param {!WebInspector.DeferredDOMNode} deferredNode
+ * @param {!WebInspector.Target} target
  */
-WebInspector.AXRelatedNodeTreeElement = function(deferredNode)
+WebInspector.AXNodePropertyTreeElement = function(target)
 {
-    this._deferredNode = deferredNode;
+    this._target = target;
+
+    // Pass an empty title, the title gets made later in onattach.
+    TreeElement.call(this, "");
+}
+
+WebInspector.AXNodePropertyTreeElement.prototype = {
+    /**
+     * @param {string} name
+     */
+    appendNameElement: function(name)
+    {
+        var nameElement = createElement("span");
+        var AXAttributes = WebInspector.AccessibilityStrings.AXAttributes;
+        if (name in AXAttributes) {
+            nameElement.textContent = WebInspector.UIString(AXAttributes[name].name);
+            nameElement.title = AXAttributes[name].description;
+            nameElement.classList.add("ax-readable-name");
+        } else {
+            nameElement.textContent = name;
+            nameElement.classList.add("ax-name");
+            nameElement.classList.add("monospace");
+        }
+        this.listItemElement.appendChild(nameElement);
+    },
+
+    /**
+     * @param {!AccessibilityAgent.AXValue} value
+     */
+    appendValueElement: function(value)
+    {
+        var AXValueType = AccessibilityAgent.AXValueType;
+        if (value.type === AXValueType.Idref || value.type === AXValueType.Node) {
+            this.appendRelationshipValueElement(value);
+            return;
+        }
+        if (value.type === AXValueType.IdrefList || value.type === AXValueType.NodeList) {
+            this.appendRelatedNodeListValueElement(value);
+            return;
+        }
+        if (value.sources) {
+            var sources = value.sources;
+            for (var i = 0; i < sources.length; i++) {
+                var source = sources[i];
+                var child = new WebInspector.AXValueSourceTreeElement(source, this._target);
+                this.appendChild(child);
+            }
+        }
+        var valueElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement(value.type, value.value);
+        this.listItemElement.appendChild(valueElement);
+    },
+
+    /**
+     * @param {!AccessibilityAgent.AXValue} value
+     */
+    appendRelationshipValueElement: function(value)
+    {
+        var deferredNode = new WebInspector.DeferredDOMNode(this._target, value.relatedNodeValue.backendNodeId);
+        var valueElement = createElement("span");
+
+        /**
+         * @param {?WebInspector.DOMNode} node
+         */
+        function onNodeResolved(node)
+        {
+            valueElement.appendChild(WebInspector.DOMPresentationUtils.linkifyNodeReference(node));
+            if (value.relatedNodeValue.text) {
+                var textElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement(AccessibilityAgent.AXValueType.ComputedString, value.relatedNodeValue.text);
+                valueElement.appendChild(textElement);
+            }
+        }
+        deferredNode.resolve(onNodeResolved);
+
+        this.listItemElement.appendChild(valueElement);
+    },
+
+    /**
+     * @param {!AccessibilityAgent.AXValue} value
+     * @return {!Element}
+     */
+    appendRelatedNodeListValueElement: function(value)
+    {
+        var relatedNodes = value.relatedNodeArrayValue;
+        var numNodes = relatedNodes.length;
+        var valueElement;
+        if (value.type === AccessibilityAgent.AXValueType.IdrefList) {
+            var idrefs = value.value.split(/\s/);
+            for (var idref of idrefs) {
+                var matchingNode = null;
+                /**
+                 * @param {!AccessibilityAgent.AXRelatedNode} relatedNode
+                 * @return {boolean}
+                 */
+                function matchesIDRef(relatedNode)
+                {
+                    if (relatedNode.idref !== idref)
+                        return false;
+                    matchingNode = relatedNode;
+                    return true;
+                }
+                relatedNodes.some(matchesIDRef);
+                if (matchingNode) {
+                    var deferredNode = new WebInspector.DeferredDOMNode(this._target, matchingNode.backendNodeId);
+                    var child = new WebInspector.AXRelatedNodeTreeElement({ deferredNode: deferredNode }, matchingNode);
+                    this.appendChild(child);
+                } else {
+                    this.appendChild(new WebInspector.AXRelatedNodeTreeElement({ idref: idref }));
+                }
+            }
+            valueElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement(value.type, value.value);
+        } else {
+            for (var i = 0; i < numNodes; i++) {
+                var relatedNode = relatedNodes[i];
+                var deferredNode = new WebInspector.DeferredDOMNode(this._target, relatedNode.backendNodeId);
+                var child = new WebInspector.AXRelatedNodeTreeElement({ deferredNode: deferredNode }, relatedNode);
+                this.appendChild(child);
+            }
+            var numNodesString = "(" + numNodes + (numNodes === 1 ? " node" : " nodes") + ")";
+            valueElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement("pseudo", numNodesString);
+        }
+        if (relatedNodes.length <= 3)
+            this.expand();
+        else
+            this.collapse();
+        this.listItemElement.appendChild(valueElement);
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AXNodePropertyTreeElement}
+ * @param {!AccessibilityAgent.AXProperty} property
+ * @param {!WebInspector.Target} target
+ */
+WebInspector.AXNodePropertyTreePropertyElement = function(property, target)
+{
+    this._property = property;
+    this.toggleOnClick = true;
+    this.selectable = false;
+
+    WebInspector.AXNodePropertyTreeElement.call(this, target);
+}
+
+WebInspector.AXNodePropertyTreePropertyElement.prototype = {
+    /**
+     * @override
+     */
+    onattach: function()
+    {
+        this._update();
+    },
+
+    _update: function()
+    {
+        this.listItemElement.removeChildren();
+
+        this.appendNameElement(this._property.name);
+
+        this.listItemElement.createChild("span", "separator").textContent = ": ";
+
+        this.appendValueElement(this._property.value);
+    },
+
+    __proto__: WebInspector.AXNodePropertyTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AXNodePropertyTreeElement}
+ * @param {!AccessibilityAgent.AXValueSource}
+ */
+WebInspector.AXValueSourceTreeElement = function(source, target)
+{
+    this._source = source;
+    WebInspector.AXNodePropertyTreeElement.call(this, target);
+}
+
+WebInspector.AXValueSourceTreeElement.prototype = {
+    /**
+     * @override
+     */
+    onattach: function()
+    {
+        this._update();
+    },
+
+    /**
+     * @param {!AccessibilityAgent.AXValueSource} source
+     */
+    appendSourceNameElement: function(source)
+    {
+        var nameElement = createElement("span");
+        var AXValueSourceType = AccessibilityAgent.AXValueSourceType;
+        var type = source.type;
+        var name;
+        switch (type) {
+        case AXValueSourceType.Attribute:
+        case AXValueSourceType.Placeholder:
+        case AXValueSourceType.RelatedElement:
+            if (source.nativeSource) {
+                var AXNativeSourceTypes = WebInspector.AccessibilityStrings.AXNativeSourceTypes;
+                var nativeSource = source.nativeSource;
+                nameElement.textContent = WebInspector.UIString(AXNativeSourceTypes[nativeSource].name);
+                nameElement.title = WebInspector.UIString(AXNativeSourceTypes[nativeSource].description);
+                nameElement.classList.add("ax-readable-name");
+                break;
+            }
+            nameElement.textContent = source.attribute;
+            nameElement.classList.add("ax-name");
+            nameElement.classList.add("monospace");
+            break;
+        default:
+            var AXSourceTypes = WebInspector.AccessibilityStrings.AXSourceTypes;
+            if (type in AXSourceTypes) {
+                nameElement.textContent = WebInspector.UIString(AXSourceTypes[type].name);
+                nameElement.title = WebInspector.UIString(AXSourceTypes[type].description);
+                nameElement.classList.add("ax-readable-name");
+            } else {
+                console.warn(type, "not in AXSourceTypes");
+                nameElement.textContent = WebInspector.UIString(type);
+            }
+        }
+        this.listItemElement.appendChild(nameElement);
+    },
+
+    /** @override */
+    _update: function() {
+        this.listItemElement.removeChildren();
+
+        this.appendSourceNameElement(this._source);
+
+        if (this._source.invalid) {
+            this.listItemElement.appendChild(WebInspector.AccessibilitySidebarView.createExclamationMark());
+            this.listItemElement.classList.add("ax-value-source-invalid");
+        } else if (this._source.superseded) {
+            this.listItemElement.classList.add("ax-value-source-unused");
+        }
+
+        this.listItemElement.createChild("span", "separator").textContent = ": ";
+
+        if (this._source.value) {
+            this.appendValueElement(this._source.value);
+            if (this._source.superseded)
+                this.listItemElement.classList.add("ax-value-source-superseded");
+        } else {
+            var valueElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement(AccessibilityAgent.AXValueType.ValueUndefined, WebInspector.UIString("Not specified"));
+            this.listItemElement.appendChild(valueElement);
+            this.listItemElement.classList.add("ax-value-source-unused");
+        }
+    },
+
+    __proto__: WebInspector.AXNodePropertyTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {{deferredNode: !WebInspector.DeferredDOMNode, idref: string}} node
+ * @param {!AXRelatedNode=} value
+ */
+WebInspector.AXRelatedNodeTreeElement = function(node, value)
+{
+    this._deferredNode = node.deferredNode;
+    this._idref = node.idref;
+    this._value = value;
 
     TreeElement.call(this, "");
 };
@@ -548,8 +715,7 @@
 
     _update: function()
     {
-        var valueElement = createElement("div");
-        this.listItemElement.appendChild(valueElement);
+        var valueElement;
 
         /**
          * @param {?WebInspector.DOMNode} node
@@ -558,7 +724,21 @@
         {
             valueElement.appendChild(WebInspector.DOMPresentationUtils.linkifyNodeReference(node));
         }
-        this._deferredNode.resolve(onNodeResolved);
+        if (this._deferredNode) {
+            valueElement = createElement("span");
+            this.listItemElement.appendChild(valueElement);
+            this._deferredNode.resolve(onNodeResolved);
+        } else {
+            this.listItemElement.classList.add("invalid");
+            valueElement = WebInspector.AccessibilitySidebarView.createExclamationMark(WebInspector.UIString("No node with this ID."));
+            valueElement.createTextChild(this._idref);
+        }
+        this.listItemElement.appendChild(valueElement);
+        if (this._value && this._value.text) {
+            var textElement = WebInspector.AccessibilitySidebarView.createSimpleValueElement(AccessibilityAgent.AXValueType.ComputedString, this._value.text);
+            this.listItemElement.appendChild(document.createTextNode(" "));
+            this.listItemElement.appendChild(textElement);
+        }
     },
 
     __proto__: TreeElement.prototype
@@ -566,19 +746,24 @@
 
 /** @type {!Object<string, string>} */
 WebInspector.AXNodePropertyTreeElement.TypeStyles = {
+    attribute: "object-value-string",
     boolean: "object-value-boolean",
     booleanOrUndefined: "object-value-boolean",
-    tristate: "object-value-boolean",
-    number: "object-value-number",
+    computedString: "ax-readable-string",
+    idref: "object-value-string",
+    idrefList: "object-value-string",
     integer: "object-value-number",
-    string: "object-value-string",
+    internalRole: "ax-internal-role",
+    number: "object-value-number",
     role: "ax-role",
-    internalRole: "ax-internal-role"
+    string: "object-value-string",
+    tristate: "object-value-boolean",
+    valueUndefined: "ax-value-undefined"
 };
 
 /**
  * @constructor
- * @extends {TreeElement}
+ * @extends {WebInspector.AXNodePropertyTreeElement}
  * @param {!AccessibilityAgent.AXProperty} property
  * @param {?AccessibilityAgent.AXNode} axNode
  * @param {!WebInspector.Target} target
@@ -587,10 +772,8 @@
 {
     this._property = property;
     this._axNode = axNode;
-    this._target = target;
 
-    // Pass an empty title, the title gets made later in onattach.
-    TreeElement.call(this, "");
+    WebInspector.AXNodePropertyTreeElement.call(this, target);
     this.toggleOnClick = true;
     this.selectable = false;
 }
@@ -607,13 +790,11 @@
         this.listItemElement.appendChild(this._reasonElement);
 
         var value = this._property.value;
-        if (value.type === "idref") {
-            this._valueElement = WebInspector.AXNodePropertyTreeElement.createRelationshipValueElement(value, this._target);
-            this.listItemElement.appendChild(this._valueElement);
-        }
+        if (value.type === AccessibilityAgent.AXValueType.Idref)
+            this._valueElement = this.appendRelationshipValueElement(value, this._target);
     },
 
-    __proto__: TreeElement.prototype
+    __proto__: WebInspector.AXNodePropertyTreeElement.prototype
 };
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityStrings.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityStrings.js
index 48e5070..fb9b570 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityStrings.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityStrings.js
@@ -162,7 +162,67 @@
     },
     "help": {
         name: "Help",
-        descrption: "The computed help text for this element.",
+        description: "The computed help text for this element.",
+        group: "Default"
+    },
+    "description": {
+        name: "Description",
+        description: "The accessible description for this element.",
         group: "Default"
     }
 };
+
+WebInspector.AccessibilityStrings.AXSourceTypes = {
+    "attribute": {
+        name: "From attribute",
+        description: "Value from attribute."
+    },
+    "implicit": {
+        name: "Implicit",
+        description: "Implicit value.",
+    },
+    "style": {
+        name: "From style",
+        description: "Value from style."
+    },
+    "contents": {
+        name: "Contents",
+        description: "Value from element contents."
+    },
+    "placeholder": {
+        name: "From placeholder attribute",
+        description: "Value from placeholder attribute."
+    },
+    "relatedElement": {
+        name: "Related element",
+        description: "Value from related element."
+    }
+}
+
+WebInspector.AccessibilityStrings.AXNativeSourceTypes = {
+    "figcaption": {
+        name: "From caption",
+        description: "Value from figcaption element."
+    },
+    "label": {
+        name: "From label",
+        description: "Value from label element."
+    },
+    "labelfor": {
+        name: "From label (for)",
+        description: "Value from label element with for= attribute."
+    },
+    "labelwrapped": {
+        name: "From label (wrapped)",
+        description: "Value from label element wrapped."
+    },
+    "tablecaption": {
+        name: "From caption",
+        description: "Value from table caption."
+    },
+    "other": {
+        name: "From native HTML",
+        description: "Value from native HTML (unknown source)."
+    },
+
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css b/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
index f6511690..e249233 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
@@ -38,6 +38,10 @@
     flex-shrink: 0;
 }
 
+.ax-readable-string {
+    font-style: italic;
+}
+
 span.ax-role {
     font-weight: bold;
 }
@@ -57,3 +61,31 @@
 .tree-outline li {
     padding-top: 5px;
 }
+
+.tree-outline li.invalid {
+    position: relative;
+    left: -2px;
+    text-decoration: line-through;
+}
+
+.tree-outline label[is=dt-icon-label] {
+    position: relative;
+    left: -11px;
+}
+
+span.ax-value-undefined {
+    font-style: italic;
+}
+
+.ax-value-source-unused {
+    opacity: 0.5;
+}
+
+.ax-value-source-superseded,
+.ax-value-source-invalid {
+    text-decoration: line-through;
+}
+
+.tree-outline label[is=dt-icon-label] + .ax-name {
+    margin-left: -11px;
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
index ea25a42..e9beaa2b 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
@@ -298,6 +298,8 @@
 
     wasShown: function()
     {
+        WebInspector.context.setFlavor(WebInspector.ElementsPanel, this);
+
         for (var i = 0; i < this._treeOutlines.length; ++i) {
             var treeOutline = this._treeOutlines[i];
             // Attach heavy component lazily
@@ -323,6 +325,8 @@
 
     willHide: function()
     {
+        WebInspector.context.setFlavor(WebInspector.ElementsPanel, null);
+
         WebInspector.DOMModel.hideDOMNodeHighlight();
         for (var i = 0; i < this._treeOutlines.length; ++i) {
             var treeOutline = this._treeOutlines[i];
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js b/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js
index 67ed818f..12b5e94a 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js
@@ -110,6 +110,7 @@
     this._palettePanelShowing = false;
     this._paletteContainer = this.contentElement.createChild("div", "spectrum-palette");
     this._paletteContainer.addEventListener("contextmenu", this._showPaletteColorContextMenu.bind(this, -1));
+    this._shadesContainer = this.contentElement.createChild("div", "palette-color-shades hidden");
     WebInspector.installDragHandle(this._paletteContainer, this._paletteDragStart.bind(this), this._paletteDrag.bind(this), this._paletteDragEnd.bind(this), "default");
     var paletteSwitcher = this.contentElement.createChild("div", "spectrum-palette-switcher spectrum-switcher");
     appendSwitcherIcon(paletteSwitcher);
@@ -259,6 +260,15 @@
                 colorElement.__mutable = true;
                 colorElement.__color = palette.colors[i];
                 colorElement.addEventListener("contextmenu", this._showPaletteColorContextMenu.bind(this, i));
+            } else if (palette === WebInspector.Spectrum.MaterialPalette) {
+                colorElement.classList.add("has-material-shades");
+                var shadow = colorElement.createChild("div", "spectrum-palette-color spectrum-palette-color-shadow");
+                shadow.style.background = palette.colors[i];
+                shadow = colorElement.createChild("div", "spectrum-palette-color spectrum-palette-color-shadow");
+                shadow.style.background = palette.colors[i];
+                var controller = new WebInspector.LongClickController(colorElement);
+                controller.enable();
+                controller.addEventListener(WebInspector.LongClickController.Events.LongClick, this._showLightnessShades.bind(this, colorElement, palette.colors[i]));
             }
             this._paletteContainer.appendChild(colorElement);
         }
@@ -279,6 +289,48 @@
     },
 
     /**
+     * @param {!Element} colorElement
+     * @param {string} colorText
+     * @param {!WebInspector.Event} event
+     */
+    _showLightnessShades: function(colorElement, colorText, event)
+    {
+        /**
+         * @param {!Element} element
+         * @this {!WebInspector.Spectrum}
+         */
+        function closeLightnessShades(element)
+        {
+            this._shadesContainer.classList.add("hidden");
+            element.classList.remove("spectrum-shades-shown");
+            this._shadesContainer.ownerDocument.removeEventListener("mousedown", this._shadesCloseHandler, true);
+            delete this._shadesCloseHandler;
+        }
+
+        if (this._shadesCloseHandler)
+            this._shadesCloseHandler();
+
+        this._shadesContainer.classList.remove("hidden");
+        this._shadesContainer.removeChildren();
+        this._shadesContainer.animate([{ transform: "scaleY(0)", opacity: "0" }, { transform: "scaleY(1)", opacity: "1" }], { duration: 200, easing: "cubic-bezier(0.4, 0, 0.2, 1)" });
+        var anchorBox = colorElement.boxInWindow();
+        this._shadesContainer.style.top = colorElement.offsetTop + "px";
+        this._shadesContainer.style.left = colorElement.offsetLeft + "px";
+        colorElement.classList.add("spectrum-shades-shown");
+
+        var shades = WebInspector.Spectrum.MaterialPaletteShades[colorText];
+        for (var i = shades.length - 1; i >= 0; i--) {
+            var shadeElement = this._createPaletteColor(shades[i], i * 200 / shades.length + 100);
+            shadeElement.addEventListener("mousedown", this._paletteColorSelected.bind(this, shades[i], false));
+            this._shadesContainer.appendChild(shadeElement);
+        }
+
+        WebInspector.setCurrentFocusElement(this._shadesContainer);
+        this._shadesCloseHandler = closeLightnessShades.bind(this, colorElement);
+        this._shadesContainer.ownerDocument.addEventListener("mousedown", this._shadesCloseHandler, true);
+    },
+
+    /**
      * @param {!Event} e
      * @return {number}
      */
@@ -949,24 +1001,26 @@
     }
 }
 
-WebInspector.Spectrum.MaterialPalette = { title: "Material", mutable: false, matchUserFormat: true, colors: [
-    "#F44336",
-    "#E91E63",
-    "#9C27B0",
-    "#673AB7",
-    "#3F51B5",
-    "#2196F3",
-    "#03A9F4",
-    "#00BCD4",
-    "#009688",
-    "#4CAF50",
-    "#8BC34A",
-    "#CDDC39",
-    "#FFEB3B",
-    "#FFC107",
-    "#FF9800",
-    "#FF5722",
-    "#795548",
-    "#9E9E9E",
-    "#607D8B"
-]};
\ No newline at end of file
+WebInspector.Spectrum.MaterialPaletteShades = {
+    "#F44336": ["#FFEBEE", "#FFCDD2", "#EF9A9A", "#E57373", "#EF5350", "#F44336", "#E53935", "#D32F2F", "#C62828", "#B71C1C"],
+    "#E91E63": ["#FCE4EC", "#F8BBD0", "#F48FB1", "#F06292", "#EC407A", "#E91E63", "#D81B60", "#C2185B", "#AD1457", "#880E4F"],
+    "#9C27B0": ["#F3E5F5", "#E1BEE7", "#CE93D8", "#BA68C8", "#AB47BC", "#9C27B0", "#8E24AA", "#7B1FA2", "#6A1B9A", "#4A148C"],
+    "#673AB7": ["#EDE7F6", "#D1C4E9", "#B39DDB", "#9575CD", "#7E57C2", "#673AB7", "#5E35B1", "#512DA8", "#4527A0", "#311B92"],
+    "#3F51B5": ["#E8EAF6", "#C5CAE9", "#9FA8DA", "#7986CB", "#5C6BC0", "#3F51B5", "#3949AB", "#303F9F", "#283593", "#1A237E"],
+    "#2196F3": ["#E3F2FD", "#BBDEFB", "#90CAF9", "#64B5F6", "#42A5F5", "#2196F3", "#1E88E5", "#1976D2", "#1565C0", "#0D47A1"],
+    "#03A9F4": ["#E1F5FE", "#B3E5FC", "#81D4FA", "#4FC3F7", "#29B6F6", "#03A9F4", "#039BE5", "#0288D1", "#0277BD", "#01579B"],
+    "#00BCD4": ["#E0F7FA", "#B2EBF2", "#80DEEA", "#4DD0E1", "#26C6DA", "#00BCD4", "#00ACC1", "#0097A7", "#00838F", "#006064"],
+    "#009688": ["#E0F2F1", "#B2DFDB", "#80CBC4", "#4DB6AC", "#26A69A", "#009688", "#00897B", "#00796B", "#00695C", "#004D40"],
+    "#4CAF50": ["#E8F5E9", "#C8E6C9", "#A5D6A7", "#81C784", "#66BB6A", "#4CAF50", "#43A047", "#388E3C", "#2E7D32", "#1B5E20"],
+    "#8BC34A": ["#F1F8E9", "#DCEDC8", "#C5E1A5", "#AED581", "#9CCC65", "#8BC34A", "#7CB342", "#689F38", "#558B2F", "#33691E"],
+    "#CDDC39": ["#F9FBE7", "#F0F4C3", "#E6EE9C", "#DCE775", "#D4E157", "#CDDC39", "#C0CA33", "#AFB42B", "#9E9D24", "#827717"],
+    "#FFEB3B": ["#FFFDE7", "#FFF9C4", "#FFF59D", "#FFF176", "#FFEE58", "#FFEB3B", "#FDD835", "#FBC02D", "#F9A825", "#F57F17"],
+    "#FFC107": ["#FFF8E1", "#FFECB3", "#FFE082", "#FFD54F", "#FFCA28", "#FFC107", "#FFB300", "#FFA000", "#FF8F00", "#FF6F00"],
+    "#FF9800": ["#FFF3E0", "#FFE0B2", "#FFCC80", "#FFB74D", "#FFA726", "#FF9800", "#FB8C00", "#F57C00", "#EF6C00", "#E65100"],
+    "#FF5722": ["#FBE9E7", "#FFCCBC", "#FFAB91", "#FF8A65", "#FF7043", "#FF5722", "#F4511E", "#E64A19", "#D84315", "#BF360C"],
+    "#795548": ["#EFEBE9", "#D7CCC8", "#BCAAA4", "#A1887F", "#8D6E63", "#795548", "#6D4C41", "#5D4037", "#4E342E", "#3E2723"],
+    "#9E9E9E": ["#FAFAFA", "#F5F5F5", "#EEEEEE", "#E0E0E0", "#BDBDBD", "#9E9E9E", "#757575", "#616161", "#424242", "#212121"],
+    "#607D8B": ["#ECEFF1", "#CFD8DC", "#B0BEC5", "#90A4AE", "#78909C", "#607D8B", "#546E7A", "#455A64", "#37474F", "#263238"]
+};
+
+WebInspector.Spectrum.MaterialPalette = { title: "Material", mutable: false, matchUserFormat: true, colors: Object.keys(WebInspector.Spectrum.MaterialPaletteShades) };
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/module.json b/third_party/WebKit/Source/devtools/front_end/elements/module.json
index 8a2fbd1..04fa293 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/elements/module.json
@@ -69,6 +69,7 @@
         {
             "type": "@WebInspector.ActionDelegate",
             "actionId": "elements.hide-element",
+            "contextTypes": ["WebInspector.ElementsPanel"],
             "className": "WebInspector.ElementsActionDelegate",
             "bindings": [
                 {
@@ -79,6 +80,7 @@
         {
             "type": "@WebInspector.ActionDelegate",
             "actionId": "elements.edit-as-html",
+            "contextTypes": ["WebInspector.ElementsPanel"],
             "className": "WebInspector.ElementsActionDelegate",
             "bindings": [
                 {
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css b/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css
index 0f16172..7fde4b3 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css
+++ b/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css
@@ -203,6 +203,48 @@
     border-radius: 2px;
     margin: 6px;
     cursor: pointer;
+    position: relative;
+}
+
+.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow {
+    opacity: 0.2;
+}
+
+.spectrum-palette-color:hover:not(.spectrum-shades-shown) > .spectrum-palette-color-shadow:first-child {
+    opacity: 0.6;
+    top: -3px;
+    left: 1px;
+}
+
+.spectrum-palette-color-shadow {
+    position: absolute;
+    opacity: 0;
+    margin: 0;
+    top: -5px;
+    left: 3px;
+}
+
+.palette-color-shades {
+    position: absolute;
+    background-color: white;
+    height: 237px;
+    width: 28px;
+    box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.4);
+    z-index: 14;
+    border-radius: 2px;
+    transform-origin: 0px 237px;
+    margin-top: 18px;
+    margin-left: -8px;
+}
+
+.spectrum-palette > .spectrum-palette-color.spectrum-shades-shown {
+    z-index: 15;
+}
+
+.palette-color-shades > .spectrum-palette-color {
+    margin: 8px 0 0 0;
+    margin-left: 8px;
+    width: 12px;
 }
 
 .spectrum-palette > .spectrum-palette-color {
@@ -216,7 +258,8 @@
     border-color: transparent;
 }
 
-.spectrum-palette > .spectrum-palette-color:not(.empty-color):hover {
+.spectrum-palette > .spectrum-palette-color:not(.empty-color):not(.has-material-shades):hover,
+.palette-color-shades > .spectrum-palette-color:not(.empty-color):hover {
     transform: scale(1.15);
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/main/FrontendWebSocketAPI.js b/third_party/WebKit/Source/devtools/front_end/main/FrontendWebSocketAPI.js
index bbe8c1c..6c7e5c00 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/FrontendWebSocketAPI.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/FrontendWebSocketAPI.js
@@ -11,6 +11,8 @@
     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchFrontendAPIMessage, this._onFrontendAPIMessage, this);
     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.FrontendAPIAttached, this._onAttach, this);
     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.FrontendAPIDetached, this._onDetach, this);
+    /** @type {!Set<string>} */
+    this._suggestedFolders = new Set();
 }
 
 WebInspector.FrontendWebSocketAPI.prototype = {
@@ -76,11 +78,22 @@
                 if (saved)
                     uiSourceCode.checkContentUpdated();
             }
-            this._issueResponse(id);
+            break;
+        case "Frontend.addFileSystem":
+            for (var path of params["paths"]) {
+                var fileSystem = WebInspector.isolatedFileSystemManager.fileSystem(path);
+                if (fileSystem)
+                    continue;
+                if (this._suggestedFolders.has(path))
+                    continue;
+                this._suggestedFolders.add(path);
+                WebInspector.isolatedFileSystemManager.addFileSystem(path);
+            }
             break;
         default:
             WebInspector.console.log("Unhandled API message: " + method);
         }
+        this._issueResponse(id);
         this._dispatchingFrontendMessage = false;
     },
 
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/SettingsScreen.js b/third_party/WebKit/Source/devtools/front_end/settings/SettingsScreen.js
index e6b9467..6f3a47a 100644
--- a/third_party/WebKit/Source/devtools/front_end/settings/SettingsScreen.js
+++ b/third_party/WebKit/Source/devtools/front_end/settings/SettingsScreen.js
@@ -470,7 +470,7 @@
 
     _addFileSystemClicked: function()
     {
-        WebInspector.isolatedFileSystemManager.addFileSystem();
+        WebInspector.isolatedFileSystemManager.addFileSystem("");
     },
 
     _fileSystemAdded: function(event)
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js b/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
index 67ab24d..cf00c39 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
@@ -88,7 +88,7 @@
 {
     function addFolder()
     {
-        WebInspector.isolatedFileSystemManager.addFileSystem();
+        WebInspector.isolatedFileSystemManager.addFileSystem("");
     }
 
     var addFolderLabel = WebInspector.UIString.capitalize("Add ^folder to ^workspace");
diff --git a/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystemManager.js b/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystemManager.js
index 78030c6e..f26ac30 100644
--- a/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystemManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystemManager.js
@@ -65,9 +65,12 @@
         InspectorFrontendHost.requestFileSystems();
     },
 
-    addFileSystem: function()
+    /**
+     * @param {string} fileSystemPath
+     */
+    addFileSystem: function(fileSystemPath)
     {
-        InspectorFrontendHost.addFileSystem("");
+        InspectorFrontendHost.addFileSystem(fileSystemPath);
     },
 
     /**
diff --git a/third_party/WebKit/Source/devtools/protocol.json b/third_party/WebKit/Source/devtools/protocol.json
index f106c857..0c32c620 100644
--- a/third_party/WebKit/Source/devtools/protocol.json
+++ b/third_party/WebKit/Source/devtools/protocol.json
@@ -5186,23 +5186,29 @@
             {
                 "id": "AXValueType",
                 "type": "string",
-                "enum": [ "boolean", "tristate", "booleanOrUndefined", "idref", "idrefList", "integer", "number", "string", "token", "tokenList", "domRelation", "role", "internalRole" ],
+                "enum": [ "boolean", "tristate", "booleanOrUndefined", "idref", "idrefList", "integer", "node", "nodeList", "number", "string", "computedString", "token", "tokenList", "domRelation", "role", "internalRole", "valueUndefined" ],
                 "description": "Enum of possible property types."
             },
             {
-                "id": "AXPropertySourceType",
+                "id": "AXValueSourceType",
                 "type": "string",
-                "enum": [ "attribute", "implicit", "style" ],
+                "enum": [ "attribute", "implicit", "style", "contents", "placeholder", "relatedElement" ],
                 "description": "Enum of possible property sources."
             },
+            { "id": "AXValueNativeSourceType",
+              "type": "string",
+              "enum": [ "figcaption", "label", "labelfor", "labelwrapped", "tablecaption", "other" ],
+              "description": "Enum of possible native property sources (as a subtype of a particular AXValueSourceType)."
+            },
             {
-                "id": "AXPropertySource",
+                "id": "AXValueSource",
                 "type": "object",
                 "properties": [
-                    { "name": "name", "type": "string", "description": "The name/label of this source." },
-                    { "name": "sourceType", "$ref": "AXPropertySourceType", "description": "What type of source this is." },
-                    { "name": "value", "type": "any", "description": "The value of this property source." },
-                    { "name": "type", "$ref": "AXValueType", "description": "What type the value should be interpreted as." },
+                    { "name": "type", "$ref": "AXValueSourceType", "description": "What type of source this is." },
+                    { "name": "value", "$ref": "AXValue", "description": "The value of this property source.", "optional": true },
+                    { "name": "attribute", "type": "string", "description": "The attribute, if any.", "optional": true },
+                    { "name": "superseded", "type": "boolean", "description": "Whether this source is superseded by a higher priority source.", "optional": true },
+                    { "name": "nativeSource", "$ref": "AXValueNativeSourceType", "description": "The native markup source for this value, e.g. a <label> element.", "optional": true },
                     { "name": "invalid", "type": "boolean", "description": "Whether the value for this property is invalid.", "optional": true },
                     { "name": "invalidReason", "type": "string", "description": "Reason for the value being invalid, if it is.", "optional": true }
                 ],
@@ -5212,8 +5218,9 @@
                 "id": "AXRelatedNode",
                 "type": "object",
                 "properties": [
+                    { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "The BackendNodeId of the related node." },
                     { "name": "idref", "type": "string", "description": "The IDRef value provided, if any.", "optional": true },
-                    { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "The BackendNodeId of the related node." }
+                    { "name": "text", "type": "string", "description": "The text alternative of this node in the current context.", "optional": true }
                 ]
             },
             {
@@ -5233,7 +5240,7 @@
                     { "name": "value", "type": "any", "description": "The computed value of this property.", "optional": true },
                     { "name": "relatedNodeValue", "$ref": "AXRelatedNode", "description": "The related node value, if any.", "optional": true },
                     { "name": "relatedNodeArrayValue", "type": "array", "items": { "$ref": "AXRelatedNode" }, "description": "Multiple relted nodes, if applicable.", "optional": true },
-                    { "name": "sources", "type": "array", "items": { "$ref": "AXPropertySource" }, "description": "The sources which contributed to the computation of this property.", "optional": true }
+                    { "name": "sources", "type": "array", "items": { "$ref": "AXValueSource" }, "description": "The sources which contributed to the computation of this property.", "optional": true }
                 ],
                 "description": "A single computed AX property."
             },
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
index 9c0d892..2db9efc 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -578,7 +578,7 @@
     }
 }
 
-void AXNodeObject::accessibilityChildrenFromAttribute(QualifiedName attr, AccessibilityChildrenVector& children) const
+void AXNodeObject::accessibilityChildrenFromAttribute(QualifiedName attr, AXObject::AccessibilityChildrenVector& children) const
 {
     WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
     elementsFromAttribute(elements, attr);
@@ -1843,11 +1843,11 @@
 // New AX name calculation.
 //
 
-String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, NameSources* nameSources) const
+String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources) const
 {
-    // If nameSources is non-null, nameObjects is used in filling it in, so it must be non-null as well.
+    // If nameSources is non-null, relatedObjects is used in filling it in, so it must be non-null as well.
     if (nameSources)
-        ASSERT(nameObjects);
+        ASSERT(relatedObjects);
 
     bool alreadyVisited = visited.contains(this);
     bool foundTextAlternative = false;
@@ -1878,13 +1878,13 @@
             if (nameSources)
                 nameSources->last().attributeValue = ariaLabelledby;
 
-            textAlternative = textFromAriaLabelledby(visited, nameObjects);
+            textAlternative = textFromAriaLabelledby(visited, relatedObjects);
 
             if (!textAlternative.isNull()) {
                 if (nameSources) {
                     NameSource& source = nameSources->last();
                     source.type = nameFrom;
-                    source.nameObjects = *nameObjects;
+                    source.relatedObjects = *relatedObjects;
                     source.text = textAlternative;
                     foundTextAlternative = true;
                 } else {
@@ -1917,7 +1917,7 @@
     }
 
     // Step 2D from: http://www.w3.org/TR/accname-aam-1.1
-    textAlternative = nativeTextAlternative(visited, nameFrom, nameObjects, nameSources, &foundTextAlternative);
+    textAlternative = nativeTextAlternative(visited, nameFrom, relatedObjects, nameSources, &foundTextAlternative);
     if (!textAlternative.isNull() && !nameSources)
         return textAlternative;
 
@@ -1982,8 +1982,8 @@
             if (!(*nameSources)[i].text.isNull() && !(*nameSources)[i].superseded) {
                 NameSource& nameSource = (*nameSources)[i];
                 nameFrom = nameSource.type;
-                if (!nameSource.nameObjects.isEmpty())
-                    *nameObjects = nameSource.nameObjects;
+                if (!nameSource.relatedObjects.isEmpty())
+                    *relatedObjects = nameSource.relatedObjects;
                 return nameSource.text;
             }
         }
@@ -2015,19 +2015,19 @@
     return accumulatedText.toString();
 }
 
-String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVector* nameObjects) const
+String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXRelatedObjectVector* relatedObjects) const
 {
     StringBuilder accumulatedText;
     bool foundValidElement = false;
-    AXObjectVector localNameObjects;
+    AXRelatedObjectVector localRelatedObjects;
 
     for (const auto& element : elements) {
         AXObject* axElement = axObjectCache().getOrCreate(element);
         if (axElement) {
             foundValidElement = true;
-            localNameObjects.append(axElement);
 
             String result = recursiveTextAlternative(*axElement, inAriaLabelledbyTraversal, visited);
+            localRelatedObjects.append(new NameSourceRelatedObject(axElement, result));
             if (!result.isEmpty()) {
                 if (!accumulatedText.isEmpty())
                     accumulatedText.append(" ");
@@ -2037,16 +2037,16 @@
     }
     if (!foundValidElement)
         return String();
-    if (nameObjects)
-        *nameObjects = localNameObjects;
+    if (relatedObjects)
+        *relatedObjects = localRelatedObjects;
     return accumulatedText.toString();
 }
 
-String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector* nameObjects) const
+String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXRelatedObjectVector* relatedObjects) const
 {
     WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
     ariaLabelledbyElements(elements);
-    return textFromElements(true, visited, elements, nameObjects);
+    return textFromElements(true, visited, elements, relatedObjects);
 }
 
 LayoutRect AXNodeObject::elementRect() const
@@ -2551,17 +2551,17 @@
 }
 
 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-name-and-description-calculation
-String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, NameSources* nameSources, bool* foundTextAlternative) const
+String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources, bool* foundTextAlternative) const
 {
     if (!node())
         return String();
 
-    // If nameSources is non-null, nameObjects is used in filling it in, so it must be non-null as well.
+    // If nameSources is non-null, relatedObjects is used in filling it in, so it must be non-null as well.
     if (nameSources)
-        ASSERT(nameObjects);
+        ASSERT(relatedObjects);
 
     String textAlternative;
-    AXObjectVector localNameObjects;
+    AXRelatedObjectVector localRelatedObjects;
 
     const HTMLInputElement* inputElement = nullptr;
     if (isHTMLInputElement(node()))
@@ -2584,16 +2584,17 @@
             AXObject* labelAXObject = axObjectCache().getOrCreate(label);
             // Avoid an infinite loop for label wrapped
             if (labelAXObject && !visited.contains(labelAXObject)) {
-                if (nameObjects) {
-                    localNameObjects.append(labelAXObject);
-                    *nameObjects = localNameObjects;
-                    localNameObjects.clear();
-                }
                 textAlternative = recursiveTextAlternative(*labelAXObject, false, visited);
 
+                if (relatedObjects) {
+                    localRelatedObjects.append(new NameSourceRelatedObject(labelAXObject, textAlternative));
+                    *relatedObjects = localRelatedObjects;
+                    localRelatedObjects.clear();
+                }
+
                 if (nameSources) {
                     NameSource& source = nameSources->last();
-                    source.nameObjects = *nameObjects;
+                    source.relatedObjects = *relatedObjects;
                     source.text = textAlternative;
                     if (label->getAttribute(forAttr).isNull())
                         source.nativeSource = AXTextFromNativeHTMLLabelWrapped;
@@ -2726,18 +2727,19 @@
         if (figcaption) {
             AXObject* figcaptionAXObject = axObjectCache().getOrCreate(figcaption);
             if (figcaptionAXObject) {
-                if (nameObjects) {
-                    localNameObjects.append(figcaptionAXObject);
-                    *nameObjects = localNameObjects;
-                    localNameObjects.clear();
-                }
-
                 textAlternative = recursiveTextAlternative(*figcaptionAXObject, false, visited);
 
+                if (relatedObjects) {
+                    localRelatedObjects.append(new NameSourceRelatedObject(figcaptionAXObject, textAlternative));
+                    *relatedObjects = localRelatedObjects;
+                    localRelatedObjects.clear();
+                }
+
                 if (nameSources) {
                     NameSource& source = nameSources->last();
-                    source.nameObjects = *nameObjects;
+                    source.relatedObjects = *relatedObjects;
                     source.text = textAlternative;
+                    *foundTextAlternative = true;
                 } else {
                     return textAlternative;
                 }
@@ -2784,17 +2786,18 @@
         if (caption) {
             AXObject* captionAXObject = axObjectCache().getOrCreate(caption);
             if (captionAXObject) {
-                if (nameObjects) {
-                    localNameObjects.append(captionAXObject);
-                    *nameObjects = localNameObjects;
-                    localNameObjects.clear();
+                textAlternative = recursiveTextAlternative(*captionAXObject, false, visited);
+                if (relatedObjects) {
+                    localRelatedObjects.append(new NameSourceRelatedObject(captionAXObject, textAlternative));
+                    *relatedObjects = localRelatedObjects;
+                    localRelatedObjects.clear();
                 }
 
-                textAlternative = recursiveTextAlternative(*captionAXObject, false, visited);
                 if (nameSources) {
                     NameSource& source = nameSources->last();
-                    source.nameObjects = *nameObjects;
+                    source.relatedObjects = *relatedObjects;
                     source.text = textAlternative;
+                    *foundTextAlternative = true;
                 } else {
                     return textAlternative;
                 }
@@ -2804,7 +2807,7 @@
         // summary
         nameFrom = AXNameFromAttribute;
         if (nameSources) {
-            nameSources->append(NameSource(*foundTextAlternative));
+            nameSources->append(NameSource(*foundTextAlternative, summaryAttr));
             nameSources->last().type = nameFrom;
         }
         const AtomicString& summary = getAttribute(summaryAttr);
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
index 79c0765c7..b4614f3 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
@@ -70,7 +70,7 @@
     AccessibilityRole determineAriaRoleAttribute() const;
     void tokenVectorFromAttribute(Vector<String>&, const QualifiedName&) const;
     void elementsFromAttribute(WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, const QualifiedName&) const;
-    void accessibilityChildrenFromAttribute(QualifiedName attr, AccessibilityChildrenVector&) const;
+    void accessibilityChildrenFromAttribute(QualifiedName attr, AXObject::AccessibilityChildrenVector&) const;
 
     bool hasContentEditableAttributeSet() const;
     bool isTextControl() const override;
@@ -171,7 +171,7 @@
     String computedName() const override;
 
     // New AX name calculation.
-    String textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom&, AXObjectVector* nameObjects, NameSources*) const override;
+    String textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom&, AXRelatedObjectVector*, NameSources*) const override;
 
     // Location and click point in frame-relative coordinates.
     LayoutRect elementRect() const override;
@@ -220,9 +220,9 @@
     void deprecatedAriaLabelledbyText(HeapVector<Member<AccessibilityText>>&) const;
 
     String textFromDescendants(AXObjectSet& visited) const;
-    String textFromElements(bool inAriaLabelledByTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVector* nameObjects) const;
-    String textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector* nameObjects) const;
-    String nativeTextAlternative(AXObjectSet& visited, AXNameFrom&, AXObjectVector* nameObjects, NameSources*, bool* foundTextAlternative) const;
+    String textFromElements(bool inAriaLabelledByTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXRelatedObjectVector* relatedObjects) const;
+    String textFromAriaLabelledby(AXObjectSet& visited, AXRelatedObjectVector* relatedObjects) const;
+    String nativeTextAlternative(AXObjectSet& visited, AXNameFrom&, AXRelatedObjectVector*, NameSources*, bool* foundTextAlternative) const;
     float stepValueForRange() const;
     AXObject* findChildWithTagName(const HTMLQualifiedName&) const;
     bool isDescendantOfElementType(const HTMLQualifiedName& tagName) const;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index 98e2ce4..c6b9701 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -673,18 +673,23 @@
     return m_cachedIsPresentationalChild;
 }
 
-String AXObject::name(AXNameFrom& nameFrom, AXObjectVector* nameObjects) const
+String AXObject::name(AXNameFrom& nameFrom, AXObject::AccessibilityChildrenVector* nameObjects) const
 {
     HeapHashSet<Member<const AXObject>> visited;
-    return textAlternative(false, false, visited, nameFrom, nameObjects, nullptr);
+    AXRelatedObjectVector relatedObjects;
+    String text = textAlternative(false, false, visited, nameFrom, &relatedObjects, nullptr);
+    nameObjects->clear();
+    for (size_t i = 0; i < relatedObjects.size(); i++)
+        nameObjects->append(relatedObjects[i]->object);
+    return text;
 }
 
 String AXObject::name(NameSources* nameSources) const
 {
     AXObjectSet visited;
     AXNameFrom tmpNameFrom;
-    AXObjectVector tmpNameObjects;
-    return textAlternative(false, false, visited, tmpNameFrom, &tmpNameObjects, nameSources);
+    AXRelatedObjectVector tmpRelatedObjects;
+    return textAlternative(false, false, visited, tmpNameFrom, &tmpRelatedObjects, nameSources);
 }
 
 String AXObject::recursiveTextAlternative(const AXObject& axObj, bool inAriaLabelledByTraversal, AXObjectSet& visited)
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h
index 28138ed..edcf525 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -385,7 +385,24 @@
     }
 };
 
-typedef HeapVector<Member<AXObject>> AXObjectVector;
+class NameSourceRelatedObject : public GarbageCollectedFinalized<NameSourceRelatedObject> {
+public:
+    WeakMember<AXObject> object;
+    String text;
+
+    NameSourceRelatedObject(AXObject* object, String text)
+        : object(object)
+        , text(text)
+    {
+    }
+
+    DEFINE_INLINE_TRACE()
+    {
+        visitor->trace(object);
+    }
+};
+
+typedef HeapVector<Member<NameSourceRelatedObject>> AXRelatedObjectVector;
 class NameSource {
     ALLOW_ONLY_INLINE_ALLOCATION();
 public:
@@ -396,9 +413,9 @@
     const QualifiedName& attribute;
     AtomicString attributeValue;
     AXTextFromNativeHTML nativeSource = AXTextFromNativeHTMLUninitialized;
-    AXObjectVector nameObjects;
+    AXRelatedObjectVector relatedObjects;
 
-    explicit NameSource(bool superseded, const QualifiedName& attr)
+    NameSource(bool superseded, const QualifiedName& attr)
         : superseded(superseded)
         , attribute(attr)
     {
@@ -412,7 +429,7 @@
 
     DEFINE_INLINE_TRACE()
     {
-        visitor->trace(nameObjects);
+        visitor->trace(relatedObjects);
     }
 };
 
@@ -634,7 +651,7 @@
 
     // Retrieves the accessible name of the object, an enum indicating where the name
     // was derived from, and a list of objects that were used to derive the name, if any.
-    virtual String name(AXNameFrom&, AXObjectVector* nameObjects) const;
+    virtual String name(AXNameFrom&, AccessibilityChildrenVector* nameObjects) const;
 
     typedef HeapVector<NameSource> NameSources;
     // Retrieves the accessible name of the object and a list of all potential sources
@@ -645,7 +662,7 @@
     // accessible description of the object, which is secondary to |name|, an enum indicating
     // where the description was derived from, and a list of objects that were used to
     // derive the description, if any.
-    virtual String description(AXNameFrom, AXDescriptionFrom&, AXObjectVector* descriptionObjects) { return String(); }
+    virtual String description(AXNameFrom, AXDescriptionFrom&, AccessibilityChildrenVector* descriptionObjects) { return String(); }
 
     // Takes the result of nameFrom and descriptionFrom from calling |name| and |description|,
     // above, and retrieves the placeholder of the object, if present and if it wasn't already
@@ -654,7 +671,7 @@
 
     // Internal function used by name and description, above.
     typedef HeapHashSet<Member<const AXObject>> AXObjectSet;
-    virtual String textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, NameSources* nameSources) const { return String(); }
+    virtual String textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources) const { return String(); }
 
     // Returns result of Accessible Name Calculation algorithm.
     // This is a simpler high-level interface to |name| used by Inspector.
diff --git a/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp b/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
index e33ccf2..dcc789c 100644
--- a/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
@@ -6,6 +6,7 @@
 
 #include "modules/accessibility/InspectorAccessibilityAgent.h"
 
+#include "core/HTMLNames.h"
 #include "core/dom/AXObjectCache.h"
 #include "core/dom/DOMNodeIds.h"
 #include "core/dom/Element.h"
@@ -25,6 +26,7 @@
 using TypeBuilder::Accessibility::AXNode;
 using TypeBuilder::Accessibility::AXNodeId;
 using TypeBuilder::Accessibility::AXProperty;
+using TypeBuilder::Accessibility::AXValueSource;
 using TypeBuilder::Accessibility::AXValueType;
 using TypeBuilder::Accessibility::AXRelatedNode;
 using TypeBuilder::Accessibility::AXRelationshipAttributes;
@@ -32,6 +34,8 @@
 using TypeBuilder::Accessibility::AXWidgetAttributes;
 using TypeBuilder::Accessibility::AXWidgetStates;
 
+using namespace HTMLNames;
+
 namespace {
 
 void fillCoreProperties(AXObject* axObject, PassRefPtr<AXNode> nodeObject)
@@ -39,7 +43,7 @@
     // core properties
     String description = axObject->deprecatedAccessibilityDescription();
     if (!description.isEmpty())
-        nodeObject->setDescription(createValue(description));
+        nodeObject->setDescription(createValue(description, AXValueType::ComputedString));
 
     if (axObject->supportsRangeValue()) {
         nodeObject->setValue(createValue(axObject->valueForRange()));
@@ -51,7 +55,7 @@
 
     String help = axObject->deprecatedHelpText();
     if (!help.isEmpty())
-        nodeObject->setHelp(createValue(help));
+        nodeObject->setHelp(createValue(help, AXValueType::ComputedString));
 }
 
 void fillLiveRegionProperties(AXObject* axObject, PassRefPtr<TypeBuilder::Array<AXProperty>> properties)
@@ -243,6 +247,14 @@
     }
 }
 
+PassRefPtr<AXProperty> createRelatedNodeListProperty(AXRelationshipAttributes::Enum key, AXObject::AccessibilityChildrenVector& nodes, const QualifiedName& attr, AXObject* axObject)
+{
+    RefPtr<AXValue> nodeListValue = createRelatedNodeListValue(nodes);
+    const AtomicString& attrValue = axObject->getAttribute(attr);
+    nodeListValue->setValue(JSONString::create(attrValue));
+    return createProperty(key, nodeListValue);
+}
+
 void fillRelationships(AXObject* axObject, PassRefPtr<TypeBuilder::Array<AXProperty>> properties)
 {
     if (AXObject* activeDescendant = axObject->activeDescendant()) {
@@ -252,27 +264,27 @@
     AXObject::AccessibilityChildrenVector results;
     axObject->ariaFlowToElements(results);
     if (!results.isEmpty())
-        properties->addItem(createProperty(AXRelationshipAttributes::Flowto, createRelatedNodeListValue(results)));
+        properties->addItem(createRelatedNodeListProperty(AXRelationshipAttributes::Flowto, results, aria_flowtoAttr, axObject));
     results.clear();
 
     axObject->ariaControlsElements(results);
     if (!results.isEmpty())
-        properties->addItem(createProperty(AXRelationshipAttributes::Controls, createRelatedNodeListValue(results)));
+        properties->addItem(createRelatedNodeListProperty(AXRelationshipAttributes::Controls, results, aria_controlsAttr, axObject));
     results.clear();
 
     axObject->deprecatedAriaDescribedbyElements(results);
     if (!results.isEmpty())
-        properties->addItem(createProperty(AXRelationshipAttributes::Describedby, createRelatedNodeListValue(results)));
+        properties->addItem(createRelatedNodeListProperty(AXRelationshipAttributes::Describedby, results, aria_describedbyAttr, axObject));
     results.clear();
 
     axObject->deprecatedAriaLabelledbyElements(results);
     if (!results.isEmpty())
-        properties->addItem(createProperty(AXRelationshipAttributes::Labelledby, createRelatedNodeListValue(results)));
+        properties->addItem(createRelatedNodeListProperty(AXRelationshipAttributes::Labelledby, results, aria_labelledbyAttr, axObject));
     results.clear();
 
     axObject->ariaOwnsElements(results);
     if (!results.isEmpty())
-        properties->addItem(createProperty(AXRelationshipAttributes::Owns, createRelatedNodeListValue(results)));
+        properties->addItem(createRelatedNodeListProperty(AXRelationshipAttributes::Owns, results, aria_ownsAttr, axObject));
     results.clear();
 }
 
@@ -317,9 +329,19 @@
     RefPtr<AXNode> nodeObject = AXNode::create().setNodeId(String::number(axObject->axObjectID())).setIgnored(false);
     nodeObject->setRole(createRoleNameValue(role));
     nodeObject->setProperties(properties);
-    String computedName = cacheImpl->computedNameForNode(node);
-    if (!computedName.isEmpty())
-        nodeObject->setName(createValue(computedName));
+
+    AXObject::NameSources nameSources;
+    String computedName = axObject->name(&nameSources);
+    if (!nameSources.isEmpty()) {
+        RefPtr<AXValue> name = createValue(computedName, AXValueType::ComputedString);
+        if (!nameSources.isEmpty()) {
+            RefPtr<TypeBuilder::Array<AXValueSource>> nameSourceProperties = TypeBuilder::Array<AXValueSource>::create();
+            for (size_t i = 0; i < nameSources.size(); ++i)
+                nameSourceProperties->addItem(createValueSource(nameSources[i]));
+            name->setSources(nameSourceProperties);
+        }
+        nodeObject->setName(name);
+    }
 
     fillCoreProperties(axObject, nodeObject);
     return nodeObject;
diff --git a/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.cpp b/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.cpp
index a280294..03435da 100644
--- a/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.cpp
@@ -12,7 +12,9 @@
 
 namespace blink {
 
+using TypeBuilder::Accessibility::AXValueNativeSourceType;
 using TypeBuilder::Accessibility::AXRelatedNode;
+using TypeBuilder::Accessibility::AXValueSourceType;
 
 PassRefPtr<AXProperty> createProperty(String name, PassRefPtr<AXValue> value)
 {
@@ -90,7 +92,7 @@
 PassRefPtr<AXProperty> createProperty(IgnoredReason reason)
 {
     if (reason.relatedObject)
-        return createProperty(ignoredReasonName(reason.reason), createRelatedNodeValue(reason.relatedObject));
+        return createProperty(ignoredReasonName(reason.reason), createRelatedNodeValue(reason.relatedObject, nullptr, AXValueType::Idref));
     return createProperty(ignoredReasonName(reason.reason), createBooleanValue(true));
 }
 
@@ -122,7 +124,7 @@
     return axValue;
 }
 
-PassRefPtr<AXRelatedNode> relatedNodeForAXObject(const AXObject* axObject)
+PassRefPtr<AXRelatedNode> relatedNodeForAXObject(const AXObject* axObject, String* name = nullptr)
 {
     Node* node = axObject->node();
     if (!node)
@@ -138,27 +140,107 @@
     const AtomicString& idref = element->getIdAttribute();
     if (!idref.isEmpty())
         relatedNode->setIdref(idref);
+
+    if (name)
+        relatedNode->setText(*name);
     return relatedNode;
 }
 
-PassRefPtr<AXValue> createRelatedNodeValue(const AXObject* axObject)
+PassRefPtr<AXValue> createRelatedNodeValue(const AXObject* axObject, String* name, AXValueType::Enum valueType)
 {
-    RefPtr<AXValue> axValue = AXValue::create().setType(AXValueType::Idref);
-    RefPtr<AXRelatedNode> relatedNode = relatedNodeForAXObject(axObject);
+    RefPtr<AXValue> axValue = AXValue::create().setType(valueType);
+    RefPtr<AXRelatedNode> relatedNode = relatedNodeForAXObject(axObject, name);
     axValue->setRelatedNodeValue(relatedNode);
     return axValue;
 }
 
-PassRefPtr<AXValue> createRelatedNodeListValue(AXObject::AccessibilityChildrenVector axObjects)
+PassRefPtr<AXValue> createRelatedNodeListValue(AXRelatedObjectVector& relatedObjects, AXValueType::Enum valueType)
+{
+    RefPtr<TypeBuilder::Array<AXRelatedNode>> frontendRelatedNodes = TypeBuilder::Array<AXRelatedNode>::create();
+    for (unsigned i = 0; i < relatedObjects.size(); i++) {
+        RefPtr<AXRelatedNode> frontendRelatedNode = relatedNodeForAXObject(relatedObjects[i]->object, &(relatedObjects[i]->text));
+        if (frontendRelatedNode)
+            frontendRelatedNodes->addItem(frontendRelatedNode);
+    }
+    RefPtr<AXValue> axValue = AXValue::create().setType(valueType);
+    axValue->setRelatedNodeArrayValue(frontendRelatedNodes);
+    return axValue;
+}
+
+PassRefPtr<AXValue> createRelatedNodeListValue(AXObject::AccessibilityChildrenVector& axObjects, AXValueType::Enum valueType)
 {
     RefPtr<TypeBuilder::Array<AXRelatedNode>> relatedNodes = TypeBuilder::Array<AXRelatedNode>::create();
     for (unsigned i = 0; i < axObjects.size(); i++) {
-        if (RefPtr<AXRelatedNode> relatedNode = relatedNodeForAXObject(axObjects[i].get()))
+        RefPtr<AXRelatedNode> relatedNode = relatedNodeForAXObject(axObjects[i].get());
+        if (relatedNode)
             relatedNodes->addItem(relatedNode);
     }
-    RefPtr<AXValue> axValue = AXValue::create().setType(AXValueType::IdrefList);
+    RefPtr<AXValue> axValue = AXValue::create().setType(valueType);
     axValue->setRelatedNodeArrayValue(relatedNodes);
     return axValue;
 }
 
+AXValueSourceType::Enum valueSourceType(AXNameFrom nameFrom)
+{
+    switch (nameFrom) {
+    case AXNameFromAttribute:
+        return AXValueSourceType::Attribute;
+    case AXNameFromContents:
+        return AXValueSourceType::Contents;
+    case AXNameFromPlaceholder:
+        return AXValueSourceType::Placeholder;
+    case AXNameFromRelatedElement:
+        return AXValueSourceType::RelatedElement;
+    default:
+        return AXValueSourceType::Implicit; // TODO(aboxhall): what to do here?
+    }
+}
+
+AXValueNativeSourceType::Enum nativeSourceType(AXTextFromNativeHTML nativeSource)
+{
+    switch (nativeSource) {
+    case AXTextFromNativeHTMLFigcaption:
+        return AXValueNativeSourceType::Figcaption;
+    case AXTextFromNativeHTMLLabel:
+        return AXValueNativeSourceType::Label;
+    case AXTextFromNativeHTMLLabelFor:
+        return AXValueNativeSourceType::Labelfor;
+    case AXTextFromNativeHTMLLabelWrapped:
+        return AXValueNativeSourceType::Labelwrapped;
+    case AXTextFromNativeHTMLTableCaption:
+        return AXValueNativeSourceType::Tablecaption;
+    default:
+        return AXValueNativeSourceType::Other;
+    }
+}
+
+
+PassRefPtr<AXValueSource> createValueSource(NameSource& nameSource)
+{
+    String attribute = nameSource.attribute.toString();
+    AXValueSourceType::Enum type = valueSourceType(nameSource.type);
+    RefPtr<AXValueSource> valueSource = AXValueSource::create().setType(type);
+    RefPtr<AXValue> value;
+    if (!nameSource.relatedObjects.isEmpty()) {
+        AXValueType::Enum valueType = nameSource.attributeValue.isNull() ? AXValueType::NodeList : AXValueType::IdrefList;
+        value = createRelatedNodeListValue(nameSource.relatedObjects, valueType);
+        if (!nameSource.attributeValue.isNull())
+            value->setValue(JSONString::create(nameSource.attributeValue.string()));
+        valueSource->setValue(value);
+    } else if (!nameSource.text.isNull()) {
+        valueSource->setValue(createValue(nameSource.text));
+    } else if (!nameSource.attributeValue.isNull()) {
+        valueSource->setValue(createValue(nameSource.attributeValue));
+    }
+    if (nameSource.attribute != QualifiedName::null())
+        valueSource->setAttribute(nameSource.attribute.localName().string());
+    if (nameSource.superseded)
+        valueSource->setSuperseded(true);
+    if (nameSource.invalid)
+        valueSource->setInvalid(true);
+    if (nameSource.nativeSource != AXTextFromNativeHTMLUninitialized)
+        valueSource->setNativeSource(nativeSourceType(nameSource.nativeSource));
+    return valueSource;
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.h b/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.h
index 0168089..ae7808e 100644
--- a/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.h
+++ b/third_party/WebKit/Source/modules/accessibility/InspectorTypeBuilderHelper.h
@@ -18,6 +18,7 @@
 using TypeBuilder::Accessibility::AXValueType;
 using TypeBuilder::Accessibility::AXRelationshipAttributes;
 using TypeBuilder::Accessibility::AXValue;
+using TypeBuilder::Accessibility::AXValueSource;
 using TypeBuilder::Accessibility::AXWidgetAttributes;
 using TypeBuilder::Accessibility::AXWidgetStates;
 
@@ -33,8 +34,11 @@
 PassRefPtr<AXValue> createValue(int value, AXValueType::Enum = AXValueType::Integer);
 PassRefPtr<AXValue> createValue(float value, AXValueType::Enum = AXValueType::Number);
 PassRefPtr<AXValue> createBooleanValue(bool value, AXValueType::Enum = AXValueType::Boolean);
-PassRefPtr<AXValue> createRelatedNodeValue(const AXObject*);
-PassRefPtr<AXValue> createRelatedNodeListValue(AXObject::AccessibilityChildrenVector);
+PassRefPtr<AXValue> createRelatedNodeValue(const AXObject*, String* name = nullptr, AXValueType::Enum = AXValueType::Idref);
+PassRefPtr<AXValue> createRelatedNodeListValue(AXRelatedObjectVector&, AXValueType::Enum);
+PassRefPtr<AXValue> createRelatedNodeListValue(AXObject::AccessibilityChildrenVector& axObjects, AXValueType::Enum = AXValueType::IdrefList);
+
+PassRefPtr<AXValueSource> createValueSource(NameSource&);
 
 } // namespace blink
 
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBValue.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBValue.cpp
index 490ab09..b576368 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBValue.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBValue.cpp
@@ -7,11 +7,17 @@
 
 #include "platform/blob/BlobData.h"
 #include "public/platform/WebBlobInfo.h"
+#include "public/platform/modules/indexeddb/WebIDBValue.h"
 
 namespace blink {
 
 IDBValue::IDBValue() = default;
 
+IDBValue::IDBValue(const WebIDBValue& value)
+    : IDBValue(value.data, value.webBlobInfo, value.primaryKey, value.keyPath)
+{
+}
+
 IDBValue::IDBValue(PassRefPtr<SharedBuffer> data, const WebVector<WebBlobInfo>& webBlobInfo, IDBKey* primaryKey, const IDBKeyPath& keyPath)
     : m_data(data)
     , m_blobData(adoptPtr(new Vector<RefPtr<BlobDataHandle>>()))
@@ -25,11 +31,6 @@
     }
 }
 
-IDBValue::IDBValue(PassRefPtr<SharedBuffer> data, const WebVector<WebBlobInfo>& webBlobInfo)
-    : IDBValue(data, webBlobInfo, nullptr, IDBKeyPath())
-{
-}
-
 IDBValue::IDBValue(const IDBValue* value, IDBKey* primaryKey, const IDBKeyPath& keyPath)
     : m_data(value->m_data)
     , m_blobData(adoptPtr(new Vector<RefPtr<BlobDataHandle>>()))
@@ -48,14 +49,9 @@
     return adoptRef(new IDBValue());
 }
 
-PassRefPtr<IDBValue> IDBValue::create(PassRefPtr<SharedBuffer>data, const WebVector<WebBlobInfo>& webBlobInfo)
+PassRefPtr<IDBValue> IDBValue::create(const WebIDBValue& value)
 {
-    return adoptRef(new IDBValue(data, webBlobInfo));
-}
-
-PassRefPtr<IDBValue> IDBValue::create(PassRefPtr<SharedBuffer>data, const WebVector<WebBlobInfo>& webBlobInfo, IDBKey* primaryKey, const IDBKeyPath& keyPath)
-{
-    return adoptRef(new IDBValue(data, webBlobInfo, primaryKey, keyPath));
+    return adoptRef(new IDBValue(value));
 }
 
 PassRefPtr<IDBValue> IDBValue::create(const IDBValue* value, IDBKey* primaryKey, const IDBKeyPath& keyPath)
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBValue.h b/third_party/WebKit/Source/modules/indexeddb/IDBValue.h
index c95098a..69fd9c6a 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBValue.h
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBValue.h
@@ -17,12 +17,12 @@
 
 class BlobDataHandle;
 class WebBlobInfo;
+struct WebIDBValue;
 
 class MODULES_EXPORT IDBValue final : public RefCounted<IDBValue> {
 public:
     static PassRefPtr<IDBValue> create();
-    static PassRefPtr<IDBValue> create(PassRefPtr<SharedBuffer>, const WebVector<WebBlobInfo>&);
-    static PassRefPtr<IDBValue> create(PassRefPtr<SharedBuffer>, const WebVector<WebBlobInfo>&, IDBKey*, const IDBKeyPath&);
+    static PassRefPtr<IDBValue> create(const WebIDBValue&);
     static PassRefPtr<IDBValue> create(const IDBValue*, IDBKey*, const IDBKeyPath&);
 
     bool isNull() const;
@@ -34,7 +34,7 @@
 
 private:
     IDBValue();
-    IDBValue(PassRefPtr<SharedBuffer>, const WebVector<WebBlobInfo>&);
+    IDBValue(const WebIDBValue&);
     IDBValue(PassRefPtr<SharedBuffer>, const WebVector<WebBlobInfo>&, IDBKey*, const IDBKeyPath&);
     IDBValue(const IDBValue*, IDBKey*, const IDBKeyPath&);
 
diff --git a/third_party/WebKit/Source/modules/indexeddb/WebIDBCallbacksImpl.cpp b/third_party/WebKit/Source/modules/indexeddb/WebIDBCallbacksImpl.cpp
index 5190eac9..8c0513d 100644
--- a/third_party/WebKit/Source/modules/indexeddb/WebIDBCallbacksImpl.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/WebIDBCallbacksImpl.cpp
@@ -89,7 +89,7 @@
 void WebIDBCallbacksImpl::onSuccess(WebIDBCursor* cursor, const WebIDBKey& key, const WebIDBKey& primaryKey, const WebIDBValue& value)
 {
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_request->executionContext(), m_asyncOperationId);
-    m_request->onSuccess(adoptPtr(cursor), key, primaryKey, IDBValue::create(value.data, value.webBlobInfo));
+    m_request->onSuccess(adoptPtr(cursor), key, primaryKey, IDBValue::create(value));
     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
 }
 
@@ -110,7 +110,7 @@
 void WebIDBCallbacksImpl::onSuccess(const WebIDBValue& value)
 {
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_request->executionContext(), m_asyncOperationId);
-    m_request->onSuccess(IDBValue::create(value.data, value.webBlobInfo, value.primaryKey, value.keyPath));
+    m_request->onSuccess(IDBValue::create(value));
     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
 }
 
@@ -118,10 +118,8 @@
 {
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_request->executionContext(), m_asyncOperationId);
     Vector<RefPtr<IDBValue>> idbValues(values.size());
-    for (size_t i = 0; i < values.size(); ++i) {
-        const WebIDBValue& value = values[i];
-        idbValues[i] = IDBValue::create(value.data, value.webBlobInfo, value.primaryKey, value.keyPath);
-    }
+    for (size_t i = 0; i < values.size(); ++i)
+        idbValues[i] = IDBValue::create(values[i]);
     m_request->onSuccess(idbValues);
     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
 }
@@ -143,7 +141,7 @@
 void WebIDBCallbacksImpl::onSuccess(const WebIDBKey& key, const WebIDBKey& primaryKey, const WebIDBValue& value)
 {
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_request->executionContext(), m_asyncOperationId);
-    m_request->onSuccess(key, primaryKey, IDBValue::create(value.data, value.webBlobInfo));
+    m_request->onSuccess(key, primaryKey, IDBValue::create(value));
     InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
 }
 
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVRDevice.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVRDevice.cpp
index 6987356..4824d2b4 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVRDevice.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVRDevice.cpp
@@ -83,7 +83,7 @@
 NavigatorVRDevice::NavigatorVRDevice(LocalFrame* frame)
     : DOMWindowProperty(frame)
 {
-    m_hardwareUnits = new VRHardwareUnitCollection(controller());
+    m_hardwareUnits = new VRHardwareUnitCollection(this);
 }
 
 NavigatorVRDevice::~NavigatorVRDevice()
diff --git a/third_party/WebKit/Source/modules/vr/VRController.h b/third_party/WebKit/Source/modules/vr/VRController.h
index 83ca7a1..fed11d5e 100644
--- a/third_party/WebKit/Source/modules/vr/VRController.h
+++ b/third_party/WebKit/Source/modules/vr/VRController.h
@@ -14,7 +14,7 @@
 namespace blink {
 
 class MODULES_EXPORT VRController final
-    : public GarbageCollectedFinalized<VRController>
+    : public NoBaseWillBeGarbageCollectedFinalized<VRController>
     , public WillBeHeapSupplement<LocalFrame>
     , public LocalFrameLifecycleObserver {
     WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(VRController);
diff --git a/third_party/WebKit/Source/modules/vr/VRHardwareUnit.cpp b/third_party/WebKit/Source/modules/vr/VRHardwareUnit.cpp
index b4d7c788..f463332 100644
--- a/third_party/WebKit/Source/modules/vr/VRHardwareUnit.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRHardwareUnit.cpp
@@ -14,10 +14,10 @@
 
 namespace blink {
 
-VRHardwareUnit::VRHardwareUnit(VRController* controller)
+VRHardwareUnit::VRHardwareUnit(NavigatorVRDevice* navigatorVRDevice)
     : m_nextDeviceId(1)
     , m_frameIndex(0)
-    , m_controller(controller)
+    , m_navigatorVRDevice(navigatorVRDevice)
 {
     m_positionState = VRPositionState::create();
 }
@@ -59,13 +59,13 @@
 
 VRController* VRHardwareUnit::controller()
 {
-    return m_controller;
+    return m_navigatorVRDevice->controller();
 }
 
 VRPositionState* VRHardwareUnit::getSensorState()
 {
     WebHMDSensorState state;
-    m_controller->getSensorState(m_index, state);
+    controller()->getSensorState(m_index, state);
     m_positionState->setState(state);
     m_frameIndex = state.frameIndex;
     return m_positionState;
@@ -73,7 +73,7 @@
 
 DEFINE_TRACE(VRHardwareUnit)
 {
-    visitor->trace(m_controller);
+    visitor->trace(m_navigatorVRDevice);
     visitor->trace(m_positionState);
     visitor->trace(m_hmd);
     visitor->trace(m_positionSensor);
diff --git a/third_party/WebKit/Source/modules/vr/VRHardwareUnit.h b/third_party/WebKit/Source/modules/vr/VRHardwareUnit.h
index fc8bfeb..62a4cbee 100644
--- a/third_party/WebKit/Source/modules/vr/VRHardwareUnit.h
+++ b/third_party/WebKit/Source/modules/vr/VRHardwareUnit.h
@@ -26,7 +26,7 @@
 
 class VRHardwareUnit : public GarbageCollectedFinalized<VRHardwareUnit> {
 public:
-    explicit VRHardwareUnit(VRController*);
+    explicit VRHardwareUnit(NavigatorVRDevice*);
     virtual ~VRHardwareUnit();
 
     void updateFromWebVRDevice(const WebVRDevice&);
@@ -55,7 +55,7 @@
 
     unsigned m_frameIndex;
 
-    Member<VRController> m_controller;
+    Member<NavigatorVRDevice> m_navigatorVRDevice;
     Member<VRPositionState> m_positionState;
 
     // Device types
diff --git a/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.cpp b/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.cpp
index 9df7d74..383ab65 100644
--- a/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.cpp
@@ -22,8 +22,8 @@
 
 namespace blink {
 
-VRHardwareUnitCollection::VRHardwareUnitCollection(VRController* controller)
-    : m_controller(controller)
+VRHardwareUnitCollection::VRHardwareUnitCollection(NavigatorVRDevice* navigatorVRDevice)
+    : m_navigatorVRDevice(navigatorVRDevice)
 {
 }
 
@@ -34,7 +34,7 @@
     for (const auto& device : devices) {
         VRHardwareUnit* hardwareUnit = getHardwareUnitForIndex(device.index);
         if (!hardwareUnit) {
-            hardwareUnit = new VRHardwareUnit(m_controller);
+            hardwareUnit = new VRHardwareUnit(m_navigatorVRDevice);
             m_hardwareUnits.append(hardwareUnit);
         }
 
@@ -60,7 +60,7 @@
 
 DEFINE_TRACE(VRHardwareUnitCollection)
 {
-    visitor->trace(m_controller);
+    visitor->trace(m_navigatorVRDevice);
     visitor->trace(m_hardwareUnits);
 }
 
diff --git a/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.h b/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.h
index e273fd2..cbdf63f 100644
--- a/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.h
+++ b/third_party/WebKit/Source/modules/vr/VRHardwareUnitCollection.h
@@ -16,7 +16,7 @@
 
 class VRHardwareUnitCollection final : public GarbageCollected<VRHardwareUnitCollection> {
 public:
-    explicit VRHardwareUnitCollection(VRController*);
+    explicit VRHardwareUnitCollection(NavigatorVRDevice*);
 
     VRDeviceVector updateVRHardwareUnits(const WebVector<WebVRDevice>&);
     VRHardwareUnit* getHardwareUnitForIndex(unsigned index);
@@ -24,7 +24,7 @@
     DECLARE_VIRTUAL_TRACE();
 
 private:
-    Member<VRController> m_controller;
+    Member<NavigatorVRDevice> m_navigatorVRDevice;
     HeapVector<Member<VRHardwareUnit>> m_hardwareUnits;
 };
 
diff --git a/third_party/WebKit/Source/platform/blink_platform.gypi b/third_party/WebKit/Source/platform/blink_platform.gypi
index 4d46be2..fba0e2e2 100644
--- a/third_party/WebKit/Source/platform/blink_platform.gypi
+++ b/third_party/WebKit/Source/platform/blink_platform.gypi
@@ -683,10 +683,10 @@
       'graphics/paint/FixedPositionDisplayItem.h',
       'graphics/paint/FloatClipDisplayItem.cpp',
       'graphics/paint/FloatClipDisplayItem.h',
+      'graphics/paint/PaintChunkProperties.h',
       'graphics/paint/PaintChunk.h',
       'graphics/paint/PaintChunker.cpp',
       'graphics/paint/PaintChunker.h',
-      'graphics/paint/PaintProperties.h',
       'graphics/paint/ScrollDisplayItem.cpp',
       'graphics/paint/ScrollDisplayItem.h',
       'graphics/paint/SkPictureBuilder.h',
@@ -697,6 +697,7 @@
       'graphics/paint/Transform3DDisplayItem.h',
       'graphics/paint/TransformDisplayItem.cpp',
       'graphics/paint/TransformDisplayItem.h',
+      'graphics/paint/TransformPaintPropertyNode.h',
       'graphics/paint/CompositingDisplayItem.cpp',
       'graphics/paint/CompositingDisplayItem.h',
       'graphics/skia/ImagePixelLocker.cpp',
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPathRecorder.cpp
index 5511d28..c2ea498 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathRecorder.cpp
@@ -16,8 +16,6 @@
     , m_client(client)
 {
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<BeginClipPathDisplayItem>(m_client, clipPath);
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipRecorder.cpp
index 156f0c2..841154c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipRecorder.cpp
@@ -17,8 +17,6 @@
     , m_type(type)
 {
     ASSERT(m_context.displayItemList());
-    if (m_context.displayItemList()->displayItemConstructionIsDisabled())
-        return;
     m_context.displayItemList()->createAndAppend<ClipDisplayItem>(m_client, type, pixelSnappedIntRect(clipRect));
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
index 7e8a0df..b4b9c84 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
@@ -99,9 +99,9 @@
         m_newPaintChunks.incrementDisplayItemIndex();
 }
 
-void DisplayItemList::updateCurrentPaintProperties(const PaintProperties& newPaintProperties)
+void DisplayItemList::updateCurrentPaintChunkProperties(const PaintChunkProperties& newProperties)
 {
-    m_newPaintChunks.updateCurrentPaintProperties(newPaintProperties);
+    m_newPaintChunks.updateCurrentPaintChunkProperties(newProperties);
 }
 
 void DisplayItemList::beginScope()
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.h
index d68207f..482c81a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.h
@@ -78,21 +78,23 @@
 
     // These methods are called during painting.
 
-    // Provide a new set of paint properties to apply to recorded display items,
-    // for Slimming Paint v2.
-    void updateCurrentPaintProperties(const PaintProperties&);
+    // Provide a new set of paint chunk properties to apply to recorded display
+    // items, for Slimming Paint v2.
+    // TODO(pdr): This should be moved to PaintArtifact.
+    void updateCurrentPaintChunkProperties(const PaintChunkProperties&);
 
     template <typename DisplayItemClass, typename... Args>
-    DisplayItemClass& createAndAppend(Args&&... args)
+    void createAndAppend(Args&&... args)
     {
         static_assert(WTF::IsSubclass<DisplayItemClass, DisplayItem>::value,
             "Can only createAndAppend subclasses of DisplayItem.");
         static_assert(sizeof(DisplayItemClass) <= kMaximumDisplayItemSize,
             "DisplayItem subclass is larger than kMaximumDisplayItemSize.");
 
+        if (displayItemConstructionIsDisabled())
+            return;
         DisplayItemClass& displayItem = m_newDisplayItems.allocateAndConstruct<DisplayItemClass>(WTF::forward<Args>(args)...);
         processNewItem(displayItem);
-        return displayItem;
     }
 
     // Creates and appends an ending display item to pair with a preceding
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemTransformTree.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemTransformTree.h
index 89863dcf..1c7d99b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemTransformTree.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemTransformTree.h
@@ -18,6 +18,8 @@
 //
 // This class is also the private implementation of WebDisplayItemTransformTree.
 // For more detail, see WebDisplayItemTransformTree.h.
+//
+// TODO(pdr): Remove this in favor of TransformPaintPropertyNode.
 class PLATFORM_EXPORT DisplayItemTransformTree {
 public:
     using TransformNode = WebDisplayItemTransformTree::TransformNode;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
index 8c41bfbb..cf2859bf 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
@@ -19,9 +19,6 @@
     ASSERT(context.displayItemList());
     ASSERT(DisplayItem::isDrawingType(type));
 
-    if (context.displayItemList()->displayItemConstructionIsDisabled())
-        return false;
-
     if (!context.displayItemList()->clientCacheIsValid(client.displayItemClient()))
         return false;
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h b/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
index b893c28..1edf9f3 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
@@ -5,7 +5,7 @@
 #ifndef PaintChunk_h
 #define PaintChunk_h
 
-#include "platform/graphics/paint/PaintProperties.h"
+#include "platform/graphics/paint/PaintChunkProperties.h"
 #include <iosfwd>
 
 namespace blink {
@@ -18,7 +18,7 @@
 // This is a Slimming Paint v2 class.
 struct PaintChunk {
     PaintChunk() : beginIndex(0), endIndex(0) { }
-    PaintChunk(unsigned begin, unsigned end, const PaintProperties& props)
+    PaintChunk(unsigned begin, unsigned end, const PaintChunkProperties& props)
         : beginIndex(begin), endIndex(end), properties(props) { }
 
     // Index of the first drawing in this chunk.
@@ -29,7 +29,7 @@
     unsigned endIndex;
 
     // The paint properties which apply to this chunk.
-    PaintProperties properties;
+    PaintChunkProperties properties;
 };
 
 inline bool operator==(const PaintChunk& a, const PaintChunk& b)
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkProperties.h b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkProperties.h
new file mode 100644
index 0000000..0e8bb4b
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkProperties.h
@@ -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.
+
+#ifndef PaintChunkProperties_h
+#define PaintChunkProperties_h
+
+#include "platform/graphics/paint/TransformPaintPropertyNode.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+// The set of paint properties applying to a |PaintChunk|. These properties are
+// not local-only paint style parameters such as color, but instead represent
+// the hierarchy of transforms, clips, effects, etc, that apply to a contiguous
+// chunk of display items. A single DisplayItemClient can generate multiple
+// properties of the same type and this struct represents the total state of all
+// properties for a given |PaintChunk|.
+//
+// This differs from |ObjectPaintProperties| because it only stores one property
+// for each type (e.g., either transform or perspective, but not both).
+struct PaintChunkProperties {
+    // TODO(pdr): Add clip, scroll, and effect properties.
+    RefPtr<TransformPaintPropertyNode> transform;
+};
+
+// Equality is based only on the pointers and is not 'deep' which would require
+// crawling the entire property tree to compute.
+inline bool operator==(const PaintChunkProperties& a, const PaintChunkProperties& b)
+{
+    return a.transform.get() == b.transform.get();
+}
+
+inline bool operator!=(const PaintChunkProperties& a, const PaintChunkProperties& b)
+{
+    return !(a == b);
+}
+
+// Redeclared here to avoid ODR issues.
+// See platform/testing/PaintPrinters.h.
+void PrintTo(const PaintChunkProperties&, std::ostream*);
+
+} // namespace blink
+
+#endif // PaintChunkProperties_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.cpp
index 2616cca..cf5e9e0 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.cpp
@@ -17,7 +17,7 @@
 {
 }
 
-void PaintChunker::updateCurrentPaintProperties(const PaintProperties& properties)
+void PaintChunker::updateCurrentPaintChunkProperties(const PaintChunkProperties& properties)
 {
     ASSERT(RuntimeEnabledFeatures::slimmingPaintV2Enabled());
 
@@ -60,7 +60,7 @@
 {
     Vector<PaintChunk> chunks;
     chunks.swap(m_chunks);
-    m_currentProperties = PaintProperties();
+    m_currentProperties = PaintChunkProperties();
     return chunks;
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.h b/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.h
index 33f5a7a..d99bd34 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunker.h
@@ -7,22 +7,22 @@
 
 #include "platform/PlatformExport.h"
 #include "platform/graphics/paint/PaintChunk.h"
-#include "platform/graphics/paint/PaintProperties.h"
+#include "platform/graphics/paint/PaintChunkProperties.h"
 #include "wtf/Vector.h"
 
 namespace blink {
 
-// Accepts information about changes to |PaintProperties| as drawings are
+// Accepts information about changes to |PaintChunkProperties| as drawings are
 // accumulated, and produces a series of paint chunks: contiguous ranges of the
-// display list with identical |PaintProperties|.
+// display list with identical |PaintChunkProperties|.
 class PLATFORM_EXPORT PaintChunker {
 public:
     PaintChunker();
     ~PaintChunker();
 
-    bool isInInitialState() const { return m_chunks.isEmpty() && m_currentProperties == PaintProperties(); }
+    bool isInInitialState() const { return m_chunks.isEmpty() && m_currentProperties == PaintChunkProperties(); }
 
-    void updateCurrentPaintProperties(const PaintProperties&);
+    void updateCurrentPaintChunkProperties(const PaintChunkProperties&);
 
     void incrementDisplayItemIndex();
     void decrementDisplayItemIndex();
@@ -33,7 +33,7 @@
 
 private:
     Vector<PaintChunk> m_chunks;
-    PaintProperties m_currentProperties;
+    PaintChunkProperties m_currentProperties;
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
index 38be4a4e..cef32358 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkerTest.cpp
@@ -14,7 +14,7 @@
 namespace blink {
 namespace {
 
-static PaintProperties samplePaintProperties() { return PaintProperties(); }
+static PaintChunkProperties rootPaintChunkProperties() { return PaintChunkProperties(); }
 
 class PaintChunkerTest : public testing::Test {
 protected:
@@ -41,33 +41,33 @@
 TEST_F(PaintChunkerTest, SingleNonEmptyRange)
 {
     PaintChunker chunker;
-    chunker.updateCurrentPaintProperties(samplePaintProperties());
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
     chunker.incrementDisplayItemIndex();
     chunker.incrementDisplayItemIndex();
     Vector<PaintChunk> chunks = chunker.releasePaintChunks();
 
     EXPECT_THAT(chunks, ElementsAre(
-        PaintChunk(0, 2, samplePaintProperties())));
+        PaintChunk(0, 2, rootPaintChunkProperties())));
 }
 
 TEST_F(PaintChunkerTest, SamePropertiesTwiceCombineIntoOneChunk)
 {
     PaintChunker chunker;
-    chunker.updateCurrentPaintProperties(samplePaintProperties());
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
     chunker.incrementDisplayItemIndex();
     chunker.incrementDisplayItemIndex();
-    chunker.updateCurrentPaintProperties(samplePaintProperties());
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
     chunker.incrementDisplayItemIndex();
     Vector<PaintChunk> chunks = chunker.releasePaintChunks();
 
     EXPECT_THAT(chunks, ElementsAre(
-        PaintChunk(0, 3, samplePaintProperties())));
+        PaintChunk(0, 3, rootPaintChunkProperties())));
 }
 
 TEST_F(PaintChunkerTest, CanRewindDisplayItemIndex)
 {
     PaintChunker chunker;
-    chunker.updateCurrentPaintProperties(samplePaintProperties());
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
     chunker.incrementDisplayItemIndex();
     chunker.incrementDisplayItemIndex();
     chunker.decrementDisplayItemIndex();
@@ -75,11 +75,85 @@
     Vector<PaintChunk> chunks = chunker.releasePaintChunks();
 
     EXPECT_THAT(chunks, ElementsAre(
-        PaintChunk(0, 2, samplePaintProperties())));
+        PaintChunk(0, 2, rootPaintChunkProperties())));
 }
 
-// TODO(jbroman): Add more tests one it is possible for there to be two distinct
-// PaintProperties.
+TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging)
+{
+    PaintChunker chunker;
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
+    chunker.incrementDisplayItemIndex();
+    chunker.incrementDisplayItemIndex();
+
+    PaintChunkProperties simpleTransform;
+    simpleTransform.transform = adoptRef(new TransformPaintPropertyNode(TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7)));
+
+    chunker.updateCurrentPaintChunkProperties(simpleTransform);
+    chunker.incrementDisplayItemIndex();
+
+    PaintChunkProperties anotherTransform;
+    anotherTransform.transform = adoptRef(new TransformPaintPropertyNode(TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7)));
+    chunker.updateCurrentPaintChunkProperties(anotherTransform);
+    chunker.incrementDisplayItemIndex();
+
+    Vector<PaintChunk> chunks = chunker.releasePaintChunks();
+
+    EXPECT_THAT(chunks, ElementsAre(
+        PaintChunk(0, 2, rootPaintChunkProperties()),
+        PaintChunk(2, 3, simpleTransform),
+        PaintChunk(3, 4, anotherTransform)));
+}
+
+TEST_F(PaintChunkerTest, BuildLinearChunksFromNestedTransforms)
+{
+    // Test that "nested" transforms linearize using the following
+    // sequence of transforms and display items:
+    // <root xform>, <paint>, <a xform>, <paint>, <paint>, </a xform>, <paint>, </root xform>
+    PaintChunker chunker;
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
+    chunker.incrementDisplayItemIndex();
+
+    PaintChunkProperties simpleTransform;
+    simpleTransform.transform = adoptRef(new TransformPaintPropertyNode(TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7)));
+    chunker.updateCurrentPaintChunkProperties(simpleTransform);
+    chunker.incrementDisplayItemIndex();
+    chunker.incrementDisplayItemIndex();
+
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
+    chunker.incrementDisplayItemIndex();
+
+    Vector<PaintChunk> chunks = chunker.releasePaintChunks();
+
+    EXPECT_THAT(chunks, ElementsAre(
+        PaintChunk(0, 1, rootPaintChunkProperties()),
+        PaintChunk(1, 3, simpleTransform),
+        PaintChunk(3, 4, rootPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, ChangingPropertiesWithoutItems)
+{
+    // Test that properties can change without display items being generated.
+    PaintChunker chunker;
+    chunker.updateCurrentPaintChunkProperties(rootPaintChunkProperties());
+    chunker.incrementDisplayItemIndex();
+
+    PaintChunkProperties firstTransform;
+    firstTransform.transform = adoptRef(new TransformPaintPropertyNode(TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7)));
+    chunker.updateCurrentPaintChunkProperties(firstTransform);
+
+    PaintChunkProperties secondTransform;
+    secondTransform.transform = adoptRef(new TransformPaintPropertyNode(TransformationMatrix(9, 8, 7, 6, 5, 4), FloatPoint3D(3, 2, 1)));
+    chunker.updateCurrentPaintChunkProperties(secondTransform);
+
+    chunker.incrementDisplayItemIndex();
+    Vector<PaintChunk> chunks = chunker.releasePaintChunks();
+
+    EXPECT_THAT(chunks, ElementsAre(
+        PaintChunk(0, 1, rootPaintChunkProperties()),
+        PaintChunk(1, 2, secondTransform)));
+}
+
+// TODO(pdr): Add more tests once we have more paint properties.
 
 } // namespace
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintProperties.h b/third_party/WebKit/Source/platform/graphics/paint/PaintProperties.h
deleted file mode 100644
index 69a6651..0000000
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintProperties.h
+++ /dev/null
@@ -1,36 +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.
-
-#ifndef PaintProperties_h
-#define PaintProperties_h
-
-#include <iosfwd>
-
-namespace blink {
-
-// The set of paint properties applying to a |PaintChunk|.
-// In particular, this does not mean properties like background-color, but
-// rather the hierarchy of transforms, clips, effects, etc. that apply to a
-// contiguous chunk of drawings.
-struct PaintProperties {
-    // TODO(jbroman): Add actual properties.
-};
-
-inline bool operator==(const PaintProperties&, const PaintProperties&)
-{
-    return true;
-}
-
-inline bool operator!=(const PaintProperties& a, const PaintProperties& b)
-{
-    return !(a == b);
-}
-
-// Redeclared here to avoid ODR issues.
-// See platform/testing/PaintPrinters.h.
-void PrintTo(const PaintProperties&, std::ostream*);
-
-} // namespace blink
-
-#endif // PaintProperties_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
new file mode 100644
index 0000000..e656dc8
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
@@ -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.
+
+#ifndef TransformPaintPropertyNode_h
+#define TransformPaintPropertyNode_h
+
+#include "platform/PlatformExport.h"
+#include "platform/geometry/FloatPoint3D.h"
+#include "platform/transforms/TransformationMatrix.h"
+#include "wtf/PassRefPtr.h"
+#include "wtf/RefCounted.h"
+#include "wtf/RefPtr.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+// A transform created by a css property such as "transform" or "perspective"
+// along with a reference to the parent TransformPaintPropertyNode, or nullptr
+// for the root.
+class PLATFORM_EXPORT TransformPaintPropertyNode : public RefCounted<TransformPaintPropertyNode> {
+public:
+    TransformPaintPropertyNode(const TransformationMatrix& matrix, const FloatPoint3D& origin, PassRefPtr<TransformPaintPropertyNode> parent = nullptr)
+        : m_matrix(matrix), m_origin(origin), m_parent(parent) { }
+
+    const TransformationMatrix& matrix() const { return m_matrix; }
+    const FloatPoint3D& origin() const { return m_origin; }
+
+    // Parent transform that this transform is relative to, or nullptr if this
+    // is the root transform.
+    const TransformPaintPropertyNode* parent() const { return m_parent.get(); }
+
+private:
+    const TransformationMatrix m_matrix;
+    const FloatPoint3D m_origin;
+    RefPtr<TransformPaintPropertyNode> m_parent;
+};
+
+// Redeclared here to avoid ODR issues.
+// See platform/testing/PaintPrinters.h.
+void PrintTo(const TransformPaintPropertyNode&, std::ostream*);
+
+} // namespace blink
+
+#endif // TransformPaintPropertyNode_h
diff --git a/third_party/WebKit/Source/platform/testing/PaintPrinters.cpp b/third_party/WebKit/Source/platform/testing/PaintPrinters.cpp
index 16d4667e..8b458a6 100644
--- a/third_party/WebKit/Source/platform/testing/PaintPrinters.cpp
+++ b/third_party/WebKit/Source/platform/testing/PaintPrinters.cpp
@@ -6,7 +6,7 @@
 #include "platform/testing/PaintPrinters.h"
 
 #include "platform/graphics/paint/PaintChunk.h"
-#include "platform/graphics/paint/PaintProperties.h"
+#include "platform/graphics/paint/PaintChunkProperties.h"
 #include <ostream> // NOLINT
 
 namespace blink {
@@ -20,9 +20,46 @@
     *os << ")";
 }
 
-void PrintTo(const PaintProperties& properties, std::ostream* os)
+void PrintTo(const PaintChunkProperties& properties, std::ostream* os)
 {
-    *os << "PaintProperties()";
+    *os << "PaintChunkProperties(";
+    if (properties.transform) {
+        *os << "transform=";
+        PrintTo(*properties.transform, os);
+    }
+    *os << ")";
+}
+
+// TODO(pdr): Create and move this to TransformPrinters.cpp.
+static void PrintTo(const TransformationMatrix& matrix, std::ostream* os)
+{
+    TransformationMatrix::DecomposedType decomposition;
+    if (!matrix.decompose(decomposition)) {
+        *os << "TransformationMatrix(degenerate)";
+        return;
+    }
+
+    if (matrix.isIdentityOrTranslation()) {
+        *os << "TransformationMatrix(translation=(" << decomposition.translateX << "," << decomposition.translateY << "," << decomposition.translateZ << "))";
+        return;
+    }
+
+    *os << "TransformationMatrix("
+        << "translation=(" << decomposition.translateX << "," << decomposition.translateY << "," << decomposition.translateZ << ")"
+        << ", scale=(" << decomposition.scaleX << "," << decomposition.scaleY << "," << decomposition.scaleZ << ")"
+        << ", skew=(" << decomposition.skewXY << "," << decomposition.skewXZ << "," << decomposition.skewYZ << ")"
+        << ", quaternion=(" << decomposition.quaternionX << "," << decomposition.quaternionY << "," << decomposition.quaternionZ << "," << decomposition.quaternionW << ")"
+        << ", perspective=(" << decomposition.perspectiveX << "," << decomposition.perspectiveY << "," << decomposition.perspectiveZ << "," << decomposition.perspectiveW << ")"
+        << ")";
+}
+
+void PrintTo(const TransformPaintPropertyNode& transformPaintProperty, std::ostream* os)
+{
+    *os << "TransformPaintPropertyNode(matrix=";
+    PrintTo(transformPaintProperty.matrix(), os);
+    *os << ", origin=";
+    PrintTo(transformPaintProperty.origin(), os);
+    *os << ")";
 }
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/PaintPrinters.h b/third_party/WebKit/Source/platform/testing/PaintPrinters.h
index 31f7ddcc..34c88ef 100644
--- a/third_party/WebKit/Source/platform/testing/PaintPrinters.h
+++ b/third_party/WebKit/Source/platform/testing/PaintPrinters.h
@@ -11,6 +11,7 @@
 
 struct PaintChunk;
 struct PaintProperties;
+class TransformPaintPropertyNode;
 
 // GTest print support for platform paint classes.
 //
@@ -25,6 +26,7 @@
 // your unit test binary.
 void PrintTo(const PaintChunk&, std::ostream*);
 void PrintTo(const PaintProperties&, std::ostream*);
+void PrintTo(const TransformPaintPropertyNode&, std::ostream*);
 
 } // namespace blink
 
diff --git a/third_party/WebKit/Source/web/WebFrame.cpp b/third_party/WebKit/Source/web/WebFrame.cpp
index 98042f6..a1f3cd6 100644
--- a/third_party/WebKit/Source/web/WebFrame.cpp
+++ b/third_party/WebKit/Source/web/WebFrame.cpp
@@ -83,12 +83,24 @@
         frame->m_openedFrameTracker.reset(m_openedFrameTracker.release());
     }
 
+    FrameHost* host = oldFrame->host();
+    AtomicString name = oldFrame->tree().name();
+    FrameOwner* owner = oldFrame->owner();
+    oldFrame->disconnectOwnerElement();
+
+    v8::HandleScope handleScope(v8::Isolate::GetCurrent());
+    HashMap<DOMWrapperWorld*, v8::Local<v8::Object>> globals;
+    oldFrame->windowProxyManager()->clearForNavigation();
+    oldFrame->windowProxyManager()->releaseGlobals(globals);
+
+    // Although the Document in this frame is now unloaded, many resources
+    // associated with the frame itself have not yet been freed yet.
+    oldFrame->detach(FrameDetachType::Swap);
+
     // Finally, clone the state of the current Frame into one matching
     // the type of the passed in WebFrame.
     // FIXME: This is a bit clunky; this results in pointless decrements and
     // increments of connected subframes.
-    FrameOwner* owner = oldFrame->owner();
-    oldFrame->disconnectOwnerElement();
     if (frame->isWebLocalFrame()) {
         LocalFrame& localFrame = *toWebLocalFrameImpl(frame)->frame();
         ASSERT(owner == localFrame.owner());
@@ -104,13 +116,11 @@
             localFrame.page()->setMainFrame(&localFrame);
         }
     } else {
-        toWebRemoteFrameImpl(frame)->initializeCoreFrame(oldFrame->host(), owner, oldFrame->tree().name());
+        toWebRemoteFrameImpl(frame)->initializeCoreFrame(host, owner, name);
     }
-    toCoreFrame(frame)->finishSwapFrom(oldFrame.get());
 
-    // Although the Document in this frame is now unloaded, many resources
-    // associated with the frame itself have not yet been freed yet.
-    oldFrame->detach(FrameDetachType::Swap);
+    toCoreFrame(frame)->windowProxyManager()->setGlobals(globals);
+
     m_parent = nullptr;
 
     return true;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 27b2257..51f87fbd 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -401,7 +401,7 @@
 
 WebViewImpl::WebViewImpl(WebViewClient* client)
     : m_client(client)
-    , m_spellCheckClient(0)
+    , m_spellCheckClient(nullptr)
     , m_chromeClientImpl(ChromeClientImpl::create(this))
     , m_contextMenuClientImpl(this)
     , m_dragClientImpl(this)
@@ -429,10 +429,10 @@
     , m_devToolsEmulator(nullptr)
     , m_isTransparent(false)
     , m_tabsToLinks(false)
-    , m_layerTreeView(0)
-    , m_rootLayer(0)
-    , m_rootGraphicsLayer(0)
-    , m_rootTransformLayer(0)
+    , m_layerTreeView(nullptr)
+    , m_rootLayer(nullptr)
+    , m_rootGraphicsLayer(nullptr)
+    , m_rootTransformLayer(nullptr)
     , m_graphicsLayerFactory(adoptPtr(new GraphicsLayerFactoryChromium(this)))
     , m_matchesHeuristicsForGpuRasterization(false)
     , m_recreatingGraphicsContext(false)
@@ -458,7 +458,7 @@
 
     m_page = adoptPtrWillBeNoop(new Page(pageClients));
     MediaKeysController::provideMediaKeysTo(*m_page, &m_mediaKeysClientImpl);
-    provideSpeechRecognitionTo(*m_page, SpeechRecognitionClientProxy::create(client ? client->speechRecognizer() : 0));
+    provideSpeechRecognitionTo(*m_page, SpeechRecognitionClientProxy::create(client ? client->speechRecognizer() : nullptr));
     provideContextFeaturesTo(*m_page, ContextFeaturesClientImpl::create());
     provideDatabaseClientTo(*m_page, DatabaseClientImpl::create());
 
@@ -501,9 +501,10 @@
     return nullptr;
 }
 
-WebLocalFrameImpl* WebViewImpl::mainFrameImpl()
+WebLocalFrameImpl* WebViewImpl::mainFrameImpl() const
 {
-    return m_page && m_page->mainFrame() && m_page->mainFrame()->isLocalFrame() ? WebLocalFrameImpl::fromFrame(m_page->deprecatedLocalMainFrame()) : 0;
+    return m_page && m_page->mainFrame() && m_page->mainFrame()->isLocalFrame()
+        ? WebLocalFrameImpl::fromFrame(m_page->deprecatedLocalMainFrame()) : nullptr;
 }
 
 bool WebViewImpl::tabKeyCyclesThroughElements() const
@@ -1296,7 +1297,7 @@
     TRACE_EVENT0("input", "WebViewImpl::bestTapNode");
 
     if (!m_page || !m_page->mainFrame())
-        return 0;
+        return nullptr;
 
     Node* bestTouchNode = targetedTapEvent.hitTestResult().innerNode();
     if (!bestTouchNode)
@@ -1318,7 +1319,7 @@
         findCursorDefiningAncestor(bestTouchNode, m_page->deprecatedLocalMainFrame());
     // We show a highlight on tap only when the current node shows a hand cursor
     if (!cursorDefiningAncestor || !showsHandCursor(cursorDefiningAncestor, m_page->deprecatedLocalMainFrame())) {
-        return 0;
+        return nullptr;
     }
 
     // We should pick the largest enclosing node with hand cursor set. We do this by first jumping
@@ -1395,7 +1396,8 @@
 
     if (shouldZoomOut) {
         scale = minimumPageScaleFactor();
-        isAnimating = startPageScaleAnimation(mainFrameImpl()->frameView()->rootFrameToContents(pointInRootFrame), true, scale, doubleTapZoomAnimationDurationInSeconds);
+        IntPoint targetPosition = mainFrameImpl()->frameView()->rootFrameToContents(pointInRootFrame);
+        isAnimating = startPageScaleAnimation(targetPosition, true, scale, doubleTapZoomAnimationDurationInSeconds);
     } else {
         isAnimating = startPageScaleAnimation(scroll, false, scale, doubleTapZoomAnimationDurationInSeconds);
     }
@@ -1654,20 +1656,22 @@
 void WebViewImpl::enablePopupMouseWheelEventListener()
 {
     ASSERT(!m_popupMouseWheelEventListener);
-    ASSERT(mainFrameImpl()->frame()->document());
+    Document* document = mainFrameImpl()->frame()->document();
+    ASSERT(document);
     // We register an empty event listener, EmptyEventListener, so that mouse
     // wheel events get sent to the WebView.
     m_popupMouseWheelEventListener = EmptyEventListener::create();
-    mainFrameImpl()->frame()->document()->addEventListener(EventTypeNames::mousewheel, m_popupMouseWheelEventListener, false);
+    document->addEventListener(EventTypeNames::mousewheel, m_popupMouseWheelEventListener, false);
 }
 
 void WebViewImpl::disablePopupMouseWheelEventListener()
 {
     ASSERT(m_popupMouseWheelEventListener);
-    ASSERT(mainFrameImpl()->frame()->document());
+    Document* document = mainFrameImpl()->frame()->document();
+    ASSERT(document);
     // Document may have already removed the event listener, for instance, due
     // to a navigation, but remove it anyway.
-    mainFrameImpl()->frame()->document()->removeEventListener(EventTypeNames::mousewheel, m_popupMouseWheelEventListener.release(), false);
+    document->removeEventListener(EventTypeNames::mousewheel, m_popupMouseWheelEventListener.release(), false);
 }
 
 LocalDOMWindow* WebViewImpl::pagePopupWindow() const
@@ -1677,14 +1681,12 @@
 
 Frame* WebViewImpl::focusedCoreFrame() const
 {
-    return m_page ? m_page->focusController().focusedOrMainFrame() : 0;
+    return m_page ? m_page->focusController().focusedOrMainFrame() : nullptr;
 }
 
 WebViewImpl* WebViewImpl::fromPage(Page* page)
 {
-    if (!page)
-        return 0;
-    return static_cast<WebViewImpl*>(page->chromeClient().webView());
+    return page ? static_cast<WebViewImpl*>(page->chromeClient().webView()) : nullptr;
 }
 
 // WebWidget ------------------------------------------------------------------
@@ -2228,7 +2230,7 @@
             return;
 
         LocalFrame* frame = m_page->mainFrame() && m_page->mainFrame()->isLocalFrame()
-            ? m_page->deprecatedLocalMainFrame() : 0;
+            ? m_page->deprecatedLocalMainFrame() : nullptr;
         if (!frame)
             return;
 
@@ -2595,7 +2597,7 @@
     WebPluginContainerImpl* container = WebLocalFrameImpl::pluginContainerFromNode(frame, WebNode(focusedElement()));
     if (container && container->supportsInputMethod())
         return container->plugin();
-    return 0;
+    return nullptr;
 }
 
 bool WebViewImpl::selectionTextDirection(WebTextDirection& start, WebTextDirection& end) const
@@ -2699,7 +2701,7 @@
     }
 
     setRootGraphicsLayer(nullptr);
-    m_layerTreeView = 0;
+    m_layerTreeView = nullptr;
 }
 
 void WebViewImpl::didAcquirePointerLock()
@@ -2783,7 +2785,7 @@
 
 WebFrame* WebViewImpl::mainFrame()
 {
-    return WebFrame::fromFrame(m_page ? m_page->mainFrame() : 0);
+    return WebFrame::fromFrame(m_page ? m_page->mainFrame() : nullptr);
 }
 
 WebFrame* WebViewImpl::findFrameByName(
@@ -2795,7 +2797,7 @@
     Frame* frame = toWebLocalFrameImpl(relativeToFrame)->frame();
     frame = frame->tree().find(name);
     if (!frame || !frame->isLocalFrame())
-        return 0;
+        return nullptr;
     return WebLocalFrameImpl::fromFrame(toLocalFrame(frame));
 }
 
@@ -2860,7 +2862,7 @@
 bool WebViewImpl::scrollFocusedNodeIntoRect(const WebRect& rectInViewport)
 {
     LocalFrame* frame = page()->mainFrame() && page()->mainFrame()->isLocalFrame()
-        ? page()->deprecatedLocalMainFrame() : 0;
+        ? page()->deprecatedLocalMainFrame() : nullptr;
     Element* element = focusedElement();
     if (!frame || !frame->view() || !element)
         return false;
@@ -3273,7 +3275,16 @@
             setInitialPageScaleOverride(-1);
     }
 
-    pageScaleConstraintsSet().adjustForAndroidWebViewQuirks(adjustedDescription, defaultMinWidth.intValue(), deviceScaleFactor(), settingsImpl()->supportDeprecatedTargetDensityDPI(), page()->settings().wideViewportQuirkEnabled(), page()->settings().useWideViewport(), page()->settings().loadWithOverviewMode(), settingsImpl()->viewportMetaNonUserScalableQuirk());
+    Settings& pageSettings = page()->settings();
+    pageScaleConstraintsSet().adjustForAndroidWebViewQuirks(
+        adjustedDescription,
+        defaultMinWidth.intValue(),
+        deviceScaleFactor(),
+        settingsImpl()->supportDeprecatedTargetDensityDPI(),
+        pageSettings.wideViewportQuirkEnabled(),
+        pageSettings.useWideViewport(),
+        pageSettings.loadWithOverviewMode(),
+        settingsImpl()->viewportMetaNonUserScalableQuirk());
     float newInitialScale = pageScaleConstraintsSet().pageDefinedConstraints().initialScale;
     if (oldInitialScale != newInitialScale && newInitialScale != -1) {
         pageScaleConstraintsSet().setNeedsReset(true);
@@ -3329,7 +3340,7 @@
 {
     layout();
 
-    Document* document = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->document() : 0;
+    Document* document = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->document() : nullptr;
     if (!document || !document->layoutView() || !document->documentElement() || !document->documentElement()->layoutBox())
         return WebSize();
 
@@ -3913,7 +3924,7 @@
     // If we get to the <body> try to resume commits since we should have content
     // to paint now.
     // TODO(esprehn): Is this really optimal? We might start producing frames
-    // for very little content, should we wait for some herustic like
+    // for very little content, should we wait for some heuristic like
     // isVisuallyNonEmpty() ?
     resumeTreeViewCommitsIfRenderingReady();
 }
@@ -3968,11 +3979,12 @@
 
 void WebViewImpl::layoutUpdated(WebLocalFrameImpl* webframe)
 {
-    if (!m_client || !webframe->frame()->isLocalRoot())
+    LocalFrame* frame = webframe->frame();
+    if (!m_client || !frame->isLocalRoot())
         return;
 
-    if (m_shouldAutoResize && webframe->frame() && webframe->frame()->view()) {
-        WebSize frameSize = webframe->frame()->view()->frameRect().size();
+    if (m_shouldAutoResize) {
+        WebSize frameSize = frame->view()->frameRect().size();
         if (frameSize != m_size) {
             m_size = frameSize;
 
@@ -4072,11 +4084,11 @@
 {
     Frame* frame = m_page->focusController().focusedFrame();
     if (!frame || !frame->isLocalFrame())
-        return 0;
+        return nullptr;
 
     Document* document = toLocalFrame(frame)->document();
     if (!document)
-        return 0;
+        return nullptr;
 
     return document->focusedElement();
 }
@@ -4137,16 +4149,16 @@
     visualViewport.attachToLayerTree(layer, graphicsLayerFactory());
     if (layer) {
         m_rootGraphicsLayer = visualViewport.rootGraphicsLayer();
-        m_rootLayer = visualViewport.rootGraphicsLayer()->platformLayer();
-        m_rootTransformLayer = visualViewport.rootGraphicsLayer();
+        m_rootLayer = m_rootGraphicsLayer->platformLayer();
+        m_rootTransformLayer = m_rootGraphicsLayer;
         updateRootLayerTransform();
         m_layerTreeView->setRootLayer(*m_rootLayer);
         // We register viewport layers here since there may not be a layer
         // tree view prior to this point.
-        page()->frameHost().visualViewport().registerLayersWithTreeView(m_layerTreeView);
+        visualViewport.registerLayersWithTreeView(m_layerTreeView);
         updatePageOverlays();
         // TODO(enne): Work around page visibility changes not being
-        // propogated to the WebView in some circumstances.  This needs to
+        // propagated to the WebView in some circumstances.  This needs to
         // be refreshed here when setting a new root layer to avoid being
         // stuck in a presumed incorrectly invisible state.
         bool visible = page()->visibilityState() == PageVisibilityStateVisible;
@@ -4162,7 +4174,7 @@
         m_layerTreeView->clearRootLayer();
         m_shouldDispatchFirstVisuallyNonEmptyLayout = true;
         m_shouldDispatchFirstLayoutAfterFinishedParsing = true;
-        page()->frameHost().visualViewport().clearLayersForTreeView(m_layerTreeView);
+        visualViewport.clearLayersForTreeView(m_layerTreeView);
     }
 }
 
@@ -4181,13 +4193,15 @@
 
 PaintLayerCompositor* WebViewImpl::compositor() const
 {
-    if (!page() || !page()->mainFrame() || !page()->mainFrame()->isLocalFrame())
-        return 0;
+    WebLocalFrameImpl* frame = mainFrameImpl();
+    if (!frame)
+        return nullptr;
 
-    if (!page()->deprecatedLocalMainFrame()->document() || !page()->deprecatedLocalMainFrame()->document()->layoutView())
-        return 0;
+    Document* document = frame->frame()->document();
+    if (!document || !document->layoutView())
+        return nullptr;
 
-    return page()->deprecatedLocalMainFrame()->document()->layoutView()->compositor();
+    return document->layoutView()->compositor();
 }
 
 void WebViewImpl::registerForAnimations(WebLayer* layer)
@@ -4233,9 +4247,9 @@
     if (WebDevToolsAgentImpl* devTools = mainFrameDevToolsAgentImpl())
         devTools->layerTreeViewChanged(m_layerTreeView);
 
-    m_page->settings().setAcceleratedCompositingEnabled(m_layerTreeView != 0);
+    m_page->settings().setAcceleratedCompositingEnabled(m_layerTreeView);
 
-    // FIXME: only unittests, click to play, Android priting, and printing (for headers and footers)
+    // FIXME: only unittests, click to play, Android printing, and printing (for headers and footers)
     // make this assert necessary. We should make them not hit this code and then delete allowsBrokenNullLayerTreeView.
     ASSERT(m_layerTreeView || !m_client || m_client->allowsBrokenNullLayerTreeView());
 
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index a1f3e54..fa13843 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -339,7 +339,7 @@
 
     // Returns the main frame associated with this view. This may be null when
     // the page is shutting down, but will be valid at all other times.
-    WebLocalFrameImpl* mainFrameImpl();
+    WebLocalFrameImpl* mainFrameImpl() const;
 
     // FIXME: Temporary method to accommodate out-of-process frame ancestors;
     // will be removed when there can be multiple WebWidgets for a single page.
diff --git a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
index d42df534..72cad6f 100644
--- a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
@@ -159,6 +159,22 @@
     ASSERT_EQ(10, point4.y);
 }
 
+TEST_F(WebPluginContainerTest, PluginDocumentPluginIsFocused)
+{
+    URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("test.pdf"), WebString::fromUTF8("application/pdf"));
+
+    TestPluginWebFrameClient pluginWebFrameClient; // Must outlive webViewHelper.
+    FrameTestHelpers::WebViewHelper webViewHelper;
+    WebView* webView = webViewHelper.initializeAndLoad(m_baseURL + "test.pdf", true, &pluginWebFrameClient);
+    ASSERT(webView);
+    webView->layout();
+
+    WebDocument document = webView->mainFrame()->document();
+    EXPECT_TRUE(document.isPluginDocument());
+    WebPluginContainer* pluginContainer = getWebPluginContainer(webView, "plugin");
+    EXPECT_EQ(document.focusedElement(), pluginContainer->element());
+}
+
 TEST_F(WebPluginContainerTest, PrintOnePage)
 {
     URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("test.pdf"), WebString::fromUTF8("application/pdf"));
diff --git a/third_party/WebKit/public/platform/WebScrollbarLayer.h b/third_party/WebKit/public/platform/WebScrollbarLayer.h
index e9f7fc1..0fabc51 100644
--- a/third_party/WebKit/public/platform/WebScrollbarLayer.h
+++ b/third_party/WebKit/public/platform/WebScrollbarLayer.h
@@ -39,7 +39,6 @@
     virtual WebLayer* layer() = 0;
 
     virtual void setScrollLayer(WebLayer*) = 0;
-    virtual void setClipLayer(WebLayer*) = 0;
 };
 
 } // namespace blink
diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium
index 0693deb..c795127 100644
--- a/third_party/android_platform/README.chromium
+++ b/third_party/android_platform/README.chromium
@@ -35,6 +35,7 @@
 Added support for arch=x64 as an alias to arch=x86_64
 Added debug logging and --verbose parameter.
 Used fast ELF symbolizer for symbols.py and tombstones
+Used multiprocessing to pre-process logcat before symbolizing it
 
 Android relocation packing tool details:
     Copy sources from AOSP bionic/tools/relocation_packer
diff --git a/third_party/android_platform/development/scripts/stack_core.py b/third_party/android_platform/development/scripts/stack_core.py
index 281917f..34309787 100755
--- a/third_party/android_platform/development/scripts/stack_core.py
+++ b/third_party/android_platform/development/scripts/stack_core.py
@@ -16,20 +16,85 @@
 
 """stack symbolizes native crash dumps."""
 
+import itertools
 import logging
+import multiprocessing
+import os
 import re
+import subprocess
+import time
 
 import symbol
 
+UNKNOWN = '<unknown>'
+HEAP = '[heap]'
+STACK = '[stack]'
+_DEFAULT_JOBS=8
+_CHUNK_SIZE = 1000
+
+_PROCESS_INFO_LINE = re.compile('(pid: [0-9]+, tid: [0-9]+.*)')
+_SIGNAL_LINE = re.compile('(signal [0-9]+ \(.*\).*)')
+_REGISTER_LINE = re.compile('(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})')
+_THREAD_LINE = re.compile('(.*)(\-\-\- ){15}\-\-\-')
+_DALVIK_JNI_THREAD_LINE = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
+_DALVIK_NATIVE_THREAD_LINE = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
+
+_WIDTH = '{8}'
+if symbol.ARCH == 'arm64' or symbol.ARCH == 'x86_64' or symbol.ARCH == 'x64':
+  _WIDTH = '{16}'
+
+# Matches LOG(FATAL) lines, like the following example:
+#   [FATAL:source_file.cc(33)] Check failed: !instances_.empty()
+_LOG_FATAL_LINE = re.compile('(\[FATAL\:.*\].*)$')
+
+# Note that both trace and value line matching allow for variable amounts of
+# whitespace (e.g. \t). This is because the we want to allow for the stack
+# tool to operate on AndroidFeedback provided system logs. AndroidFeedback
+# strips out double spaces that are found in tombsone files and logcat output.
+#
+# Examples of matched trace lines include lines from tombstone files like:
+#   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so
+#   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so (symbol)
+# Or lines from AndroidFeedback crash report system logs like:
+#   03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
+# Please note the spacing differences.
+_TRACE_LINE = re.compile('(.*)\#(?P<frame>[0-9]+)[ \t]+(..)[ \t]+(0x)?(?P<address>[0-9a-f]{0,16})[ \t]+(?P<lib>[^\r\n \t]*)(?P<symbol_present> \((?P<symbol_name>.*)\))?')  # pylint: disable-msg=C6310
+
+# Matches lines emitted by src/base/debug/stack_trace_android.cc, like:
+#   #00 0x7324d92d /data/app-lib/org.chromium.native_test-1/libbase.cr.so+0x0006992d
+# This pattern includes the unused named capture groups <symbol_present> and
+# <symbol_name> so that it can interoperate with the |_TRACE_LINE| regex.
+_DEBUG_TRACE_LINE = re.compile(
+    '(.*)(?P<frame>\#[0-9]+ 0x[0-9a-f]' + _WIDTH + ') '
+    '(?P<lib>[^+]+)\+0x(?P<address>[0-9a-f]' + _WIDTH + ')'
+    '(?P<symbol_present>)(?P<symbol_name>)')
+
+# Examples of matched value lines include:
+#   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so
+#   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so (symbol)
+#   03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
+# Again, note the spacing differences.
+_VALUE_LINE = re.compile('(.*)([0-9a-f]' + _WIDTH + ')[ \t]+([0-9a-f]' + _WIDTH + ')[ \t]+([^\r\n \t]*)( \((.*)\))?')
+# Lines from 'code around' sections of the output will be matched before
+# value lines because otheriwse the 'code around' sections will be confused as
+# value lines.
+#
+# Examples include:
+#   801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
+#   03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
+code_line = re.compile('(.*)[ \t]*[a-f0-9]' + _WIDTH + '[ \t]*[a-f0-9]' + _WIDTH +
+                       '[ \t]*[a-f0-9]' + _WIDTH + '[ \t]*[a-f0-9]' + _WIDTH +
+                       '[ \t]*[a-f0-9]' + _WIDTH + '[ \t]*[ \r\n]')  # pylint: disable-msg=C6310
+
 def PrintTraceLines(trace_lines):
   """Print back trace."""
   maxlen = max(map(lambda tl: len(tl[1]), trace_lines))
   print
-  print "Stack Trace:"
-  print "  RELADDR   " + "FUNCTION".ljust(maxlen) + "  FILE:LINE"
+  print 'Stack Trace:'
+  print '  RELADDR   ' + 'FUNCTION'.ljust(maxlen) + '  FILE:LINE'
   for tl in trace_lines:
     (addr, symbol_with_offset, location) = tl
-    print "  %8s  %s  %s" % (addr, symbol_with_offset.ljust(maxlen), location)
+    print '  %8s  %s  %s' % (addr, symbol_with_offset.ljust(maxlen), location)
   return
 
 
@@ -37,17 +102,13 @@
   """Print stack data values."""
   maxlen = max(map(lambda tl: len(tl[2]), value_lines))
   print
-  print "Stack Data:"
-  print "  ADDR      VALUE     " + "FUNCTION".ljust(maxlen) + "  FILE:LINE"
+  print 'Stack Data:'
+  print '  ADDR      VALUE     ' + 'FUNCTION'.ljust(maxlen) + '  FILE:LINE'
   for vl in value_lines:
     (addr, value, symbol_with_offset, location) = vl
-    print "  %8s  %8s  %s  %s" % (addr, value, symbol_with_offset.ljust(maxlen), location)
+    print '  %8s  %8s  %s  %s' % (addr, value, symbol_with_offset.ljust(maxlen), location)
   return
 
-UNKNOWN = "<unknown>"
-HEAP = "[heap]"
-STACK = "[stack]"
-
 
 def PrintOutput(trace_lines, value_lines, more_info):
   if trace_lines:
@@ -63,66 +124,60 @@
 
 def PrintDivider():
   print
-  print "-----------------------------------------------------\n"
+  print '-----------------------------------------------------\n'
 
 def ConvertTrace(lines, more_info):
   """Convert strings containing native crash to a stack."""
-  process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
-  signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
-  register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
-  thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
-  dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
-  dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
+  start = time.time()
 
-  width = "{8}"
-  if symbol.ARCH == "arm64" or symbol.ARCH == "x86_64" or symbol.ARCH == "x64":
-    width = "{16}"
+  chunks = [lines[i: i+_CHUNK_SIZE] for i in xrange(0, len(lines), _CHUNK_SIZE)]
+  pool = multiprocessing.Pool(processes=_DEFAULT_JOBS)
+  useful_log = itertools.chain(*pool.map(PreProcessLog, chunks))
+  pool.close()
+  pool.join()
+  end = time.time()
+  logging.debug('Finished processing. Elapsed time: %.4fs', (end - start))
 
-  # Matches LOG(FATAL) lines, like the following example:
-  #   [FATAL:source_file.cc(33)] Check failed: !instances_.empty()
-  log_fatal_line = re.compile("(\[FATAL\:.*\].*)$")
+  ResolveCrashSymbol(list(useful_log), more_info)
+  end = time.time()
+  logging.debug('Finished resolving symbols. Elapsed time: %.4fs',
+                (end - start))
 
-  # Note that both trace and value line matching allow for variable amounts of
-  # whitespace (e.g. \t). This is because the we want to allow for the stack
-  # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
-  # strips out double spaces that are found in tombsone files and logcat output.
-  #
-  # Examples of matched trace lines include lines from tombstone files like:
-  #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so
-  #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so (symbol)
-  # Or lines from AndroidFeedback crash report system logs like:
-  #   03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
-  # Please note the spacing differences.
-  trace_line = re.compile("(.*)\#(?P<frame>[0-9]+)[ \t]+(..)[ \t]+(0x)?(?P<address>[0-9a-f]{0,16})[ \t]+(?P<lib>[^\r\n \t]*)(?P<symbol_present> \((?P<symbol_name>.*)\))?")  # pylint: disable-msg=C6310
 
-  # Matches lines emitted by src/base/debug/stack_trace_android.cc, like:
-  #   #00 0x7324d92d /data/app-lib/org.chromium.native_test-1/libbase.cr.so+0x0006992d
-  # This pattern includes the unused named capture groups <symbol_present> and
-  # <symbol_name> so that it can interoperate with the |trace_line| regex.
-  debug_trace_line = re.compile(
-      '(.*)(?P<frame>\#[0-9]+ 0x[0-9a-f]' + width + ') '
-      '(?P<lib>[^+]+)\+0x(?P<address>[0-9a-f]' + width + ')'
-      '(?P<symbol_present>)(?P<symbol_name>)')
+def PreProcessLog(lines):
+  """Preprocess the strings, only keep the useful ones.
+  Args:
+    lines: a list of byte strings read from logcat
 
-  # Examples of matched value lines include:
-  #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so
-  #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so (symbol)
-  #   03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
-  # Again, note the spacing differences.
-  value_line = re.compile("(.*)([0-9a-f]" + width + ")[ \t]+([0-9a-f]" + width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?")
-  # Lines from 'code around' sections of the output will be matched before
-  # value lines because otheriwse the 'code around' sections will be confused as
-  # value lines.
-  #
-  # Examples include:
-  #   801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
-  #   03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
-  code_line = re.compile("(.*)[ \t]*[a-f0-9]" + width +
-                         "[ \t]*[a-f0-9]" + width +
-                         "[ \t]*[a-f0-9]" + width +
-                         "[ \t]*[a-f0-9]" + width +
-                         "[ \t]*[a-f0-9]" + width +
-                         "[ \t]*[ \r\n]")  # pylint: disable-msg=C6310
+  Returns:
+    A list of unicode strings related to native crash
+  """
+  useful_log = []
+  for ln in lines:
+    line = unicode(ln, errors='ignore')
+    if (_PROCESS_INFO_LINE.search(line)
+        or _SIGNAL_LINE.search(line)
+        or _REGISTER_LINE.search(line)
+        or _THREAD_LINE.search(line)
+        or _DALVIK_JNI_THREAD_LINE.search(line)
+        or _DALVIK_NATIVE_THREAD_LINE.search(line)
+        or _LOG_FATAL_LINE.search(line)
+        or _TRACE_LINE.match(line)
+        or _DEBUG_TRACE_LINE.match(line)):
+      useful_log.append(line)
+      continue
+
+    if code_line.match(line):
+      # Code lines should be ignored. If this were excluded the 'code around'
+      # sections would trigger value_line matches.
+      continue
+    if _VALUE_LINE.match(line):
+      useful_log.append(line)
+  return useful_log
+
+def ResolveCrashSymbol(lines, more_info):
+  """Convert unicode strings which contains native crash to a stack
+  """
 
   trace_lines = []
   value_lines = []
@@ -134,15 +189,14 @@
   # from the log and call symbol.SymbolInformation so that the results are
   # cached in the following lookups.
   code_addresses = {}
-  for ln in lines:
-    line = unicode(ln, errors='ignore')
+  for line in lines:
     lib, address = None, None
 
-    match = trace_line.match(line) or debug_trace_line.match(line)
+    match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
     if match:
       address, lib = match.group('address', 'lib')
 
-    match = value_line.match(line)
+    match = _VALUE_LINE.match(line)
     if match and not code_line.match(line):
       (_0, _1, address, lib, _2, _3) = match.groups()
 
@@ -153,17 +207,16 @@
     symbol.SymbolInformationForSet(
         symbol.TranslateLibPath(lib), code_addresses[lib], more_info)
 
-  for ln in lines:
+  for line in lines:
     # AndroidFeedback adds zero width spaces into its crash reports. These
     # should be removed or the regular expresssions will fail to match.
-    line = unicode(ln, errors='ignore')
-    process_header = process_info_line.search(line)
-    signal_header = signal_line.search(line)
-    register_header = register_line.search(line)
-    thread_header = thread_line.search(line)
-    dalvik_jni_thread_header = dalvik_jni_thread_line.search(line)
-    dalvik_native_thread_header = dalvik_native_thread_line.search(line)
-    log_fatal_header = log_fatal_line.search(line)
+    process_header = _PROCESS_INFO_LINE.search(line)
+    signal_header = _SIGNAL_LINE.search(line)
+    register_header = _REGISTER_LINE.search(line)
+    thread_header = _THREAD_LINE.search(line)
+    dalvik_jni_thread_header = _DALVIK_JNI_THREAD_LINE.search(line)
+    dalvik_native_thread_header = _DALVIK_NATIVE_THREAD_LINE.search(line)
+    log_fatal_header = _LOG_FATAL_LINE.search(line)
     if (process_header or signal_header or register_header or thread_header or
         dalvik_jni_thread_header or dalvik_native_thread_header or
         log_fatal_header) :
@@ -189,7 +242,7 @@
         print log_fatal_header.group(1)
       continue
 
-    match = trace_line.match(line) or debug_trace_line.match(line)
+    match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
     if match:
       frame, code_addr, area, symbol_present, symbol_name = match.group(
           'frame', 'address', 'lib', 'symbol_present', 'symbol_name')
@@ -203,7 +256,7 @@
       last_frame = frame
 
       if area == UNKNOWN or area == HEAP or area == STACK:
-        trace_lines.append((code_addr, "", area))
+        trace_lines.append((code_addr, '', area))
       else:
         logging.debug('Identified lib: %s' % area)
         # If a calls b which further calls c and c is inlined to b, we want to
@@ -221,22 +274,18 @@
             source_location = area
           if nest_count > 0:
             nest_count = nest_count - 1
-            trace_lines.append(("v------>", source_symbol, source_location))
+            trace_lines.append(('v------>', source_symbol, source_location))
           else:
             if not object_symbol_with_offset:
               object_symbol_with_offset = source_symbol
             trace_lines.append((code_addr,
                                 object_symbol_with_offset,
                                 source_location))
-    if code_line.match(line):
-      # Code lines should be ignored. If this were exluded the 'code around'
-      # sections would trigger value_line matches.
-      continue;
-    match = value_line.match(line)
+    match = _VALUE_LINE.match(line)
     if match:
       (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
       if area == UNKNOWN or area == HEAP or area == STACK or not area:
-        value_lines.append((addr, value, "", area))
+        value_lines.append((addr, value, '', area))
       else:
         info = symbol.SymbolInformation(area, value, more_info)
         (source_symbol, source_location, object_symbol_with_offset) = info.pop()
@@ -255,3 +304,5 @@
                             source_location))
 
   PrintOutput(trace_lines, value_lines, more_info)
+
+
diff --git a/third_party/closure_compiler/externs/networking_private.js b/third_party/closure_compiler/externs/networking_private.js
index cc9e8b6..aebf7f9 100644
--- a/third_party/closure_compiler/externs/networking_private.js
+++ b/third_party/closure_compiler/externs/networking_private.js
@@ -731,6 +731,7 @@
 
 /**
  * @typedef {{
+ *   AutoConnect: (boolean|undefined),
  *   Authentication: (string|undefined),
  *   EAP: (!chrome.networkingPrivate.EAPProperties|undefined)
  * }}
@@ -740,6 +741,7 @@
 
 /**
  * @typedef {{
+ *   AutoConnect: (!chrome.networkingPrivate.ManagedBoolean|undefined),
  *   Authentication: (!chrome.networkingPrivate.ManagedDOMString|undefined),
  *   EAP: (!chrome.networkingPrivate.ManagedEAPProperties|undefined)
  * }}
@@ -1308,5 +1310,3 @@
  * @see https://developer.chrome.com/extensions/networkingPrivate#event-onPortalDetectionCompleted
  */
 chrome.networkingPrivate.onPortalDetectionCompleted;
-
-
diff --git a/third_party/libvpx_new/README.chromium b/third_party/libvpx_new/README.chromium
index 0404d6c..609a484 100644
--- a/third_party/libvpx_new/README.chromium
+++ b/third_party/libvpx_new/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Tuesday September 29 2015
+Date: Tuesday October 06 2015
 Branch: master
-Commit: 7d28d12ef34f6cbb6b1e18f3b23b71392fd3ddf5
+Commit: ce3f4ade670cf02e05998f4ca50e08736802f5e7
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx_new/libvpx_srcs.gni b/third_party/libvpx_new/libvpx_srcs.gni
index 63685cb..1b70acf3 100644
--- a/third_party/libvpx_new/libvpx_srcs.gni
+++ b/third_party/libvpx_new/libvpx_srcs.gni
@@ -49,6 +49,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -393,7 +394,6 @@
 ]
 libvpx_srcs_x86_sse2 = [
   "//third_party/libvpx_new/source/libvpx/vp8/common/x86/idct_blk_sse2.c",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/x86/recon_wrapper_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/denoising_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/vp8_enc_stubs_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/vp8_quantize_sse2.c",
@@ -474,6 +474,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -825,7 +826,6 @@
 ]
 libvpx_srcs_x86_64_sse2 = [
   "//third_party/libvpx_new/source/libvpx/vp8/common/x86/idct_blk_sse2.c",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/x86/recon_wrapper_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/denoising_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/vp8_enc_stubs_sse2.c",
   "//third_party/libvpx_new/source/libvpx/vp8/encoder/x86/vp8_quantize_sse2.c",
@@ -912,6 +912,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -1200,7 +1201,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/dequantize_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/filter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/idct_v6.asm",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/intra4x4_predict_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/iwalsh_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/loopfilter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/simpleloopfilter_v6.asm",
@@ -1235,7 +1235,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/mbloopfilter_neon.c",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/reconintra_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/shortidct4x4llm_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/sixtappredict_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/vp8_loopfilter_neon.c",
@@ -1282,6 +1281,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -1590,7 +1590,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/dequantize_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/filter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/idct_v6.asm",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/intra4x4_predict_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/iwalsh_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/loopfilter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/simpleloopfilter_v6.asm",
@@ -1674,6 +1673,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -1962,7 +1962,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/dequantize_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/filter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/idct_v6.asm",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/intra4x4_predict_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/iwalsh_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/loopfilter_v6.asm",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/armv6/simpleloopfilter_v6.asm",
@@ -2007,7 +2006,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/mbloopfilter_neon.c",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/reconintra_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/shortidct4x4llm_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/sixtappredict_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/vp8_loopfilter_neon.c",
@@ -2050,7 +2048,6 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/mbloopfilter_neon.c",
-  "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/reconintra_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/shortidct4x4llm_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/sixtappredict_neon.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/arm/neon/vp8_loopfilter_neon.c",
@@ -2097,6 +2094,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -2456,6 +2454,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -2779,6 +2778,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
@@ -3100,6 +3100,7 @@
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconinter.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.c",
+  "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.c",
   "//third_party/libvpx_new/source/libvpx/vp8/common/reconintra4x4.h",
   "//third_party/libvpx_new/source/libvpx/vp8/common/rtcd.c",
diff --git a/third_party/libvpx_new/libvpx_srcs_arm.gypi b/third_party/libvpx_new/libvpx_srcs_arm.gypi
index a5b97bd2..56de0933 100644
--- a/third_party/libvpx_new/libvpx_srcs_arm.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_arm.gypi
@@ -17,7 +17,6 @@
     '<(libvpx_source)/vp8/common/arm/armv6/filter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_blk_v6.c',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_v6.asm',
-    '<(libvpx_source)/vp8/common/arm/armv6/intra4x4_predict_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/iwalsh_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/loopfilter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/simpleloopfilter_v6.asm',
@@ -70,6 +69,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_arm64.gypi b/third_party/libvpx_new/libvpx_srcs_arm64.gypi
index 7d88bc5..28199b37 100644
--- a/third_party/libvpx_new/libvpx_srcs_arm64.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_arm64.gypi
@@ -22,7 +22,6 @@
     '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/mbloopfilter_neon.c',
-    '<(libvpx_source)/vp8/common/arm/neon/reconintra_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/shortidct4x4llm_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/sixtappredict_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/vp8_loopfilter_neon.c',
@@ -69,6 +68,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_arm_neon.gypi b/third_party/libvpx_new/libvpx_srcs_arm_neon.gypi
index 0e7d48d..0a9803f 100644
--- a/third_party/libvpx_new/libvpx_srcs_arm_neon.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_arm_neon.gypi
@@ -17,7 +17,6 @@
     '<(libvpx_source)/vp8/common/arm/armv6/filter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_blk_v6.c',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_v6.asm',
-    '<(libvpx_source)/vp8/common/arm/armv6/intra4x4_predict_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/iwalsh_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/loopfilter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/simpleloopfilter_v6.asm',
@@ -39,7 +38,6 @@
     '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/mbloopfilter_neon.c',
-    '<(libvpx_source)/vp8/common/arm/neon/reconintra_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/shortidct4x4llm_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/sixtappredict_neon.c',
     '<(libvpx_source)/vp8/common/arm/neon/vp8_loopfilter_neon.c',
@@ -86,6 +84,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect.gypi b/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect.gypi
index a5b97bd2..56de0933 100644
--- a/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect.gypi
@@ -17,7 +17,6 @@
     '<(libvpx_source)/vp8/common/arm/armv6/filter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_blk_v6.c',
     '<(libvpx_source)/vp8/common/arm/armv6/idct_v6.asm',
-    '<(libvpx_source)/vp8/common/arm/armv6/intra4x4_predict_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/iwalsh_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/loopfilter_v6.asm',
     '<(libvpx_source)/vp8/common/arm/armv6/simpleloopfilter_v6.asm',
@@ -70,6 +69,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect_intrinsics.gypi b/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect_intrinsics.gypi
index 918dc3d..dcf6043 100644
--- a/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect_intrinsics.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_arm_neon_cpu_detect_intrinsics.gypi
@@ -25,7 +25,6 @@
         '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c',
         '<(libvpx_source)/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c',
         '<(libvpx_source)/vp8/common/arm/neon/mbloopfilter_neon.c',
-        '<(libvpx_source)/vp8/common/arm/neon/reconintra_neon.c',
         '<(libvpx_source)/vp8/common/arm/neon/shortidct4x4llm_neon.c',
         '<(libvpx_source)/vp8/common/arm/neon/sixtappredict_neon.c',
         '<(libvpx_source)/vp8/common/arm/neon/vp8_loopfilter_neon.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_generic.gypi b/third_party/libvpx_new/libvpx_srcs_generic.gypi
index fbfe3f7..6ad20965 100644
--- a/third_party/libvpx_new/libvpx_srcs_generic.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_generic.gypi
@@ -50,6 +50,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_mips.gypi b/third_party/libvpx_new/libvpx_srcs_mips.gypi
index 6ec3373..ec42587 100644
--- a/third_party/libvpx_new/libvpx_srcs_mips.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_mips.gypi
@@ -50,6 +50,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_nacl.gypi b/third_party/libvpx_new/libvpx_srcs_nacl.gypi
index fbfe3f7..6ad20965 100644
--- a/third_party/libvpx_new/libvpx_srcs_nacl.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_nacl.gypi
@@ -50,6 +50,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_x86.gypi b/third_party/libvpx_new/libvpx_srcs_x86.gypi
index ffd6280..1616574 100644
--- a/third_party/libvpx_new/libvpx_srcs_x86.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_x86.gypi
@@ -50,6 +50,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_x86_64.gypi b/third_party/libvpx_new/libvpx_srcs_x86_64.gypi
index 18b5a02..bce3da4 100644
--- a/third_party/libvpx_new/libvpx_srcs_x86_64.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_x86_64.gypi
@@ -50,6 +50,7 @@
     '<(libvpx_source)/vp8/common/reconinter.c',
     '<(libvpx_source)/vp8/common/reconinter.h',
     '<(libvpx_source)/vp8/common/reconintra.c',
+    '<(libvpx_source)/vp8/common/reconintra.h',
     '<(libvpx_source)/vp8/common/reconintra4x4.c',
     '<(libvpx_source)/vp8/common/reconintra4x4.h',
     '<(libvpx_source)/vp8/common/rtcd.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_x86_64_intrinsics.gypi b/third_party/libvpx_new/libvpx_srcs_x86_64_intrinsics.gypi
index 95fa0e7..881194e 100644
--- a/third_party/libvpx_new/libvpx_srcs_x86_64_intrinsics.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_x86_64_intrinsics.gypi
@@ -29,7 +29,6 @@
       ],
       'sources': [
         '<(libvpx_source)/vp8/common/x86/idct_blk_sse2.c',
-        '<(libvpx_source)/vp8/common/x86/recon_wrapper_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/denoising_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/vp8_enc_stubs_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/vp8_quantize_sse2.c',
diff --git a/third_party/libvpx_new/libvpx_srcs_x86_intrinsics.gypi b/third_party/libvpx_new/libvpx_srcs_x86_intrinsics.gypi
index 95fa0e7..881194e 100644
--- a/third_party/libvpx_new/libvpx_srcs_x86_intrinsics.gypi
+++ b/third_party/libvpx_new/libvpx_srcs_x86_intrinsics.gypi
@@ -29,7 +29,6 @@
       ],
       'sources': [
         '<(libvpx_source)/vp8/common/x86/idct_blk_sse2.c',
-        '<(libvpx_source)/vp8/common/x86/recon_wrapper_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/denoising_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/vp8_enc_stubs_sse2.c',
         '<(libvpx_source)/vp8/encoder/x86/vp8_quantize_sse2.c',
diff --git a/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vp8_rtcd.h
index a7c415d..c8b17eca 100644
--- a/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vp8_rtcd.h
@@ -57,14 +57,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_neon(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_neon(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -135,10 +127,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-void vp8_intra4x4_predict_armv6(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_armv6
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_armv6(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_neon(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -268,10 +256,6 @@
     if (flags & HAS_NEON) vp8_bilinear_predict8x4 = vp8_bilinear_predict8x4_neon;
     vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_armv6;
     if (flags & HAS_NEON) vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_neon;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_c;
-    if (flags & HAS_NEON) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_neon;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_c;
-    if (flags & HAS_NEON) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_neon;
     vp8_copy_mem16x16 = vp8_copy_mem16x16_v6;
     if (flags & HAS_NEON) vp8_copy_mem16x16 = vp8_copy_mem16x16_neon;
     vp8_copy_mem8x4 = vp8_copy_mem8x4_v6;
diff --git a/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
index 64fce3a..9919f64 100644
--- a/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
@@ -118,6 +118,9 @@
 void vpx_d45_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -130,6 +133,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_dc_128_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -254,6 +260,9 @@
 void vpx_h_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_neon(const tran_low_t *input, uint8_t *dest, int dest_stride);
 RTCD_EXTERN void (*vpx_idct16x16_10_add)(const tran_low_t *input, uint8_t *dest, int dest_stride);
@@ -743,6 +752,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_media(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_media
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/arm-neon/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/arm-neon/vp8_rtcd.h
index 17d17eb..4fba7a7 100644
--- a/third_party/libvpx_new/source/config/linux/arm-neon/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm-neon/vp8_rtcd.h
@@ -57,14 +57,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_neon(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_neon
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_neon(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_neon
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -135,10 +127,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-void vp8_intra4x4_predict_armv6(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_armv6
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_armv6(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_neon(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
diff --git a/third_party/libvpx_new/source/config/linux/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/arm-neon/vpx_dsp_rtcd.h
index 4de075d..b618558a 100644
--- a/third_party/libvpx_new/source/config/linux/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm-neon/vpx_dsp_rtcd.h
@@ -118,6 +118,9 @@
 void vpx_d45_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_neon
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -130,6 +133,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_neon
@@ -254,6 +260,9 @@
 void vpx_h_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_neon
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_neon(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_neon
@@ -743,6 +752,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_media(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_media
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/arm/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/arm/vp8_rtcd.h
index 6cd65fb..20783d4 100644
--- a/third_party/libvpx_new/source/config/linux/arm/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm/vp8_rtcd.h
@@ -54,12 +54,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_c
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_c
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -119,10 +113,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-void vp8_intra4x4_predict_armv6(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_armv6
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_armv6(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_armv6
diff --git a/third_party/libvpx_new/source/config/linux/arm/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/arm/vpx_dsp_rtcd.h
index bb570a0..635357f 100644
--- a/third_party/libvpx_new/source/config/linux/arm/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm/vpx_dsp_rtcd.h
@@ -106,6 +106,9 @@
 void vpx_d45_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_c
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -118,6 +121,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_c
 
@@ -217,6 +223,9 @@
 void vpx_h_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_c
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_c
 
@@ -652,6 +661,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_media(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_media
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/arm64/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/arm64/vp8_rtcd.h
index 74456fe0..0d421b6 100644
--- a/third_party/libvpx_new/source/config/linux/arm64/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm64/vp8_rtcd.h
@@ -53,14 +53,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_neon(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_neon
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_neon(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_neon
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -123,9 +115,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_neon(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_neon
diff --git a/third_party/libvpx_new/source/config/linux/arm64/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/arm64/vpx_dsp_rtcd.h
index 2cac9e6..f93276b0 100644
--- a/third_party/libvpx_new/source/config/linux/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/arm64/vpx_dsp_rtcd.h
@@ -118,6 +118,9 @@
 void vpx_d45_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_neon
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -130,6 +133,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_neon
@@ -254,6 +260,9 @@
 void vpx_h_predictor_8x8_neon(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_neon
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_neon(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_neon
@@ -728,6 +737,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_c(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_c
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/generic/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/generic/vp8_rtcd.h
index dc2834b..1697a30 100644
--- a/third_party/libvpx_new/source/config/linux/generic/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/generic/vp8_rtcd.h
@@ -50,12 +50,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_c
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_c
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -107,9 +101,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_c
 
diff --git a/third_party/libvpx_new/source/config/linux/generic/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/generic/vpx_dsp_rtcd.h
index 010cbe7..14170f5 100644
--- a/third_party/libvpx_new/source/config/linux/generic/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/generic/vpx_dsp_rtcd.h
@@ -106,6 +106,9 @@
 void vpx_d45_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_c
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -118,6 +121,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_c
 
@@ -217,6 +223,9 @@
 void vpx_h_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_c
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_c
 
@@ -643,6 +652,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_c(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_c
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/ia32/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/ia32/vp8_rtcd.h
index d08f0804..affac11 100644
--- a/third_party/libvpx_new/source/config/linux/ia32/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/ia32/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 RTCD_EXTERN int (*vp8_block_error)(short *coeff, short *dqcoeff);
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 RTCD_EXTERN void (*vp8_clear_system_state)();
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -301,12 +288,6 @@
     vp8_block_error = vp8_block_error_c;
     if (flags & HAS_MMX) vp8_block_error = vp8_block_error_mmx;
     if (flags & HAS_SSE2) vp8_block_error = vp8_block_error_xmm;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_clear_system_state = vp8_clear_system_state_c;
     if (flags & HAS_MMX) vp8_clear_system_state = vpx_reset_mmx_state;
     vp8_copy32xn = vp8_copy32xn_c;
diff --git a/third_party/libvpx_new/source/config/linux/ia32/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/ia32/vpx_dsp_rtcd.h
index 8f0da9d..f529afb 100644
--- a/third_party/libvpx_new/source/config/linux/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/ia32/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_dc_128_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -287,6 +293,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 RTCD_EXTERN void (*vpx_idct16x16_10_add)(const tran_low_t *input, uint8_t *dest, int dest_stride);
@@ -911,6 +920,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 RTCD_EXTERN uint32_t (*vpx_variance_halfpixvar16x16_v)(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/libvpx_new/source/config/linux/mips64el/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/mips64el/vp8_rtcd.h
index a023cfa..b7ee61ada 100644
--- a/third_party/libvpx_new/source/config/linux/mips64el/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/mips64el/vp8_rtcd.h
@@ -50,12 +50,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_c
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_c
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -107,9 +101,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_c
 
diff --git a/third_party/libvpx_new/source/config/linux/mips64el/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/mips64el/vpx_dsp_rtcd.h
index 010cbe7..14170f5 100644
--- a/third_party/libvpx_new/source/config/linux/mips64el/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/mips64el/vpx_dsp_rtcd.h
@@ -106,6 +106,9 @@
 void vpx_d45_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_c
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -118,6 +121,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_c
 
@@ -217,6 +223,9 @@
 void vpx_h_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_c
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_c
 
@@ -643,6 +652,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_c(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_c
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/mipsel/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/mipsel/vp8_rtcd.h
index a023cfa..b7ee61ada 100644
--- a/third_party/libvpx_new/source/config/linux/mipsel/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/mipsel/vp8_rtcd.h
@@ -50,12 +50,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_c
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_c
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -107,9 +101,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_c
 
diff --git a/third_party/libvpx_new/source/config/linux/mipsel/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/mipsel/vpx_dsp_rtcd.h
index 010cbe7..14170f5 100644
--- a/third_party/libvpx_new/source/config/linux/mipsel/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/mipsel/vpx_dsp_rtcd.h
@@ -106,6 +106,9 @@
 void vpx_d45_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_c
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -118,6 +121,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_c
 
@@ -217,6 +223,9 @@
 void vpx_h_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_c
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_c
 
@@ -643,6 +652,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_c(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_c
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/linux/x64/vp8_rtcd.h b/third_party/libvpx_new/source/config/linux/x64/vp8_rtcd.h
index ff9ac9f..7ff11f92 100644
--- a/third_party/libvpx_new/source/config/linux/x64/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/x64/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_xmm
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 #define vp8_clear_system_state vpx_reset_mmx_state
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -290,10 +277,6 @@
     if (flags & HAS_SSSE3) vp8_bilinear_predict16x16 = vp8_bilinear_predict16x16_ssse3;
     vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_sse2;
     if (flags & HAS_SSSE3) vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_ssse3;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_copy32xn = vp8_copy32xn_sse2;
     if (flags & HAS_SSE3) vp8_copy32xn = vp8_copy32xn_sse3;
     vp8_diamond_search_sad = vp8_diamond_search_sad_c;
diff --git a/third_party/libvpx_new/source/config/linux/x64/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/linux/x64/vpx_dsp_rtcd.h
index 7cf74d2..a2da40af 100644
--- a/third_party/libvpx_new/source/config/linux/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/linux/x64/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_sse2
@@ -288,6 +294,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_sse2
@@ -917,6 +926,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_sse2
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/libvpx_new/source/config/mac/ia32/vp8_rtcd.h b/third_party/libvpx_new/source/config/mac/ia32/vp8_rtcd.h
index d08f0804..affac11 100644
--- a/third_party/libvpx_new/source/config/mac/ia32/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/mac/ia32/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 RTCD_EXTERN int (*vp8_block_error)(short *coeff, short *dqcoeff);
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 RTCD_EXTERN void (*vp8_clear_system_state)();
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -301,12 +288,6 @@
     vp8_block_error = vp8_block_error_c;
     if (flags & HAS_MMX) vp8_block_error = vp8_block_error_mmx;
     if (flags & HAS_SSE2) vp8_block_error = vp8_block_error_xmm;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_clear_system_state = vp8_clear_system_state_c;
     if (flags & HAS_MMX) vp8_clear_system_state = vpx_reset_mmx_state;
     vp8_copy32xn = vp8_copy32xn_c;
diff --git a/third_party/libvpx_new/source/config/mac/ia32/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/mac/ia32/vpx_dsp_rtcd.h
index 8f0da9d..f529afb 100644
--- a/third_party/libvpx_new/source/config/mac/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/mac/ia32/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_dc_128_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -287,6 +293,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 RTCD_EXTERN void (*vpx_idct16x16_10_add)(const tran_low_t *input, uint8_t *dest, int dest_stride);
@@ -911,6 +920,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 RTCD_EXTERN uint32_t (*vpx_variance_halfpixvar16x16_v)(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/libvpx_new/source/config/mac/x64/vp8_rtcd.h b/third_party/libvpx_new/source/config/mac/x64/vp8_rtcd.h
index ff9ac9f..7ff11f92 100644
--- a/third_party/libvpx_new/source/config/mac/x64/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/mac/x64/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_xmm
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 #define vp8_clear_system_state vpx_reset_mmx_state
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -290,10 +277,6 @@
     if (flags & HAS_SSSE3) vp8_bilinear_predict16x16 = vp8_bilinear_predict16x16_ssse3;
     vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_sse2;
     if (flags & HAS_SSSE3) vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_ssse3;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_copy32xn = vp8_copy32xn_sse2;
     if (flags & HAS_SSE3) vp8_copy32xn = vp8_copy32xn_sse3;
     vp8_diamond_search_sad = vp8_diamond_search_sad_c;
diff --git a/third_party/libvpx_new/source/config/mac/x64/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/mac/x64/vpx_dsp_rtcd.h
index 7cf74d2..a2da40af 100644
--- a/third_party/libvpx_new/source/config/mac/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/mac/x64/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_sse2
@@ -288,6 +294,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_sse2
@@ -917,6 +926,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_sse2
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/libvpx_new/source/config/nacl/vp8_rtcd.h b/third_party/libvpx_new/source/config/nacl/vp8_rtcd.h
index dc2834b..1697a30 100644
--- a/third_party/libvpx_new/source/config/nacl/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/nacl/vp8_rtcd.h
@@ -50,12 +50,6 @@
 int vp8_block_error_c(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_c
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-#define vp8_build_intra_predictors_mbuv_s vp8_build_intra_predictors_mbuv_s_c
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-#define vp8_build_intra_predictors_mby_s vp8_build_intra_predictors_mby_s_c
-
 void vp8_clear_system_state_c();
 #define vp8_clear_system_state vp8_clear_system_state_c
 
@@ -107,9 +101,6 @@
 int vp8_full_search_sad_c(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 #define vp8_full_search_sad vp8_full_search_sad_c
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 #define vp8_loop_filter_bh vp8_loop_filter_bh_c
 
diff --git a/third_party/libvpx_new/source/config/nacl/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/nacl/vpx_dsp_rtcd.h
index 010cbe7..14170f5 100644
--- a/third_party/libvpx_new/source/config/nacl/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/nacl/vpx_dsp_rtcd.h
@@ -106,6 +106,9 @@
 void vpx_d45_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d45_predictor_8x8 vpx_d45_predictor_8x8_c
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_16x16 vpx_d63_predictor_16x16_c
 
@@ -118,6 +121,9 @@
 void vpx_d63_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_d63_predictor_8x8 vpx_d63_predictor_8x8_c
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_c
 
@@ -217,6 +223,9 @@
 void vpx_h_predictor_8x8_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_h_predictor_8x8 vpx_h_predictor_8x8_c
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_c
 
@@ -643,6 +652,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_c(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_c
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #include "vpx_config.h"
diff --git a/third_party/libvpx_new/source/config/vpx_version.h b/third_party/libvpx_new/source/config/vpx_version.h
index 2752f2d..cf95c13 100644
--- a/third_party/libvpx_new/source/config/vpx_version.h
+++ b/third_party/libvpx_new/source/config/vpx_version.h
@@ -1,7 +1,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  4
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "1410-g7d28d12"
+#define VERSION_EXTRA  "1466-gce3f4ad"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.4.0-1410-g7d28d12"
-#define VERSION_STRING      " v1.4.0-1410-g7d28d12"
+#define VERSION_STRING_NOSP "v1.4.0-1466-gce3f4ad"
+#define VERSION_STRING      " v1.4.0-1466-gce3f4ad"
diff --git a/third_party/libvpx_new/source/config/win/ia32/vp8_rtcd.h b/third_party/libvpx_new/source/config/win/ia32/vp8_rtcd.h
index d08f0804..affac11 100644
--- a/third_party/libvpx_new/source/config/win/ia32/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/win/ia32/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 RTCD_EXTERN int (*vp8_block_error)(short *coeff, short *dqcoeff);
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 RTCD_EXTERN void (*vp8_clear_system_state)();
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -301,12 +288,6 @@
     vp8_block_error = vp8_block_error_c;
     if (flags & HAS_MMX) vp8_block_error = vp8_block_error_mmx;
     if (flags & HAS_SSE2) vp8_block_error = vp8_block_error_xmm;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_c;
-    if (flags & HAS_SSE2) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_clear_system_state = vp8_clear_system_state_c;
     if (flags & HAS_MMX) vp8_clear_system_state = vpx_reset_mmx_state;
     vp8_copy32xn = vp8_copy32xn_c;
diff --git a/third_party/libvpx_new/source/config/win/ia32/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/win/ia32/vpx_dsp_rtcd.h
index 8f0da9d..f529afb 100644
--- a/third_party/libvpx_new/source/config/win/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/win/ia32/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_dc_128_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -287,6 +293,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 RTCD_EXTERN void (*vpx_idct16x16_10_add)(const tran_low_t *input, uint8_t *dest, int dest_stride);
@@ -911,6 +920,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 RTCD_EXTERN uint32_t (*vpx_variance_halfpixvar16x16_v)(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/libvpx_new/source/config/win/x64/vp8_rtcd.h b/third_party/libvpx_new/source/config/win/x64/vp8_rtcd.h
index ff9ac9f..7ff11f92 100644
--- a/third_party/libvpx_new/source/config/win/x64/vp8_rtcd.h
+++ b/third_party/libvpx_new/source/config/win/x64/vp8_rtcd.h
@@ -60,16 +60,6 @@
 int vp8_block_error_xmm(short *coeff, short *dqcoeff);
 #define vp8_block_error vp8_block_error_xmm
 
-void vp8_build_intra_predictors_mbuv_s_c(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_sse2(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-void vp8_build_intra_predictors_mbuv_s_ssse3(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mbuv_s)(struct macroblockd *x, unsigned char * uabove_row, unsigned char * vabove_row,  unsigned char *uleft, unsigned char *vleft, int left_stride, unsigned char * upred_ptr, unsigned char * vpred_ptr, int pred_stride);
-
-void vp8_build_intra_predictors_mby_s_c(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_sse2(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-void vp8_build_intra_predictors_mby_s_ssse3(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-RTCD_EXTERN void (*vp8_build_intra_predictors_mby_s)(struct macroblockd *x, unsigned char * yabove_row, unsigned char * yleft, int left_stride, unsigned char * ypred_ptr, int y_stride);
-
 void vp8_clear_system_state_c();
 void vpx_reset_mmx_state();
 #define vp8_clear_system_state vpx_reset_mmx_state
@@ -147,9 +137,6 @@
 int vp8_full_search_sadx8(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 RTCD_EXTERN int (*vp8_full_search_sad)(struct macroblock *x, struct block *b, struct blockd *d, union int_mv *ref_mv, int sad_per_bit, int distance, struct variance_vtable *fn_ptr, int *mvcost[2], union int_mv *center_mv);
 
-void vp8_intra4x4_predict_c(unsigned char *Above, unsigned char *yleft, int left_stride, int b_mode, unsigned char *dst, int dst_stride, unsigned char top_left);
-#define vp8_intra4x4_predict vp8_intra4x4_predict_c
-
 void vp8_loop_filter_bh_c(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_mmx(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
 void vp8_loop_filter_bh_sse2(unsigned char *y, unsigned char *u, unsigned char *v, int ystride, int uv_stride, struct loop_filter_info *lfi);
@@ -290,10 +277,6 @@
     if (flags & HAS_SSSE3) vp8_bilinear_predict16x16 = vp8_bilinear_predict16x16_ssse3;
     vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_sse2;
     if (flags & HAS_SSSE3) vp8_bilinear_predict8x8 = vp8_bilinear_predict8x8_ssse3;
-    vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mbuv_s = vp8_build_intra_predictors_mbuv_s_ssse3;
-    vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_sse2;
-    if (flags & HAS_SSSE3) vp8_build_intra_predictors_mby_s = vp8_build_intra_predictors_mby_s_ssse3;
     vp8_copy32xn = vp8_copy32xn_sse2;
     if (flags & HAS_SSE3) vp8_copy32xn = vp8_copy32xn_sse3;
     vp8_diamond_search_sad = vp8_diamond_search_sad_c;
diff --git a/third_party/libvpx_new/source/config/win/x64/vpx_dsp_rtcd.h b/third_party/libvpx_new/source/config/win/x64/vpx_dsp_rtcd.h
index 7cf74d2..a2da40af 100644
--- a/third_party/libvpx_new/source/config/win/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx_new/source/config/win/x64/vpx_dsp_rtcd.h
@@ -135,6 +135,9 @@
 void vpx_d45_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d45_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d45e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d45e_predictor_4x4 vpx_d45e_predictor_4x4_c
+
 void vpx_d63_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_d63_predictor_16x16_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_16x16)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
@@ -151,6 +154,9 @@
 void vpx_d63_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_d63_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_d63e_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_d63e_predictor_4x4 vpx_d63e_predictor_4x4_c
+
 void vpx_dc_128_predictor_16x16_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 void vpx_dc_128_predictor_16x16_sse2(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 #define vpx_dc_128_predictor_16x16 vpx_dc_128_predictor_16x16_sse2
@@ -288,6 +294,9 @@
 void vpx_h_predictor_8x8_ssse3(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 RTCD_EXTERN void (*vpx_h_predictor_8x8)(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
 
+void vpx_he_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_he_predictor_4x4 vpx_he_predictor_4x4_c
+
 void vpx_idct16x16_10_add_c(const tran_low_t *input, uint8_t *dest, int dest_stride);
 void vpx_idct16x16_10_add_sse2(const tran_low_t *input, uint8_t *dest, int dest_stride);
 #define vpx_idct16x16_10_add vpx_idct16x16_10_add_sse2
@@ -917,6 +926,9 @@
 uint32_t vpx_variance_halfpixvar16x16_v_sse2(const unsigned char *src_ptr, int source_stride, const unsigned char *ref_ptr, int  ref_stride, uint32_t *sse);
 #define vpx_variance_halfpixvar16x16_v vpx_variance_halfpixvar16x16_v_sse2
 
+void vpx_ve_predictor_4x4_c(uint8_t *dst, ptrdiff_t y_stride, const uint8_t *above, const uint8_t *left);
+#define vpx_ve_predictor_4x4 vpx_ve_predictor_4x4_c
+
 void vpx_dsp_rtcd(void);
 
 #ifdef RTCD_C
diff --git a/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h
index 9f3788cb..ef8b025 100644
--- a/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h
+++ b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_extension_autogen.h
@@ -327,6 +327,15 @@
               (current, other))
 VISIT_GL_CALL(InsertSyncPointCHROMIUM, GLuint, (), ())
 VISIT_GL_CALL(WaitSyncPointCHROMIUM, void, (GLuint sync_point), (sync_point))
+VISIT_GL_CALL(InsertFenceSyncCHROMIUM, GLuint64, (), ())
+VISIT_GL_CALL(GenSyncTokenCHROMIUM,
+              void,
+              (GLuint64 fence_sync, GLbyte* sync_token),
+              (fence_sync, sync_token))
+VISIT_GL_CALL(WaitSyncTokenCHROMIUM,
+              void,
+              (const GLbyte* sync_token),
+              (sync_token))
 VISIT_GL_CALL(DrawBuffersEXT,
               void,
               (GLsizei count, const GLenum* bufs),
diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
index b90bcff..ddb63d7a 100644
--- a/third_party/zlib/README.chromium
+++ b/third_party/zlib/README.chromium
@@ -15,6 +15,7 @@
 - Added 'int z_errno' global for WinCE, to which 'errno' is defined in zutil.h.
 - Added 'mozzconf.h' to mangle the function names.
 - Added logic in zlib.h to undef our earlier mangles when defaulting to 64 bit offset versions of API.
+- Added casts to suppress VC++ warnings
 The 'google.patch' file represents our changes from the original zlib-1.2.5.
 
 A more significant change to support mixed-source data compression. See
diff --git a/third_party/zlib/crc_folding.c b/third_party/zlib/crc_folding.c
index 98c559c..48d7774 100644
--- a/third_party/zlib/crc_folding.c
+++ b/third_party/zlib/crc_folding.c
@@ -283,7 +283,7 @@
         goto partial;
     }
 
-    algn_diff = 0 - (unsigned long)src & 0xF;
+    algn_diff = 0 - (uintptr_t)src & 0xF;
     if (algn_diff) {
         xmm_crc_part = _mm_loadu_si128((__m128i *)src);
         _mm_storeu_si128((__m128i *)dst, xmm_crc_part);
diff --git a/third_party/zlib/google.patch b/third_party/zlib/google.patch
index 3818b11..c943b410 100644
--- a/third_party/zlib/google.patch
+++ b/third_party/zlib/google.patch
@@ -1,3 +1,15 @@
+diff -ru zlib-1.2.5/crc_folding.c zlib/crc_folding.c
+--- zlib-1.2.5/crc_folding.c
++++ zlib/crc_folding.c
+@@ -283,7 +283,7 @@
+         goto partial;
+     }
+
+-    algn_diff = 0 - (unsigned long)src & 0xF;
++    algn_diff = 0 - (uintptr_t)src & 0xF;
+     if (algn_diff) {
+         xmm_crc_part = _mm_loadu_si128((__m128i *)src);
+         _mm_storeu_si128((__m128i *)dst, xmm_crc_part);
 diff -ru zlib-1.2.5/gzlib.c zlib/gzlib.c
 --- zlib-1.2.5/gzlib.c
 +++ zlib/gzlib.c
diff --git a/tools/chrome_proxy/common/chrome_proxy_measurements.py b/tools/chrome_proxy/common/chrome_proxy_measurements.py
index 7cdef14d..b7f7515 100644
--- a/tools/chrome_proxy/common/chrome_proxy_measurements.py
+++ b/tools/chrome_proxy/common/chrome_proxy_measurements.py
@@ -25,11 +25,15 @@
   tab.Navigate('data:text/html;base64,%s' % base64.b64encode(
     '<html><body><script>'
     'function ProbeViaHeader(url, wanted_via) {'
-      'var xmlhttp = new XMLHttpRequest();'
-      'xmlhttp.open("HEAD",url,false);'
-      'xmlhttp.send();'
-      'var via=xmlhttp.getResponseHeader("via");'
-      'return (via && via.indexOf(wanted_via) != -1);'
+      'try {'
+        'var xmlhttp = new XMLHttpRequest();'
+        'xmlhttp.open("HEAD",url,false);'
+        'xmlhttp.send();'
+        'var via=xmlhttp.getResponseHeader("via");'
+        'return (via && via.indexOf(wanted_via) != -1);'
+       '} catch (err) {'
+         'return false;'
+       '}'
     '}'
     '</script>'
     'Waiting for Chrome to start using the DRP...'
diff --git a/tools/gn/README.md b/tools/gn/README.md
index 90cf908..f9679eb 100644
--- a/tools/gn/README.md
+++ b/tools/gn/README.md
@@ -1,7 +1,7 @@
 # What is GN?
 
 GN is a meta-build system that generates
-[NinjaBuild](https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md)
+[NinjaBuild](/chromium/src/+/master/docs/ninja_build.md)
 files. It's meant to be faster and simpler than GYP. It outputs only Ninja build
 files.
 
@@ -10,10 +10,7 @@
 1. We believe GN files are more readable and more maintainable
    than GYP files.
 2. GN is fast:
-  * GN is 20x faster than GYP (as of mid November, building 80%
-    of what GYP builds, in one configuration rather than two, takes 500ms
-    on a z620 running Ubuntu Trusty. GYP takes 20 seconds.
-    We see similar speedups on Mac and Windows).
+  * GN is 20x faster than GYP.
   * GN supports automatically re-running itself as needed by Ninja
     as part of the build. This eliminates the need to remember to
     re-run GN when you change a build file.
@@ -25,30 +22,23 @@
 
 ## What's the status?
 
-_as of 8 Feb 2015:_
+_as of 7 Oct 2015:_
 
-Chrome and most of the major test suites link on Linux and ChromeOS.
-There's probably <1000 objects left to build, in a few test suites and a
-bunch of utillities and helper binaries. We will probably have
-everything converted in another couple weeks.
+GN is now the default system for Linux, though GYP still works. It
+is mostly complete on Android, ChromeOS, and Windows (apart from NaCl
+support on Windows).
 
-Chrome also links on Android and Windows, and bringing up the remaining
-test suites should be mostly straightforward. There's some work left to
-enable NaCl on Windows but it should also be straightforward.
-
-Mac and iOS have not progressed much as attention has been focused on
-Linux and Windows; we still need support for bundles and frameworks
-before it can get to parity w/ the other platforms.
+Mac and iOS are making progress, though we still need better support
+for bundles before the major targets like Chrome can link and they get
+to parity w/ the other platforms.
 
 ## When are you going to be done?
 
-_as of 8 Feb 2015:_
+_as of 7 Oct 2015:_
 
-We're currently shooting for having the main developer configurations
-working and usable by the end of March 2015. There will probably be a
-fair amount of verification of flags, bug fixes, etc over the next
-couple months, but hopefully we will be flipping configurations from GYP
-to GN throughout Q2, targeting having everything done by the end of Q2.
+We're currently shooting for having Android, ChromeOS, and Windows
+converted over by the end of 2015, with Mac and iOS following in Q1
+of 2016.
 
 ## What does "done" mean?
 
@@ -85,21 +75,20 @@
 Check to see if your targets build under GN yet. If they don't,
 volunteer to help convert them!
 
-_17 Nov 2014. We are updating the stuff we use to track progress. Watch
-this space and chromium-dev@ for more info!_.
+Or, look at [the list of open bugs](https://code.google.com/p/chromium/issues/list?can=2&q=label:Proj-GN-Migration%20-type:Project&sort=pri&colspec=ID%20Pri%20Summary%20Type%20OS%20Owner%20Status%20Modified%20Blocking) related to the migration and see if there's anything that catches your fancy.
 
 ## I want more info on GN!
 
 Read these links:
 
-  * [Quick start](docs/quick_start.md)
-  * [FAQ](docs/faq.md)
-  * [GYP conversion cookbook](docs/cookbook.md)
-  * [Language and operation details](docs/language.md)
-  * [Reference](docs/reference.md) The built-in `gn help` documentation.
-  * [Style guide](docs/style_guide.md)
-  * [Cross compiling and toolchains](docs/cross_compiles.md)
-  * [Hacking on GN itself](docs/hacking.md)
-  * [GNStandalone](docs/standalone.md) Standalone GN projects
-  * [UpdateGNBinaries](docs/update_binaries.md) Pushing new binaries
-  * [Check](docs/check.md) `gn check` command reference
+  * [Quick start](/chromium/src/+/master/tools/gn/docs/quick_start.md)
+  * [FAQ](/chromium/src/+/master/tools/gn/docs/faq.md)
+  * [GYP conversion cookbook](/chromium/src/+/master/tools/gn/docs/cookbook.md)
+  * [Language and operation details](/chromium/src/+/master/tools/gn/docs/language.md)
+  * [Reference](/chromium/src/+/master/tools/gn/docs/reference.md) The built-in `gn help` documentation.
+  * [Style guide](/chromium/src/+/master/tools/gn/docs/style_guide.md)
+  * [Cross compiling and toolchains](/chromium/src/+/master/tools/gn/docs/cross_compiles.md)
+  * [Hacking on GN itself](/chromium/src/+/master/tools/gn/docs/hacking.md)
+  * [GNStandalone](/chromium/src/+/master/tools/gn/docs/standalone.md) Standalone GN projects
+  * [UpdateGNBinaries](/chromium/src/+/master/tools/gn/docs/update_binaries.md) Pushing new binaries
+  * [Check](/chromium/src/+/master/tools/gn/docs/check.md) `gn check` command reference
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 92456c8..1367aef4 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -115,6 +115,15 @@
   "content/app/strings/content_strings.grd": {
     "messages": [18900],
   },
+  # Settings Chromium strings and Settings Google Chrome strings must start
+  # at the same id.  We only use one file depending on whether we're building
+  # Chromium or Google Chrome.
+  "chrome/app/settings_chromium_strings.grd": {
+    "messages": [19700],
+  },
+  "chrome/app/settings_google_chrome_strings.grd": {
+    "messages": [19700],
+  },
   "chrome/app/settings_strings.grd": {
     "messages": [20000],
   },
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index 0b37063..a64d7c2 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -64,6 +64,8 @@
     "chrome/app/resources/locale_settings_mac.grd": "Not UI strings; localized separately",
     "chrome/app/resources/locale_settings_win.grd": "Not UI strings; localized separately",
     "chrome/app/settings_strings.grd": "Work in progress; to be localized later in development (late 2015)",
+    "chrome/app/settings_chromium_strings.grd": "Work in progress; to be localized later in development (late 2015)",
+    "chrome/app/settings_google_chrome_strings.grd": "Work in progress; to be localized later in development (late 2015)",
     "chromecast/app/resources/chromecast_settings.grd": "Not UI strings; localized separately",
     "cloud_print/service/win/service_resources.grd": "Separate release process",
     "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": "Separate release process",
diff --git a/tools/md_browser/md_browser.py b/tools/md_browser/md_browser.py
index 7fb51eb2..9baa84bf8 100644
--- a/tools/md_browser/md_browser.py
+++ b/tools/md_browser/md_browser.py
@@ -49,27 +49,34 @@
 
 class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
   def do_GET(self):
-    full_path = os.path.abspath(os.path.join(self.server.top_level,
-                                             self.path[1:]))
+    path = self.path
+
+    # strip off the repo and branch info, if present, for compatibility
+    # with gitiles.
+    if path.startswith('/chromium/src/+/master'):
+      path = path[len('/chromium/src/+/master'):]
+
+    full_path = os.path.abspath(os.path.join(self.server.top_level, path[1:]))
+
     if not full_path.startswith(SRC_DIR):
       self._DoUnknown()
-    elif self.path == '/doc.css':
+    elif path == '/doc.css':
       self._WriteTemplate('doc.css')
     elif not os.path.exists(full_path):
       self._DoNotFound()
-    elif self.path.lower().endswith('.md'):
-      self._DoMD()
+    elif path.lower().endswith('.md'):
+      self._DoMD(path)
     else:
       self._DoUnknown()
 
-  def _DoMD(self):
+  def _DoMD(self, path):
     extensions = [
         'markdown.extensions.fenced_code',
         'markdown.extensions.tables',
         'markdown.extensions.toc',
     ]
 
-    contents = self._Read(self.path[1:])
+    contents = self._Read(path[1:])
     md_fragment = markdown.markdown(contents,
                                     extensions=extensions,
                                     output_format='html4').encode('utf-8')
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6521f84..cf6d33a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -43262,6 +43262,32 @@
   <summary>Usage of the Software Removal Tool (SRT) Prompt.</summary>
 </histogram>
 
+<histogram name="SoftwareReporter.RunningTime" units="ms">
+  <owner>alito@chromium.org</owner>
+  <summary>
+    The amount of time it took to run the software reporter tool as reported by
+    the tool itself via the registry. Logged just after the software reporter
+    tool has finished.
+  </summary>
+</histogram>
+
+<histogram name="SoftwareReporter.RunningTimeAccordingToChrome" units="ms">
+  <owner>alito@chromium.org</owner>
+  <summary>
+    The amount of time it took for the software reporter to run as measured by
+    chrome. Logged just after the software reporter tool has finished.
+  </summary>
+</histogram>
+
+<histogram name="SoftwareReporter.RunningTimeRegistryError"
+    enum="SwReporterRunningTimeRegistryError">
+  <owner>alito@chromium.org</owner>
+  <summary>
+    Error encountered when reading the software reporter tool's start and end
+    times from the registry.
+  </summary>
+</histogram>
+
 <histogram name="SoftwareReporter.Step" enum="SwReporterStep">
   <owner>mad@chromium.org</owner>
   <summary>
@@ -46487,6 +46513,14 @@
   </summary>
 </histogram>
 
+<histogram name="TabManager.Discarding.DiscardToReloadTime" units="ms">
+  <owner>georgesak@chromium.org</owner>
+  <summary>
+    Elapsed time between a tab getting discarded to eventually being reloaded by
+    the user.
+  </summary>
+</histogram>
+
 <histogram name="TabManager.Discarding.ReloadCount" units="Reloads">
   <owner>georgesak@chromium.org</owner>
   <summary>
@@ -72780,6 +72814,14 @@
   <int value="3" label="Attempted"/>
 </enum>
 
+<enum name="SwReporterRunningTimeRegistryError" type="int">
+  <int value="0" label="No error"/>
+  <int value="1" label="Registry key invalid"/>
+  <int value="2" label="Missing start time"/>
+  <int value="3" label="Missing end time"/>
+  <int value="4" label="Missing start and end times"/>
+</enum>
+
 <enum name="SwReporterStep" type="int">
   <int value="0" label="Explicit request"/>
   <int value="1" label="Startup retry"/>
@@ -75200,7 +75242,7 @@
   <suffix name="ImageLink"
       label="The context menu was shown for an image which is also a link"/>
   <suffix name="Video" label="The context menu was shown for a video"/>
-  <suffix name="TextSelection"
+  <suffix name="SelectedText"
       label="The context menu was shown for a text selection"/>
   <affected-histogram name="ContextMenu.SelectedOption"/>
 </histogram_suffixes>
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 9a9bb638..e009e30 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -4,10 +4,10 @@
 
 import os
 
+from core import path_util
 from core import perf_benchmark
 
 from telemetry import benchmark
-from telemetry.core import util
 from telemetry import page as page_module
 from telemetry.page import page_test
 from telemetry.page import shared_page_state
@@ -18,7 +18,7 @@
 from page_sets import webgl_supported_shared_state
 
 
-BLINK_PERF_BASE_DIR = os.path.join(util.GetChromiumSrcDir(),
+BLINK_PERF_BASE_DIR = os.path.join(path_util.GetChromiumSrcDir(),
                                    'third_party', 'WebKit', 'PerformanceTests')
 SKIPPED_FILE = os.path.join(BLINK_PERF_BASE_DIR, 'Skipped')
 
diff --git a/tools/perf/benchmarks/indexeddb_perf.py b/tools/perf/benchmarks/indexeddb_perf.py
index 2de700fd..a0fc46c 100644
--- a/tools/perf/benchmarks/indexeddb_perf.py
+++ b/tools/perf/benchmarks/indexeddb_perf.py
@@ -22,12 +22,12 @@
 import json
 import os
 
+from core import path_util
 from core import perf_benchmark
 
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry import story
-from telemetry.core import util
 from telemetry.page import page_test
 from telemetry.value import scalar
 
@@ -100,8 +100,8 @@
     return 'indexeddb_perf'
 
   def CreateStorySet(self, options):
-    indexeddb_dir = os.path.join(util.GetChromiumSrcDir(), 'chrome', 'test',
-                                 'data', 'indexeddb')
+    indexeddb_dir = os.path.join(path_util.GetChromiumSrcDir(), 'chrome',
+                                 'test', 'data', 'indexeddb')
     ps = story.StorySet(base_dir=indexeddb_dir)
     ps.AddStory(page_module.Page('file://perf_test.html', ps, ps.base_dir))
     return ps
diff --git a/tools/perf/benchmarks/maps.py b/tools/perf/benchmarks/maps.py
index 6924592..900bb90f 100644
--- a/tools/perf/benchmarks/maps.py
+++ b/tools/perf/benchmarks/maps.py
@@ -5,13 +5,12 @@
 """Runs a Google Maps performance test.
 Rerforms several common navigation actions on the map (pan, zoom, rotate)"""
 
-import os
 import re
 
+from core import path_util
 from core import perf_benchmark
 
 from telemetry import benchmark
-from telemetry.core import util
 from telemetry.page import page as page_module
 from telemetry.page import page_test
 from telemetry import story
@@ -56,11 +55,9 @@
     return 'maps'
 
   def CreateStorySet(self, options):
-    page_set_path = os.path.join(
-        util.GetChromiumSrcDir(), 'tools', 'perf', 'page_sets')
-    ps = story.StorySet(
-        archive_data_file='data/maps.json', base_dir=page_set_path,
-        cloud_storage_bucket=story.PUBLIC_BUCKET)
+    ps = story.StorySet(archive_data_file='data/maps.json',
+                        base_dir=path_util.GetStorySetsDir(),
+                        cloud_storage_bucket=story.PUBLIC_BUCKET)
     ps.AddStory(MapsPage(ps, ps.base_dir))
     return ps
 
diff --git a/tools/perf/benchmarks/pywebsocket_server.py b/tools/perf/benchmarks/pywebsocket_server.py
index 304d269..494b8db 100644
--- a/tools/perf/benchmarks/pywebsocket_server.py
+++ b/tools/perf/benchmarks/pywebsocket_server.py
@@ -5,6 +5,8 @@
 import os
 import sys
 
+from core import path_util
+
 from telemetry.core import local_server
 from telemetry.core import util
 
@@ -15,7 +17,7 @@
     super(PywebsocketServerBackend, self).__init__()
     self.port = 8001
     self.base_dir = os.path.relpath(
-        os.path.join(util.GetChromiumSrcDir(),
+        os.path.join(path_util.GetChromiumSrcDir(),
                      'third_party', 'pywebsocket', 'src'),
         start=util.GetTelemetryDir())
 
diff --git a/tools/perf/benchmarks/spaceport.py b/tools/perf/benchmarks/spaceport.py
index 9f2ae32..7bcc809 100644
--- a/tools/perf/benchmarks/spaceport.py
+++ b/tools/perf/benchmarks/spaceport.py
@@ -7,10 +7,10 @@
 import logging
 import os
 
+from core import path_util
 from core import perf_benchmark
 
 from telemetry import benchmark
-from telemetry.core import util
 from telemetry import page as page_module
 from telemetry.page import page_test
 from telemetry import story
@@ -114,8 +114,8 @@
     return 'spaceport'
 
   def CreateStorySet(self, options):
-    spaceport_dir = os.path.join(util.GetChromiumSrcDir(), 'chrome', 'test',
-        'data', 'third_party', 'spaceport')
+    spaceport_dir = os.path.join(path_util.GetChromiumSrcDir(), 'chrome',
+                                 'test', 'data', 'third_party', 'spaceport')
     ps = story.StorySet(base_dir=spaceport_dir)
     ps.AddStory(page_module.Page('file://index.html', ps, ps.base_dir))
     return ps
diff --git a/tools/perf/core/path_util.py b/tools/perf/core/path_util.py
new file mode 100644
index 0000000..3050c981
--- /dev/null
+++ b/tools/perf/core/path_util.py
@@ -0,0 +1,14 @@
+# 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.
+
+import os
+
+
+def GetChromiumSrcDir():
+  return os.path.abspath(os.path.join(
+      os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
+
+def GetStorySetsDir():
+  return os.path.abspath(os.path.join(
+      os.path.dirname(__file__), os.pardir , 'page_sets'))
diff --git a/tools/telemetry/telemetry/__init__.py b/tools/telemetry/telemetry/__init__.py
index d201ec6..c90ed7f0 100644
--- a/tools/telemetry/telemetry/__init__.py
+++ b/tools/telemetry/telemetry/__init__.py
@@ -37,6 +37,8 @@
 _AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'webpagereplay')
 _AddDirToPythonPath(util.GetTelemetryThirdPartyDir(), 'websocket-client')
 
-_AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
-_AddDirToPythonPath(util.GetChromiumSrcDir(),
-                    'third_party', 'catapult', 'tracing')
+_AddDirToPythonPath(os.path.dirname(__file__), os.path.pardir, os.path.pardir,
+                    os.path.pardir, 'build', 'android')
+
+_AddDirToPythonPath(os.path.dirname(__file__), os.path.pardir, os.path.pardir,
+                    os.path.pardir, 'third_party', 'catapult', 'tracing')
diff --git a/tools/telemetry/telemetry/benchmark_runner.py b/tools/telemetry/telemetry/benchmark_runner.py
index 66c12a4..aed7d7e 100644
--- a/tools/telemetry/telemetry/benchmark_runner.py
+++ b/tools/telemetry/telemetry/benchmark_runner.py
@@ -32,6 +32,11 @@
 from telemetry.internal.util import binary_manager
 from telemetry.internal.util import command_line
 from telemetry.internal.util import ps_util
+from telemetry import project_config
+
+
+# TODO(aiolos): Remove this once clients move over to project_config version.
+ProjectConfig = project_config.ProjectConfig
 
 
 def _IsBenchmarkEnabled(benchmark_class, possible_browser):
@@ -80,40 +85,6 @@
       'Pass --browser to list benchmarks for another browser.\n')
 
 
-class ProjectConfig(object):
-  """Contains information about the benchmark runtime environment.
-
-  Attributes:
-    top_level_dir: A dir that contains benchmark, page test, and/or story
-        set dirs and associated artifacts.
-    benchmark_dirs: A list of dirs containing benchmarks.
-    benchmark_aliases: A dict of name:alias string pairs to be matched against
-        exactly during benchmark selection.
-    client_config: A path to a ProjectDependencies json file.
-  """
-  def __init__(self, top_level_dir, benchmark_dirs=None,
-               benchmark_aliases=None, client_config=None):
-    self._top_level_dir = top_level_dir
-    self._benchmark_dirs = benchmark_dirs or []
-    self._benchmark_aliases = benchmark_aliases or dict()
-    self._client_config = client_config or ''
-
-  @property
-  def top_level_dir(self):
-    return self._top_level_dir
-
-  @property
-  def benchmark_dirs(self):
-    return self._benchmark_dirs
-
-  @property
-  def benchmark_aliases(self):
-    return self._benchmark_aliases
-
-  @property
-  def client_config(self):
-    return self._client_config
-
 class Help(command_line.OptparseCommand):
   """Display help information about a command"""
 
diff --git a/tools/telemetry/telemetry/project_config.py b/tools/telemetry/telemetry/project_config.py
new file mode 100644
index 0000000..549b19da
--- /dev/null
+++ b/tools/telemetry/telemetry/project_config.py
@@ -0,0 +1,38 @@
+# 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.
+
+
+class ProjectConfig(object):
+  """Contains information about the benchmark runtime environment.
+
+  Attributes:
+    top_level_dir: A dir that contains benchmark, page test, and/or story
+        set dirs and associated artifacts.
+    benchmark_dirs: A list of dirs containing benchmarks.
+    benchmark_aliases: A dict of name:alias string pairs to be matched against
+        exactly during benchmark selection.
+    client_config: A path to a ProjectDependencies json file.
+  """
+  def __init__(self, top_level_dir, benchmark_dirs=None,
+               benchmark_aliases=None, client_config=None):
+    self._top_level_dir = top_level_dir
+    self._benchmark_dirs = benchmark_dirs or []
+    self._benchmark_aliases = benchmark_aliases or dict()
+    self._client_config = client_config or ''
+
+  @property
+  def top_level_dir(self):
+    return self._top_level_dir
+
+  @property
+  def benchmark_dirs(self):
+    return self._benchmark_dirs
+
+  @property
+  def benchmark_aliases(self):
+    return self._benchmark_aliases
+
+  @property
+  def client_config(self):
+    return self._client_config
diff --git a/ui/base/test/ui_controls_mac.mm b/ui/base/test/ui_controls_mac.mm
index 002cb03e..15cdb796 100644
--- a/ui/base/test/ui_controls_mac.mm
+++ b/ui/base/test/ui_controls_mac.mm
@@ -235,7 +235,7 @@
 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) {
   CHECK(g_ui_controls_enabled);
   CGFloat screenHeight =
-    [[[NSScreen screens] objectAtIndex:0] frame].size.height;
+    [[[NSScreen screens] firstObject] frame].size.height;
   g_mouse_location = NSMakePoint(x, screenHeight - y);  // flip!
 
   NSWindow* window = WindowAtCurrentMouseLocation();
diff --git a/ui/compositor/layer_animation_observer.cc b/ui/compositor/layer_animation_observer.cc
index 658a835..e5db5ecd 100644
--- a/ui/compositor/layer_animation_observer.cc
+++ b/ui/compositor/layer_animation_observer.cc
@@ -18,6 +18,9 @@
   StopObserving();
 }
 
+void LayerAnimationObserver::OnLayerAnimationStarted(
+    LayerAnimationSequence* sequence) {}
+
 bool LayerAnimationObserver::RequiresNotificationWhenAnimatorDestroyed() const {
   return false;
 }
diff --git a/ui/compositor/layer_animation_observer.h b/ui/compositor/layer_animation_observer.h
index 35253d3..26bfdfa 100644
--- a/ui/compositor/layer_animation_observer.h
+++ b/ui/compositor/layer_animation_observer.h
@@ -22,6 +22,9 @@
 // LayerAnimationObservers are notified when animations complete.
 class COMPOSITOR_EXPORT LayerAnimationObserver  {
  public:
+  // Called when the |sequence| starts.
+  virtual void OnLayerAnimationStarted(LayerAnimationSequence* sequence);
+
   // Called when the |sequence| ends. Not called if |sequence| is aborted.
   virtual void OnLayerAnimationEnded(
       LayerAnimationSequence* sequence) = 0;
diff --git a/ui/compositor/layer_animation_sequence.cc b/ui/compositor/layer_animation_sequence.cc
index 2c01a22..04a91af2 100644
--- a/ui/compositor/layer_animation_sequence.cc
+++ b/ui/compositor/layer_animation_sequence.cc
@@ -48,6 +48,8 @@
   if (elements_.empty())
     return;
 
+  NotifyStarted();
+
   elements_[0]->set_requested_start_time(start_time_);
   elements_[0]->Start(delegate, animation_group_id_);
 }
@@ -258,6 +260,11 @@
                     OnLayerAnimationScheduled(this));
 }
 
+void LayerAnimationSequence::NotifyStarted() {
+  FOR_EACH_OBSERVER(LayerAnimationObserver, observers_,
+                    OnLayerAnimationStarted(this));
+}
+
 void LayerAnimationSequence::NotifyEnded() {
   FOR_EACH_OBSERVER(LayerAnimationObserver,
                     observers_,
diff --git a/ui/compositor/layer_animation_sequence.h b/ui/compositor/layer_animation_sequence.h
index 0dad650..98afe9e 100644
--- a/ui/compositor/layer_animation_sequence.h
+++ b/ui/compositor/layer_animation_sequence.h
@@ -140,6 +140,9 @@
   // Notifies the observers that this sequence has been scheduled.
   void NotifyScheduled();
 
+  // Notifies the observers that this sequence has been started.
+  void NotifyStarted();
+
   // Notifies the observers that this sequence has ended.
   void NotifyEnded();
 
diff --git a/ui/compositor/layer_animator_unittest.cc b/ui/compositor/layer_animator_unittest.cc
index b3743ed..3ceeb4d 100644
--- a/ui/compositor/layer_animator_unittest.cc
+++ b/ui/compositor/layer_animator_unittest.cc
@@ -54,6 +54,54 @@
   return animations;
 }
 
+// Creates a default animator with timers disabled for test. |delegate| and
+// |observer| are attached if non-null.
+LayerAnimator* CreateDefaultTestAnimator(LayerAnimationDelegate* delegate,
+                                         LayerAnimationObserver* observer) {
+  LayerAnimator* animator(LayerAnimator::CreateDefaultAnimator());
+  animator->set_disable_timer_for_test(true);
+  if (delegate)
+    animator->SetDelegate(delegate);
+  if (observer)
+    animator->AddObserver(observer);
+  return animator;
+}
+
+// Creates a default animator with timers disabled for test. |delegate| is
+// attached if non-null.
+LayerAnimator* CreateDefaultTestAnimator(LayerAnimationDelegate* delegate) {
+  return CreateDefaultTestAnimator(delegate, nullptr);
+}
+
+// Creates a default animator with timers disabled for test.
+LayerAnimator* CreateDefaultTestAnimator() {
+  return CreateDefaultTestAnimator(nullptr, nullptr);
+}
+
+// Creates an implicit animator with timers disabled for test. |delegate| and
+// |observer| are attached if non-null.
+LayerAnimator* CreateImplicitTestAnimator(LayerAnimationDelegate* delegate,
+                                          LayerAnimationObserver* observer) {
+  LayerAnimator* animator(LayerAnimator::CreateImplicitAnimator());
+  animator->set_disable_timer_for_test(true);
+  if (delegate)
+    animator->SetDelegate(delegate);
+  if (observer)
+    animator->AddObserver(observer);
+  return animator;
+}
+
+// Creates an implicit animator with timers disabled for test. |delegate| is
+// attached if non-null.
+LayerAnimator* CreateImplicitTestAnimator(LayerAnimationDelegate* delegate) {
+  return CreateImplicitTestAnimator(delegate, nullptr);
+}
+
+// Creates an implicit animator with timers disabled for test.
+LayerAnimator* CreateImplicitTestAnimator() {
+  return CreateImplicitTestAnimator(nullptr, nullptr);
+}
+
 class TestImplicitAnimationObserver : public ImplicitAnimationObserver {
  public:
   explicit TestImplicitAnimationObserver(bool notify_when_animator_destructed)
@@ -188,11 +236,8 @@
 // Checks that setting a property on an implicit animator causes an animation to
 // happen.
 TEST(LayerAnimatorTest, ImplicitAnimation) {
-  scoped_refptr<LayerAnimator> animator(
-      LayerAnimator::CreateImplicitAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
   base::TimeTicks now = base::TimeTicks::Now();
   animator->SetBrightness(0.5);
   EXPECT_TRUE(animator->is_animating());
@@ -203,10 +248,8 @@
 // Checks that if the animator is a default animator, that implicit animations
 // are not started.
 TEST(LayerAnimatorTest, NoImplicitAnimation) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
   animator->SetBrightness(0.5);
   EXPECT_FALSE(animator->is_animating());
   EXPECT_FLOAT_EQ(delegate.GetBrightnessForAnimation(), 0.5);
@@ -215,11 +258,8 @@
 // Checks that StopAnimatingProperty stops animation for that property, and also
 // skips the stopped animation to the end.
 TEST(LayerAnimatorTest, StopAnimatingProperty) {
-  scoped_refptr<LayerAnimator> animator(
-      LayerAnimator::CreateImplicitAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
   double target_opacity(0.5);
   gfx::Rect target_bounds(0, 0, 50, 50);
   animator->SetOpacity(target_opacity);
@@ -232,14 +272,12 @@
   CheckApproximatelyEqual(delegate.GetBoundsForAnimation(), target_bounds);
 }
 
-// Checks that multiple running animation for separate properties can be stopped
-// simultaneously and that all animations are advanced to their target values.
+// Checks that multiple running animations for separate properties can be
+// stopped simultaneously and that all animations are advanced to their target
+// values.
 TEST(LayerAnimatorTest, StopAnimating) {
-  scoped_refptr<LayerAnimator> animator(
-      LayerAnimator::CreateImplicitAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
   double target_opacity(0.5);
   gfx::Rect target_bounds(0, 0, 50, 50);
   animator->SetOpacity(target_opacity);
@@ -251,18 +289,16 @@
   CheckApproximatelyEqual(delegate.GetBoundsForAnimation(), target_bounds);
 }
 
-// Checks that multiple running animation for separate properties can be stopped
-// simultaneously and that all animations are advanced to their target values.
+// Checks that multiple running animations for separate properties can be
+// stopped simultaneously and that aborted animations are NOT advanced to their
+// target values.
 TEST(LayerAnimatorTest, AbortAllAnimations) {
-  scoped_refptr<LayerAnimator> animator(
-      LayerAnimator::CreateImplicitAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
   double initial_opacity(1.0);
   gfx::Rect initial_bounds(0, 0, 10, 10);
   delegate.SetOpacityFromAnimation(initial_opacity);
   delegate.SetBoundsFromAnimation(initial_bounds);
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
   double target_opacity(0.5);
   gfx::Rect target_bounds(0, 0, 50, 50);
   animator->SetOpacity(target_opacity);
@@ -277,10 +313,8 @@
 // Schedule a non-threaded animation that can run immediately. This is the
 // trivial case and should result in the animation being started immediately.
 TEST(LayerAnimatorTest, ScheduleAnimationThatCanRunImmediately) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -314,12 +348,10 @@
 // Schedule a threaded animation that can run immediately.
 TEST(LayerAnimatorTest, ScheduleThreadedAnimationThatCanRunImmediately) {
   double epsilon = 0.00001;
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -362,10 +394,8 @@
 // Schedule two non-threaded animations on separate properties. Both animations
 // should start immediately and should progress in lock step.
 TEST(LayerAnimatorTest, ScheduleTwoAnimationsThatCanRunImmediately) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -413,12 +443,10 @@
 // animations should progress in lock step.
 TEST(LayerAnimatorTest, ScheduleThreadedAndNonThreadedAnimations) {
   double epsilon = 0.00001;
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -477,10 +505,8 @@
 // Schedule two animations on the same property. In this case, the two
 // animations should run one after another.
 TEST(LayerAnimatorTest, ScheduleTwoAnimationsOnSameProperty) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -530,10 +556,8 @@
 // is, ensure that all animations targetting a particular property are run in
 // order.
 TEST(LayerAnimatorTest, ScheduleBlockedAnimation) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double middle_grayscale(0.5);
@@ -609,10 +633,8 @@
 // ScheduleTogether is being used, the bounds animation should not start until
 // the second grayscale animation starts.
 TEST(LayerAnimatorTest, ScheduleTogether) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double target_grayscale(1.0);
@@ -662,10 +684,8 @@
 // Start non-threaded animation (that can run immediately). This is the trivial
 // case (see the trival case for ScheduleAnimation).
 TEST(LayerAnimatorTest, StartAnimationThatCanRunImmediately) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -699,12 +719,10 @@
 // Start threaded animation (that can run immediately).
 TEST(LayerAnimatorTest, StartThreadedAnimationThatCanRunImmediately) {
   double epsilon = 0.00001;
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -745,10 +763,8 @@
 
 // Preempt by immediately setting new target.
 TEST(LayerAnimatorTest, PreemptBySettingNewTarget) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -773,10 +789,8 @@
 
 // Preempt by animating to new target, with a non-threaded animation.
 TEST(LayerAnimatorTest, PreemptByImmediatelyAnimatingToNewTarget) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -828,12 +842,10 @@
 // Preempt by animating to new target, with a threaded animation.
 TEST(LayerAnimatorTest, PreemptThreadedByImmediatelyAnimatingToNewTarget) {
   double epsilon = 0.00001;
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double middle_opacity(0.5);
@@ -899,10 +911,8 @@
 
 // Preempt by enqueuing the new animation.
 TEST(LayerAnimatorTest, PreemptEnqueueNewAnimation) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -953,10 +963,8 @@
 // case, all pending and running animations should be finished, and the new
 // animation started.
 TEST(LayerAnimatorTest, PreemptyByReplacingQueuedAnimations) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double middle_brightness(0.5);
@@ -1009,10 +1017,8 @@
 }
 
 TEST(LayerAnimatorTest, StartTogetherSetsLastStepTime) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double target_grayscale(1.0);
@@ -1048,10 +1054,8 @@
 //-------------------------------------------------------
 // Preempt by immediately setting new target.
 TEST(LayerAnimatorTest, MultiPreemptBySettingNewTarget) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -1086,10 +1090,8 @@
 
 // Preempt by animating to new target.
 TEST(LayerAnimatorTest, MultiPreemptByImmediatelyAnimatingToNewTarget) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double middle_grayscale(0.5);
@@ -1155,12 +1157,10 @@
 // Preempt a threaded animation by animating to new target.
 TEST(LayerAnimatorTest, MultiPreemptThreadedByImmediatelyAnimatingToNewTarget) {
   double epsilon = 0.00001;
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double middle_opacity(0.5);
@@ -1243,10 +1243,8 @@
 
 // Preempt by enqueuing the new animation.
 TEST(LayerAnimatorTest, MultiPreemptEnqueueNewAnimation) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double middle_grayscale(0.5);
@@ -1309,10 +1307,8 @@
 // case, all pending and running animations should be finished, and the new
 // animation started.
 TEST(LayerAnimatorTest, MultiPreemptByReplacingQueuedAnimations) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_grayscale(0.0);
   double middle_grayscale(0.5);
@@ -1380,10 +1376,8 @@
 //-------------------------------------------------------
 // Test that non-threaded cyclic sequences continue to animate.
 TEST(LayerAnimatorTest, CyclicSequences) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_brightness(0.0);
   double target_brightness(1.0);
@@ -1440,12 +1434,10 @@
 
 // Test that threaded cyclic sequences continue to animate.
 TEST(LayerAnimatorTest, ThreadedCyclicSequences) {
-  LayerAnimatorTestController test_controller(
-      LayerAnimator::CreateDefaultAnimator());
-  LayerAnimator* animator = test_controller.animator();
-  test_controller.animator()->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  test_controller.animator()->SetDelegate(&delegate);
+  LayerAnimatorTestController test_controller(
+      CreateDefaultTestAnimator(&delegate));
+  LayerAnimator* animator = test_controller.animator();
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -1534,12 +1526,10 @@
 }
 
 TEST(LayerAnimatorTest, AddObserverExplicit) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationObserver observer;
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
-  animator->AddObserver(&observer);
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, &observer));
   observer.set_requires_notification_when_animator_destroyed(true);
 
   EXPECT_TRUE(!observer.last_ended_sequence());
@@ -1575,11 +1565,9 @@
 // Tests that an observer added to a scoped settings object is still notified
 // when the object goes out of scope.
 TEST(LayerAnimatorTest, ImplicitAnimationObservers) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
-  TestImplicitAnimationObserver observer(false);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  TestImplicitAnimationObserver observer(false);
 
   EXPECT_FALSE(observer.animations_completed());
   animator->SetBrightness(1.0f);
@@ -1602,11 +1590,9 @@
 // Tests that an observer added to a scoped settings object is still notified
 // when the object goes out of scope due to the animation being interrupted.
 TEST(LayerAnimatorTest, InterruptedImplicitAnimationObservers) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
-  TestImplicitAnimationObserver observer(false);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  TestImplicitAnimationObserver observer(false);
 
   EXPECT_FALSE(observer.animations_completed());
   animator->SetBrightness(1.0f);
@@ -1653,12 +1639,10 @@
 // Tests that an observer added to a scoped settings object is not notified
 // when the animator is destroyed unless explicitly requested.
 TEST(LayerAnimatorTest, ImplicitObserversAtAnimatorDestruction) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
   TestImplicitAnimationObserver observer_notify(true);
   TestImplicitAnimationObserver observer_do_not_notify(false);
-  TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
 
   EXPECT_FALSE(observer_notify.animations_completed());
   EXPECT_FALSE(observer_do_not_notify.animations_completed());
@@ -1682,11 +1666,9 @@
 }
 
 TEST(LayerAnimatorTest, AbortedAnimationStatusInImplicitObservers) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
-  TestImplicitAnimationObserver observer(false);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  TestImplicitAnimationObserver observer(false);
 
   EXPECT_FALSE(observer.animations_completed());
   animator->SetBrightness(1.0f);
@@ -1722,12 +1704,10 @@
 }
 
 TEST(LayerAnimatorTest, RemoveObserverShouldRemoveFromSequences) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
   TestLayerAnimationObserver observer;
   TestLayerAnimationObserver removed_observer;
-  TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
 
   base::TimeDelta delta = base::TimeDelta::FromSeconds(1);
 
@@ -1757,13 +1737,10 @@
 
 TEST(LayerAnimatorTest, ObserverReleasedBeforeAnimationSequenceEnds) {
   TestLayerAnimationDelegate delegate;
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
-
   scoped_ptr<TestLayerAnimationObserver> observer(
       new TestLayerAnimationObserver);
-  animator->SetDelegate(&delegate);
-  animator->AddObserver(observer.get());
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, observer.get()));
 
   delegate.SetOpacityFromAnimation(0.0f);
 
@@ -1784,12 +1761,10 @@
 }
 
 TEST(LayerAnimatorTest, ObserverAttachedAfterAnimationStarted) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   TestImplicitAnimationObserver observer(false);
-  TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
 
   delegate.SetBrightnessFromAnimation(0.0f);
 
@@ -1818,12 +1793,10 @@
 }
 
 TEST(LayerAnimatorTest, ObserverDetachedBeforeAnimationFinished) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   TestImplicitAnimationObserver observer(false);
-  TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
 
   delegate.SetBrightnessFromAnimation(0.0f);
   base::TimeDelta delta = base::TimeDelta::FromSeconds(1);
@@ -2017,10 +1990,8 @@
 // Check that setting a property during an animation with a default animator
 // cancels the original animation.
 TEST(LayerAnimatorTest, SettingPropertyDuringAnAnimation) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   double start_opacity(0.0);
   double target_opacity(1.0);
@@ -2044,11 +2015,9 @@
 // Tests that the preemption mode IMMEDIATELY_SET_NEW_TARGET, doesn't cause the
 // second sequence to be leaked.
 TEST(LayerAnimatorTest, ImmediatelySettingNewTargetDoesNotLeak) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_preemption_strategy(LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  animator->set_preemption_strategy(LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
 
   gfx::Rect start_bounds(0, 0, 50, 50);
   gfx::Rect middle_bounds(10, 10, 100, 100);
@@ -2085,10 +2054,8 @@
 // Verifies GetTargetOpacity() works when multiple sequences are scheduled.
 TEST(LayerAnimatorTest, GetTargetOpacity) {
   TestLayerAnimationDelegate delegate;
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
   animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
-  animator->set_disable_timer_for_test(true);
-  animator->SetDelegate(&delegate);
 
   delegate.SetOpacityFromAnimation(0.0);
 
@@ -2105,11 +2072,9 @@
 
 // Verifies GetTargetBrightness() works when multiple sequences are scheduled.
 TEST(LayerAnimatorTest, GetTargetBrightness) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
 
   delegate.SetBrightnessFromAnimation(0.0);
 
@@ -2126,11 +2091,9 @@
 
 // Verifies GetTargetGrayscale() works when multiple sequences are scheduled.
 TEST(LayerAnimatorTest, GetTargetGrayscale) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
+  animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
 
   delegate.SetGrayscaleFromAnimation(0.0);
 
@@ -2147,10 +2110,8 @@
 
 // Verifies color property is modified appropriately.
 TEST(LayerAnimatorTest, Color) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   SkColor start_color  = SkColorSetARGB( 64, 20, 40,  60);
   SkColor middle_color = SkColorSetARGB(128, 35, 70, 120);
@@ -2185,7 +2146,7 @@
 
 // Verifies SchedulePauseForProperties().
 TEST(LayerAnimatorTest, SchedulePauseForProperties) {
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator());
   animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
   animator->SchedulePauseForProperties(
       base::TimeDelta::FromMilliseconds(100),
@@ -2198,9 +2159,7 @@
 
 class AnimatorOwner {
 public:
-  AnimatorOwner()
-      : animator_(LayerAnimator::CreateDefaultAnimator()) {
-  }
+ AnimatorOwner() : animator_(CreateDefaultTestAnimator()) {}
 
   LayerAnimator* animator() { return animator_.get(); }
 
@@ -2281,7 +2240,6 @@
   observer->set_delete_on_animation_ended(true);
   observer->set_delete_on_animation_aborted(true);
   LayerAnimator* animator = observer->animator();
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
   animator->SetDelegate(&delegate);
 
@@ -2314,7 +2272,6 @@
   observer->set_delete_on_animation_ended(true);
   observer->set_delete_on_animation_aborted(true);
   LayerAnimator* animator = observer->animator();
-  animator->set_disable_timer_for_test(true);
   TestLayerAnimationDelegate delegate;
   animator->SetDelegate(&delegate);
 
@@ -2346,7 +2303,6 @@
   DeletingObserver* observer = new DeletingObserver(&observer_was_deleted);
   observer->set_delete_on_animation_scheduled(true);
   LayerAnimator* animator = observer->animator();
-  animator->set_disable_timer_for_test(true);
   animator->SetDelegate(&delegate);
 
   delegate.SetOpacityFromAnimation(0.0f);
@@ -2379,7 +2335,6 @@
   LayerAnimator* animator = observer->animator();
   animator->set_preemption_strategy(
       LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-  animator->set_disable_timer_for_test(true);
   animator->SetDelegate(&delegate);
 
   delegate.SetOpacityFromAnimation(0.0f);
@@ -2412,10 +2367,7 @@
 
 TEST(LayerAnimatorTest, TestSetterRespectEnqueueStrategy) {
   TestLayerAnimationDelegate delegate;
-  scoped_refptr<LayerAnimator> animator(LayerAnimator::CreateDefaultAnimator());
-  animator->set_disable_timer_for_test(true);
-
-  animator->SetDelegate(&delegate);
+  scoped_refptr<LayerAnimator> animator(CreateDefaultTestAnimator(&delegate));
 
   float start_opacity = 0.0f;
   float target_opacity = 1.0f;
@@ -2618,9 +2570,7 @@
 };
 
 TEST(LayerAnimatorTest, ObserverDeletesLayerInStopAnimating) {
-  scoped_refptr<LayerAnimator> animator(
-      LayerAnimator::CreateImplicitAnimator());
-  animator->set_disable_timer_for_test(true);
+  scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator());
   LayerOwnerAnimationObserver observer(animator.get());
   LayerAnimationDelegate* delegate = observer.animator_layer();
 
@@ -2649,4 +2599,206 @@
   EXPECT_TRUE(animator->is_animating());
 }
 
+// Verifies the LayerAnimatorObserver notification order for an animation
+// sequence that completes successfully.
+TEST(LayerAnimatorObserverNotificationOrderTest,
+     SuccessfulCompletionOfSequence) {
+  TestLayerAnimationObserver observer;
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, &observer));
+  observer.set_requires_notification_when_animator_destroyed(true);
+
+  const base::TimeDelta animation_duration = base::TimeDelta::FromSeconds(100);
+
+  LayerAnimationSequence* sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  EXPECT_TRUE(observer.NoEventsObserved());
+
+  animator->StartAnimation(sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), sequence);
+  EXPECT_EQ(observer.last_started_sequence(), sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), nullptr);
+
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+  EXPECT_TRUE(observer.ScheduledEpochIsBeforeStartedEpoch());
+
+  observer.ResetLayerAnimationObserverations();
+
+  const base::TimeTicks start_time = animator->last_step_time();
+
+  animator->Step(start_time + animation_duration);
+
+  EXPECT_EQ(observer.last_attached_sequence(), nullptr);
+  EXPECT_EQ(observer.last_scheduled_sequence(), nullptr);
+  EXPECT_EQ(observer.last_started_sequence(), nullptr);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), sequence);
+  EXPECT_EQ(observer.last_detached_sequence(), sequence);
+
+  EXPECT_TRUE(observer.EndedEpochIsBeforeDetachedEpoch());
+}
+
+// Verifies the LayerAnimatorObserver notification order for an animation
+// sequence that is aborted after being scheduled.
+TEST(LayerAnimatorObserverNotificationOrderTest, AbortingAScheduledSequence) {
+  TestLayerAnimationObserver observer;
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, &observer));
+  observer.set_requires_notification_when_animator_destroyed(true);
+
+  const base::TimeDelta animation_duration = base::TimeDelta::FromSeconds(100);
+
+  LayerAnimationSequence* sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  EXPECT_TRUE(observer.NoEventsObserved());
+
+  animator->StartAnimation(sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), sequence);
+  EXPECT_EQ(observer.last_started_sequence(), sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), nullptr);
+
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+  EXPECT_TRUE(observer.ScheduledEpochIsBeforeStartedEpoch());
+
+  observer.ResetLayerAnimationObserverations();
+
+  animator->AbortAllAnimations();
+
+  EXPECT_EQ(observer.last_attached_sequence(), nullptr);
+  EXPECT_EQ(observer.last_scheduled_sequence(), nullptr);
+  EXPECT_EQ(observer.last_started_sequence(), nullptr);
+  EXPECT_EQ(observer.last_aborted_sequence(), sequence);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), sequence);
+
+  EXPECT_TRUE(observer.AbortedEpochIsBeforeDetachedEpoch());
+}
+
+// Verifies the LayerAnimatorObserver notification order for an animation
+// sequence that is queued up after another sequence that
+// completes successfully.
+TEST(LayerAnimatorObserverNotificationOrderTest,
+     RunningASequenceThatIsQueuedForLaterStartTime) {
+  TestLayerAnimationObserver observer;
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, &observer));
+  observer.set_requires_notification_when_animator_destroyed(true);
+
+  const base::TimeDelta animation_duration = base::TimeDelta::FromSeconds(100);
+
+  LayerAnimationSequence* first_sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  LayerAnimationSequence* queued_sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  EXPECT_TRUE(observer.NoEventsObserved());
+
+  animator->StartAnimation(first_sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_started_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), nullptr);
+
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+  EXPECT_TRUE(observer.ScheduledEpochIsBeforeStartedEpoch());
+
+  observer.ResetLayerAnimationObserverations();
+
+  animator->set_preemption_strategy(LayerAnimator::ENQUEUE_NEW_ANIMATION);
+  animator->StartAnimation(queued_sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_started_sequence(), nullptr);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), nullptr);
+
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+
+  observer.ResetLayerAnimationObserverations();
+
+  base::TimeTicks start_time = animator->last_step_time();
+
+  animator->Step(start_time + animation_duration);
+
+  EXPECT_EQ(observer.last_attached_sequence(), nullptr);
+  EXPECT_EQ(observer.last_scheduled_sequence(), nullptr);
+  EXPECT_EQ(observer.last_started_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_detached_sequence(), first_sequence);
+
+  EXPECT_TRUE(observer.EndedEpochIsBeforeDetachedEpoch());
+  EXPECT_TRUE(observer.EndedEpochIsBeforeStartedEpoch());
+}
+
+// Verifies the LayerAnimatorObserver notification order for an animation
+// sequence that pre-empts another sequence.
+TEST(LayerAnimatorObserverNotificationOrderTest,
+     RunningASequenceThatPreEmptsAnotherSequence) {
+  TestLayerAnimationObserver observer;
+  TestLayerAnimationDelegate delegate;
+  scoped_refptr<LayerAnimator> animator(
+      CreateDefaultTestAnimator(&delegate, &observer));
+  observer.set_requires_notification_when_animator_destroyed(true);
+
+  const base::TimeDelta animation_duration = base::TimeDelta::FromSeconds(100);
+
+  LayerAnimationSequence* first_sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  LayerAnimationSequence* queued_sequence = new LayerAnimationSequence(
+      LayerAnimationElement::CreateBrightnessElement(1.0f, animation_duration));
+
+  EXPECT_TRUE(observer.NoEventsObserved());
+
+  animator->StartAnimation(first_sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_started_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), nullptr);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), nullptr);
+
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+  EXPECT_TRUE(observer.ScheduledEpochIsBeforeStartedEpoch());
+
+  observer.ResetLayerAnimationObserverations();
+
+  animator->set_preemption_strategy(
+      LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  animator->StartAnimation(queued_sequence);
+
+  EXPECT_EQ(observer.last_attached_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_scheduled_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_started_sequence(), queued_sequence);
+  EXPECT_EQ(observer.last_aborted_sequence(), first_sequence);
+  EXPECT_EQ(observer.last_ended_sequence(), nullptr);
+  EXPECT_EQ(observer.last_detached_sequence(), first_sequence);
+
+  EXPECT_TRUE(observer.AbortedEpochIsBeforeDetachedEpoch());
+  EXPECT_TRUE(observer.AbortedEpochIsBeforeStartedEpoch());
+  EXPECT_TRUE(observer.AttachedEpochIsBeforeScheduledEpoch());
+  EXPECT_TRUE(observer.ScheduledEpochIsBeforeStartedEpoch());
+}
+
 }  // namespace ui
diff --git a/ui/compositor/test/test_layer_animation_observer.cc b/ui/compositor/test/test_layer_animation_observer.cc
index abc74ae..838b7f2 100644
--- a/ui/compositor/test/test_layer_animation_observer.cc
+++ b/ui/compositor/test/test_layer_animation_observer.cc
@@ -9,28 +9,74 @@
 namespace ui {
 
 TestLayerAnimationObserver::TestLayerAnimationObserver()
-    : last_ended_sequence_(NULL),
-      last_scheduled_sequence_(NULL),
-      last_aborted_sequence_(NULL),
-      requires_notification_when_animator_destroyed_(false) {
-}
+    : next_epoch_(0),
+      last_attached_sequence_(nullptr),
+      last_attached_sequence_epoch_(-1),
+      last_scheduled_sequence_(nullptr),
+      last_scheduled_sequence_epoch_(-1),
+      last_started_sequence_(nullptr),
+      last_started_sequence_epoch_(-1),
+      last_aborted_sequence_(nullptr),
+      last_aborted_sequence_epoch_(-1),
+      last_ended_sequence_(nullptr),
+      last_ended_sequence_epoch_(-1),
+      last_detached_sequence_(nullptr),
+      last_detached_sequence_epoch_(-1),
+      requires_notification_when_animator_destroyed_(false) {}
 
 TestLayerAnimationObserver::~TestLayerAnimationObserver() {
 }
 
-void TestLayerAnimationObserver::OnLayerAnimationEnded(
-    LayerAnimationSequence* sequence) {
-  last_ended_sequence_ = sequence;
+void TestLayerAnimationObserver::ResetLayerAnimationObserverations() {
+  next_epoch_ = 0;
+  last_attached_sequence_ = nullptr;
+  last_attached_sequence_epoch_ = -1;
+  last_scheduled_sequence_ = nullptr;
+  last_scheduled_sequence_epoch_ = -1;
+  last_started_sequence_ = nullptr;
+  last_started_sequence_epoch_ = -1;
+  last_aborted_sequence_ = nullptr;
+  last_aborted_sequence_epoch_ = -1;
+  last_ended_sequence_ = nullptr;
+  last_ended_sequence_epoch_ = -1;
+  last_detached_sequence_ = nullptr;
+  last_detached_sequence_epoch_ = -1;
 }
 
-void TestLayerAnimationObserver::OnLayerAnimationAborted(
+void TestLayerAnimationObserver::OnAttachedToSequence(
     LayerAnimationSequence* sequence) {
-  last_aborted_sequence_ = sequence;
+  last_attached_sequence_ = sequence;
+  last_attached_sequence_epoch_ = next_epoch_++;
 }
 
 void TestLayerAnimationObserver::OnLayerAnimationScheduled(
     LayerAnimationSequence* sequence) {
   last_scheduled_sequence_ = sequence;
+  last_scheduled_sequence_epoch_ = next_epoch_++;
+}
+
+void TestLayerAnimationObserver::OnLayerAnimationStarted(
+    LayerAnimationSequence* sequence) {
+  last_started_sequence_ = sequence;
+  last_started_sequence_epoch_ = next_epoch_++;
+}
+
+void TestLayerAnimationObserver::OnLayerAnimationAborted(
+    LayerAnimationSequence* sequence) {
+  last_aborted_sequence_ = sequence;
+  last_aborted_sequence_epoch_ = next_epoch_++;
+}
+
+void TestLayerAnimationObserver::OnLayerAnimationEnded(
+    LayerAnimationSequence* sequence) {
+  last_ended_sequence_ = sequence;
+  last_ended_sequence_epoch_ = next_epoch_++;
+}
+
+void TestLayerAnimationObserver::OnDetachedFromSequence(
+    LayerAnimationSequence* sequence) {
+  last_detached_sequence_ = sequence;
+  last_detached_sequence_epoch_ = next_epoch_++;
 }
 
 bool
@@ -38,4 +84,134 @@
   return requires_notification_when_animator_destroyed_;
 }
 
+testing::AssertionResult TestLayerAnimationObserver::NoEventsObserved() {
+  if (!last_attached_sequence_ && !last_scheduled_sequence_ &&
+      !last_started_sequence_ && !last_aborted_sequence_ &&
+      !last_ended_sequence_ && !last_detached_sequence_) {
+    return testing::AssertionSuccess();
+  } else {
+    testing::AssertionResult assertion_failure = testing::AssertionFailure();
+    assertion_failure << "The following events have been observed:";
+    if (last_attached_sequence_) {
+      assertion_failure << "\n\tlast_attached_sequence_="
+                        << last_attached_sequence_;
+    }
+    if (last_scheduled_sequence_) {
+      assertion_failure << "\n\tlast_scheduled_sequence_="
+                        << last_scheduled_sequence_;
+    }
+    if (last_started_sequence_) {
+      assertion_failure << "\n\tlast_started_sequence_="
+                        << last_started_sequence_;
+    }
+    if (last_aborted_sequence_) {
+      assertion_failure << "\n\tlast_aborted_sequence_="
+                        << last_aborted_sequence_;
+    }
+    if (last_ended_sequence_) {
+      assertion_failure << "\n\tlast_ended_sequence_" << last_ended_sequence_;
+    }
+    if (last_detached_sequence_) {
+      assertion_failure << "\n\tlast_detached_sequence_="
+                        << last_detached_sequence_;
+    }
+    return assertion_failure;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::AttachedEpochIsBeforeScheduledEpoch() {
+  if (last_attached_sequence_epoch_ < last_scheduled_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The attached epoch=" << last_attached_sequence_epoch_
+           << " is NOT before the scheduled epoch="
+           << last_scheduled_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::ScheduledEpochIsBeforeStartedEpoch() {
+  if (last_scheduled_sequence_epoch_ < last_started_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The scheduled epoch=" << last_scheduled_sequence_epoch_
+           << " is NOT before the started epoch="
+           << last_started_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::StartedEpochIsBeforeEndedEpoch() {
+  if (last_started_sequence_epoch_ < last_ended_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The started epoch=" << last_started_sequence_epoch_
+           << " is NOT before the ended epoch=" << last_ended_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::StartedEpochIsBeforeAbortedEpoch() {
+  if (last_started_sequence_epoch_ < last_aborted_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The started epoch=" << last_started_sequence_epoch_
+           << " is NOT before the aborted epoch="
+           << last_aborted_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::AbortedEpochIsBeforeStartedEpoch() {
+  if (last_aborted_sequence_epoch_ < last_started_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The aborted epoch=" << last_aborted_sequence_epoch_
+           << " is NOT before the started epoch="
+           << last_started_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::AbortedEpochIsBeforeDetachedEpoch() {
+  if (last_aborted_sequence_epoch_ < last_detached_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The aborted epoch=" << last_aborted_sequence_epoch_
+           << " is NOT before the detached epoch="
+           << last_detached_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::EndedEpochIsBeforeStartedEpoch() {
+  if (last_ended_sequence_epoch_ < last_started_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The ended epoch=" << last_ended_sequence_epoch_
+           << " is NOT before the started epoch="
+           << last_started_sequence_epoch_;
+  }
+}
+
+testing::AssertionResult
+TestLayerAnimationObserver::EndedEpochIsBeforeDetachedEpoch() {
+  if (last_ended_sequence_epoch_ < last_detached_sequence_epoch_) {
+    return testing::AssertionSuccess();
+  } else {
+    return testing::AssertionFailure()
+           << "The ended epoch=" << last_ended_sequence_epoch_
+           << " is NOT before the detached epoch="
+           << last_detached_sequence_epoch_;
+  }
+}
+
 }  // namespace ui
diff --git a/ui/compositor/test/test_layer_animation_observer.h b/ui/compositor/test/test_layer_animation_observer.h
index 0f0a18e..6860b173 100644
--- a/ui/compositor/test/test_layer_animation_observer.h
+++ b/ui/compositor/test/test_layer_animation_observer.h
@@ -6,6 +6,7 @@
 #define UI_COMPOSITOR_TEST_TEST_LAYER_ANIMATION_OBSERVER_H_
 
 #include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/layer_animation_observer.h"
 
 namespace ui {
@@ -19,34 +20,110 @@
   TestLayerAnimationObserver();
   ~TestLayerAnimationObserver() override;
 
-  void OnLayerAnimationEnded(LayerAnimationSequence* sequence) override;
+  // Resets all the data tracking LayerAnimationObserver observations.
+  void ResetLayerAnimationObserverations();
 
-  void OnLayerAnimationAborted(LayerAnimationSequence* sequence) override;
-
+  // LayerAnimationObserver:
   void OnLayerAnimationScheduled(LayerAnimationSequence* sequence) override;
-
+  void OnLayerAnimationStarted(LayerAnimationSequence* sequence) override;
+  void OnLayerAnimationAborted(LayerAnimationSequence* sequence) override;
+  void OnLayerAnimationEnded(LayerAnimationSequence* sequence) override;
   bool RequiresNotificationWhenAnimatorDestroyed() const override;
 
-  const LayerAnimationSequence* last_ended_sequence() const {
-    return last_ended_sequence_;
+  const LayerAnimationSequence* last_attached_sequence() const {
+    return last_attached_sequence_;
+  }
+
+  int last_attached_sequence_epoch() const {
+    return last_attached_sequence_epoch_;
   }
 
   const LayerAnimationSequence* last_scheduled_sequence() const {
     return last_scheduled_sequence_;
   }
 
+  int last_scheduled_sequence_epoch() const {
+    return last_scheduled_sequence_epoch_;
+  }
+
+  const LayerAnimationSequence* last_started_sequence() const {
+    return last_started_sequence_;
+  }
+
+  int last_started_sequence_epoch() const {
+    return last_started_sequence_epoch_;
+  }
+
   const LayerAnimationSequence* last_aborted_sequence() const {
     return last_aborted_sequence_;
   }
 
+  int last_aborted_sequence_epoch() const {
+    return last_aborted_sequence_epoch_;
+  }
+
+  const LayerAnimationSequence* last_ended_sequence() const {
+    return last_ended_sequence_;
+  }
+
+  int last_ended_sequence_epoch() const { return last_ended_sequence_epoch_; }
+
+  const LayerAnimationSequence* last_detached_sequence() const {
+    return last_detached_sequence_;
+  }
+
+  int last_detached_sequence_epoch() const {
+    return last_detached_sequence_epoch_;
+  }
+
   void set_requires_notification_when_animator_destroyed(bool value) {
     requires_notification_when_animator_destroyed_ = value;
   }
 
+  testing::AssertionResult NoEventsObserved();
+
+  testing::AssertionResult AttachedEpochIsBeforeScheduledEpoch();
+
+  testing::AssertionResult ScheduledEpochIsBeforeStartedEpoch();
+
+  testing::AssertionResult StartedEpochIsBeforeEndedEpoch();
+
+  testing::AssertionResult StartedEpochIsBeforeAbortedEpoch();
+
+  testing::AssertionResult AbortedEpochIsBeforeStartedEpoch();
+
+  testing::AssertionResult AbortedEpochIsBeforeDetachedEpoch();
+
+  testing::AssertionResult EndedEpochIsBeforeStartedEpoch();
+
+  testing::AssertionResult EndedEpochIsBeforeDetachedEpoch();
+
+ protected:
+  // LayerAnimationObserver:
+  void OnAttachedToSequence(LayerAnimationSequence* sequence) override;
+  void OnDetachedFromSequence(LayerAnimationSequence* sequence) override;
+
  private:
-  const LayerAnimationSequence* last_ended_sequence_;
+  int next_epoch_;
+
+  const LayerAnimationSequence* last_attached_sequence_;
+  int last_attached_sequence_epoch_;
+
   const LayerAnimationSequence* last_scheduled_sequence_;
+  int last_scheduled_sequence_epoch_;
+
+  const LayerAnimationSequence* last_started_sequence_;
+  int last_started_sequence_epoch_;
+
   const LayerAnimationSequence* last_aborted_sequence_;
+  int last_aborted_sequence_epoch_;
+
+  const LayerAnimationSequence* last_ended_sequence_;
+  int last_ended_sequence_epoch_;
+
+  const LayerAnimationSequence* last_detached_sequence_;
+  int last_detached_sequence_epoch_;
+
   bool requires_notification_when_animator_destroyed_;
 
   // Copy and assign are allowed.
diff --git a/ui/events/cocoa/events_mac_unittest.mm b/ui/events/cocoa/events_mac_unittest.mm
index c4169bf..ff943b9f 100644
--- a/ui/events/cocoa/events_mac_unittest.mm
+++ b/ui/events/cocoa/events_mac_unittest.mm
@@ -109,7 +109,7 @@
         NSPointFromCGPoint(Flip(window_location).ToCGPoint());
     NSPoint screen_point = [test_window() convertBaseToScreen:window_point];
     CGFloat primary_screen_height =
-        NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
+        NSHeight([[[NSScreen screens] firstObject] frame]);
     screen_point.y = primary_screen_height - screen_point.y;
     CGEventSetLocation(scroll, NSPointToCGPoint(screen_point));
     return [NSEvent eventWithCGEvent:scroll];
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 341d876..f2f465f 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -665,6 +665,10 @@
   }
 
   if (is_android) {
+    apk_deps = [
+      "//ui/android:ui_java"
+    ]
+
     sources -= [
       # Do not run display_change_notifier_unittest.cc on Android because it
       # does not compile display_observer.cc
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index ac6e6b3..3c51f44 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -20,6 +20,7 @@
 
 # GPU memory buffer interface.
 per-file gpu_memory_buffer*=reveman@chromium.org
+per-file buffer*=reveman@chromium.org
 
 # Vector icons.
 per-file *vector_icon*=estade@chromium.org
diff --git a/ui/gfx/buffer_format_util.cc b/ui/gfx/buffer_format_util.cc
index 36222c9..33bf627 100644
--- a/ui/gfx/buffer_format_util.cc
+++ b/ui/gfx/buffer_format_util.cc
@@ -8,6 +8,27 @@
 #include "base/numerics/safe_math.h"
 
 namespace gfx {
+namespace {
+
+const BufferFormat kBufferFormats[] = {
+    BufferFormat::ATC,       BufferFormat::ATCIA,
+    BufferFormat::DXT1,      BufferFormat::DXT5,
+    BufferFormat::ETC1,      BufferFormat::R_8,
+    BufferFormat::RGBA_4444, BufferFormat::RGBA_8888,
+    BufferFormat::BGRX_8888, BufferFormat::BGRA_8888,
+    BufferFormat::UYVY_422,  BufferFormat::YUV_420_BIPLANAR,
+    BufferFormat::YUV_420};
+
+static_assert(arraysize(kBufferFormats) ==
+                  (static_cast<int>(BufferFormat::LAST) + 1),
+              "BufferFormat::LAST must be last value of kBufferFormats");
+
+}  // namespace
+
+std::vector<BufferFormat> GetBufferFormats() {
+  return std::vector<BufferFormat>(kBufferFormats,
+                                   kBufferFormats + arraysize(kBufferFormats));
+}
 
 size_t NumberOfPlanesForBufferFormat(BufferFormat format) {
   switch (format) {
diff --git a/ui/gfx/buffer_format_util.h b/ui/gfx/buffer_format_util.h
index 39df27b..f656f4ad 100644
--- a/ui/gfx/buffer_format_util.h
+++ b/ui/gfx/buffer_format_util.h
@@ -5,6 +5,8 @@
 #ifndef UI_GFX_BUFFER_FORMAT_UTIL_H_
 #define UI_GFX_BUFFER_FORMAT_UTIL_H_
 
+#include <vector>
+
 #include "base/basictypes.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
@@ -12,6 +14,9 @@
 
 namespace gfx {
 
+// Returns a vector containing all buffer formats.
+GFX_EXPORT std::vector<BufferFormat> GetBufferFormats();
+
 // Returns the number of planes for |format|.
 GFX_EXPORT size_t NumberOfPlanesForBufferFormat(BufferFormat format);
 
@@ -22,17 +27,21 @@
 
 // Returns the number of bytes used to store a row of the given zero-indexed
 // |plane| of |format|.
-GFX_EXPORT size_t RowSizeForBufferFormat(
-    size_t width, gfx::BufferFormat format, int plane);
-GFX_EXPORT bool RowSizeForBufferFormatChecked(
-    size_t width, gfx::BufferFormat format, int plane, size_t* size_in_bytes)
+GFX_EXPORT size_t RowSizeForBufferFormat(size_t width,
+                                         BufferFormat format,
+                                         int plane);
+GFX_EXPORT bool RowSizeForBufferFormatChecked(size_t width,
+                                              BufferFormat format,
+                                              int plane,
+                                              size_t* size_in_bytes)
     WARN_UNUSED_RESULT;
 
 // Returns the number of bytes used to store all the planes of a given |format|.
-GFX_EXPORT size_t BufferSizeForBufferFormat(
-    const Size& size, gfx::BufferFormat format);
-GFX_EXPORT bool BufferSizeForBufferFormatChecked(
-    const Size& size, gfx::BufferFormat format, size_t* size_in_bytes)
+GFX_EXPORT size_t BufferSizeForBufferFormat(const Size& size,
+                                            BufferFormat format);
+GFX_EXPORT bool BufferSizeForBufferFormatChecked(const Size& size,
+                                                 BufferFormat format,
+                                                 size_t* size_in_bytes)
     WARN_UNUSED_RESULT;
 
 }  // namespace gfx
diff --git a/ui/gfx/color_profile_mac_unittest.mm b/ui/gfx/color_profile_mac_unittest.mm
index 26dd290..3910a9d2 100644
--- a/ui/gfx/color_profile_mac_unittest.mm
+++ b/ui/gfx/color_profile_mac_unittest.mm
@@ -51,7 +51,7 @@
   }
 
   NSRect PrimaryScreenFrame() {
-    return [[[NSScreen screens] objectAtIndex:0] frame];
+    return [[[NSScreen screens] firstObject] frame];
   }
 };
 
diff --git a/ui/gfx/mac/coordinate_conversion.mm b/ui/gfx/mac/coordinate_conversion.mm
index 54b16e9..b31ce9e 100644
--- a/ui/gfx/mac/coordinate_conversion.mm
+++ b/ui/gfx/mac/coordinate_conversion.mm
@@ -16,7 +16,7 @@
 // The height of the primary display, which OSX defines as the monitor with the
 // menubar. This is always at index 0.
 CGFloat PrimaryDisplayHeight() {
-  return NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]);
+  return NSMaxY([[[NSScreen screens] firstObject] frame]);
 }
 
 }  // namespace
diff --git a/ui/gfx/mac/coordinate_conversion_unittest.mm b/ui/gfx/mac/coordinate_conversion_unittest.mm
index ac882fa6..ccddbe8 100644
--- a/ui/gfx/mac/coordinate_conversion_unittest.mm
+++ b/ui/gfx/mac/coordinate_conversion_unittest.mm
@@ -48,7 +48,7 @@
 void MacCoordinateConversionTest::SetUp() {
   // Before swizzling, do a sanity check that the primary screen's origin is
   // (0, 0). This should always be true.
-  NSRect primary_screen_frame = [[[NSScreen screens] objectAtIndex:0] frame];
+  NSRect primary_screen_frame = [[[NSScreen screens] firstObject] frame];
   EXPECT_EQ(0, primary_screen_frame.origin.x);
   EXPECT_EQ(0, primary_screen_frame.origin.y);
 
@@ -57,7 +57,7 @@
       [MacCoordinateConversionTestScreenDonor class],
       @selector(frame)));
 
-  primary_screen_frame = [[[NSScreen screens] objectAtIndex:0] frame];
+  primary_screen_frame = [[[NSScreen screens] firstObject] frame];
   EXPECT_EQ(kTestWidth, primary_screen_frame.size.width);
   EXPECT_EQ(kTestHeight, primary_screen_frame.size.height);
 }
diff --git a/ui/gfx/screen_mac.mm b/ui/gfx/screen_mac.mm
index abd8549..e23feede 100644
--- a/ui/gfx/screen_mac.mm
+++ b/ui/gfx/screen_mac.mm
@@ -24,7 +24,7 @@
 gfx::Rect ConvertCoordinateSystem(NSRect ns_rect) {
   // Primary monitor is defined as the monitor with the menubar,
   // which is always at index 0.
-  NSScreen* primary_screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* primary_screen = [[NSScreen screens] firstObject];
   float primary_screen_height = [primary_screen frame].size.height;
   gfx::Rect rect(NSRectToCGRect(ns_rect));
   rect.set_y(primary_screen_height - rect.y() - rect.height());
@@ -58,7 +58,7 @@
 
   gfx::Display display(display_id, gfx::Rect(NSRectToCGRect(frame)));
   NSRect visible_frame = [screen visibleFrame];
-  NSScreen* primary = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* primary = [[NSScreen screens] firstObject];
 
   // Convert work area's coordinate systems.
   if ([screen isEqual:primary]) {
@@ -103,7 +103,7 @@
   gfx::Point GetCursorScreenPoint() override {
     NSPoint mouseLocation  = [NSEvent mouseLocation];
     // Flip coordinates to gfx (0,0 in top-left corner) using primary screen.
-    NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+    NSScreen* screen = [[NSScreen screens] firstObject];
     mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y;
     return gfx::Point(mouseLocation.x, mouseLocation.y);
   }
@@ -161,7 +161,7 @@
   gfx::Display GetPrimaryDisplay() const override {
     // Primary display is defined as the display with the menubar,
     // which is always at index 0.
-    NSScreen* primary = [[NSScreen screens] objectAtIndex:0];
+    NSScreen* primary = [[NSScreen screens] firstObject];
     gfx::Display display = GetDisplayForScreen(primary);
     return display;
   }
diff --git a/ui/gl/gl_enums_implementation_autogen.h b/ui/gl/gl_enums_implementation_autogen.h
index 38efaed..54dea5a 100644
--- a/ui/gl/gl_enums_implementation_autogen.h
+++ b/ui/gl/gl_enums_implementation_autogen.h
@@ -2857,6 +2857,9 @@
         0x00100000, "GL_STENCIL_BUFFER_BIT4_QCOM",
     },
     {
+        24, "GL_SYNC_TOKEN_SIZE_CHROMIUM",
+    },
+    {
         0x8E4E, "GL_LAST_VERTEX_CONVENTION_EXT",
     },
     {
diff --git a/ui/message_center/cocoa/popup_collection.mm b/ui/message_center/cocoa/popup_collection.mm
index 5961e614..05a6bdde 100644
--- a/ui/message_center/cocoa/popup_collection.mm
+++ b/ui/message_center/cocoa/popup_collection.mm
@@ -150,7 +150,7 @@
 - (NSRect)screenFrame {
   if (!NSIsEmptyRect(testingScreenFrame_))
     return testingScreenFrame_;
-  return [[[NSScreen screens] objectAtIndex:0] visibleFrame];
+  return [[[NSScreen screens] firstObject] visibleFrame];
 }
 
 - (BOOL)addNotification:(const message_center::Notification*)notification {
diff --git a/ui/message_center/message_center_style.cc b/ui/message_center/message_center_style.cc
index 3413149e..33f8757 100644
--- a/ui/message_center/message_center_style.cc
+++ b/ui/message_center/message_center_style.cc
@@ -54,6 +54,10 @@
 // Timing.
 const int kAutocloseDefaultDelaySeconds = 8;
 const int kAutocloseHighPriorityDelaySeconds = 25;
+// Web notifications use a larger timeout for now, which improves re-engagement.
+// TODO(johnme): Use Finch to experiment with different values for this, then
+// consider replacing kAutocloseDefaultDelaySeconds with this.
+const int kAutocloseWebNotificationDelaySeconds = 20;
 
 // Colors.
 const SkColor kBackgroundLightColor = SkColorSetRGB(0xf1, 0xf1, 0xf1);
diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h
index 51ab6652..6c1ac96 100644
--- a/ui/message_center/message_center_style.h
+++ b/ui/message_center/message_center_style.h
@@ -101,6 +101,7 @@
 // Timing.
 extern const int kAutocloseDefaultDelaySeconds;
 extern const int kAutocloseHighPriorityDelaySeconds;
+extern const int kAutocloseWebNotificationDelaySeconds;
 
 // Buttons.
 const int kButtonHeight = 38;              // In DIPs.
diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc
index af35e57..a39f7c7 100644
--- a/ui/message_center/notification.cc
+++ b/ui/message_center/notification.cc
@@ -26,6 +26,7 @@
 
 RichNotificationData::RichNotificationData()
     : priority(DEFAULT_PRIORITY),
+      is_web_notification(false),
       never_timeout(false),
       timestamp(base::Time::Now()),
       context_message(base::string16()),
@@ -36,6 +37,7 @@
 
 RichNotificationData::RichNotificationData(const RichNotificationData& other)
     : priority(other.priority),
+      is_web_notification(other.is_web_notification),
       never_timeout(other.never_timeout),
       timestamp(other.timestamp),
       context_message(other.context_message),
@@ -139,6 +141,7 @@
   is_read_ = base->is_read_;
   if (!delegate_.get())
     delegate_ = base->delegate();
+  optional_fields_.is_web_notification = base->is_web_notification();
   optional_fields_.never_timeout = base->never_timeout();
 }
 
diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h
index 3b63e54..41dfa21 100644
--- a/ui/message_center/notification.h
+++ b/ui/message_center/notification.h
@@ -41,6 +41,7 @@
   ~RichNotificationData();
 
   int priority;
+  bool is_web_notification;
   bool never_timeout;
   base::Time timestamp;
   base::string16 context_message;
@@ -75,7 +76,7 @@
   virtual ~Notification();
 
   // Copies the internal on-memory state from |base|, i.e. shown_as_popup,
-  // is_read, and never_timeout.
+  // is_read, is_web_notification and never_timeout.
   void CopyState(Notification* base);
 
   NotificationType type() const { return type_; }
@@ -193,14 +194,20 @@
   // The notification with lesser serial_number is considered 'older'.
   unsigned serial_number() { return serial_number_; }
 
-  // Marks this explicitly to prevent the timeout dismiss of notification.
-  // This is used by webkit notifications to keep the existing behavior.
+  // Gets and sets whether this was shown using the Web Notifications API.
+  bool is_web_notification() const {
+    return optional_fields_.is_web_notification;
+  }
+  void set_is_web_notification(bool is_web_notification) {
+    optional_fields_.is_web_notification = is_web_notification;
+  }
+
+  // Gets and sets whether the notifiction should remain onscreen permanently.
+  bool never_timeout() const { return optional_fields_.never_timeout; }
   void set_never_timeout(bool never_timeout) {
     optional_fields_.never_timeout = never_timeout;
   }
 
-  bool never_timeout() const { return optional_fields_.never_timeout; }
-
   bool clickable() const { return optional_fields_.clickable; }
   void set_clickable(bool clickable) {
     optional_fields_.clickable = clickable;
diff --git a/ui/message_center/popup_timer.cc b/ui/message_center/popup_timer.cc
index aa45d06..f2f9c5e0 100644
--- a/ui/message_center/popup_timer.cc
+++ b/ui/message_center/popup_timer.cc
@@ -11,21 +11,20 @@
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_list.h"
 
+namespace message_center {
+
 namespace {
 
-base::TimeDelta GetTimeoutForPriority(int priority) {
-  if (priority > message_center::DEFAULT_PRIORITY) {
-    return base::TimeDelta::FromSeconds(
-        message_center::kAutocloseHighPriorityDelaySeconds);
-  }
-  return base::TimeDelta::FromSeconds(
-      message_center::kAutocloseDefaultDelaySeconds);
+base::TimeDelta GetTimeoutForNotification(Notification* notification) {
+  if (notification->priority() > message_center::DEFAULT_PRIORITY)
+    return base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds);
+  if (notification->is_web_notification())
+    return base::TimeDelta::FromSeconds(kAutocloseWebNotificationDelaySeconds);
+  return base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds);
 }
 
 }  // namespace
 
-namespace message_center {
-
 ////////////////////////////////////////////////////////////////////////////////
 // PopupTimer
 
@@ -165,7 +164,7 @@
 
   // Start the timer if not yet.
   if (popup_timers_.find(id) == popup_timers_.end())
-    StartTimer(id, GetTimeoutForPriority((*iter)->priority()));
+    StartTimer(id, GetTimeoutForNotification(*iter));
 }
 
 void PopupTimersController::OnNotificationRemoved(const std::string& id,
diff --git a/ui/ozone/common/stub_client_native_pixmap_factory.cc b/ui/ozone/common/stub_client_native_pixmap_factory.cc
index d076a0c..112a7ff 100644
--- a/ui/ozone/common/stub_client_native_pixmap_factory.cc
+++ b/ui/ozone/common/stub_client_native_pixmap_factory.cc
@@ -15,8 +15,9 @@
 
   // ClientNativePixmapFactory:
   void Initialize(base::ScopedFD device_fd) override {}
-  std::vector<Configuration> GetSupportedConfigurations() const override {
-    return std::vector<Configuration>();
+  bool IsConfigurationSupported(gfx::BufferFormat format,
+                                gfx::BufferUsage usage) const override {
+    return false;
   }
   scoped_ptr<ClientNativePixmap> ImportFromHandle(
       const gfx::NativePixmapHandle& handle,
diff --git a/ui/ozone/platform/drm/common/client_native_pixmap_factory_gbm.cc b/ui/ozone/platform/drm/common/client_native_pixmap_factory_gbm.cc
index af7042d44..f5dfd78 100644
--- a/ui/ozone/platform/drm/common/client_native_pixmap_factory_gbm.cc
+++ b/ui/ozone/platform/drm/common/client_native_pixmap_factory_gbm.cc
@@ -44,21 +44,23 @@
     vgem_fd_ = device_fd.Pass();
 #endif
   }
-  std::vector<Configuration> GetSupportedConfigurations() const override {
-    const Configuration kConfigurations[] = {
-        {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT},
-        {gfx::BufferFormat::BGRX_8888, gfx::BufferUsage::SCANOUT}};
-    std::vector<Configuration> configurations(
-        kConfigurations, kConfigurations + arraysize(kConfigurations));
+  bool IsConfigurationSupported(gfx::BufferFormat format,
+                                gfx::BufferUsage usage) const override {
+    switch (usage) {
+      case gfx::BufferUsage::SCANOUT:
+        return format == gfx::BufferFormat::BGRA_8888 ||
+               format == gfx::BufferFormat::BGRX_8888;
+      case gfx::BufferUsage::MAP:
+      case gfx::BufferUsage::PERSISTENT_MAP: {
 #if defined(USE_VGEM_MAP)
-    if (vgem_fd_.is_valid()) {
-      configurations.push_back(
-          {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::MAP});
-      configurations.push_back(
-          {gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::PERSISTENT_MAP});
-    }
+        return vgem_fd_.is_valid() && format == gfx::BufferFormat::BGRA_8888;
+#else
+        return false;
 #endif
-    return configurations;
+      }
+    }
+    NOTREACHED();
+    return false;
   }
   scoped_ptr<ClientNativePixmap> ImportFromHandle(
       const gfx::NativePixmapHandle& handle,
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 845cb41..5e100873 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -4,6 +4,7 @@
 
 #include "ui/ozone/platform/drm/common/drm_util.h"
 
+#include <drm_fourcc.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/mman.h>
@@ -264,4 +265,16 @@
   return params;
 }
 
+int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format) {
+  switch (format) {
+    case gfx::BufferFormat::BGRA_8888:
+      return DRM_FORMAT_ARGB8888;
+    case gfx::BufferFormat::BGRX_8888:
+      return DRM_FORMAT_XRGB8888;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index e761aba2..a79700e9 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -56,6 +56,8 @@
     size_t display_index,
     const gfx::Point& origin);
 
+int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format);
+
 }  // namespace ui
 
 #endif  // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
diff --git a/ui/ozone/platform/drm/gpu/drm_window.cc b/ui/ozone/platform/drm/gpu/drm_window.cc
index 22e99cb..969224c4 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window.cc
@@ -12,6 +12,7 @@
 #include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
@@ -34,20 +35,6 @@
 void EmptyFlipCallback(gfx::SwapResult) {
 }
 
-// TODO(kalyank): We now have this switch statement in GBMBuffer and here.
-// It would be nice to have it in one place.
-uint32_t GetFourCCFormatFromBufferFormat(gfx::BufferFormat format) {
-  switch (format) {
-    case gfx::BufferFormat::BGRA_8888:
-      return DRM_FORMAT_ARGB8888;
-    case gfx::BufferFormat::BGRX_8888:
-      return DRM_FORMAT_XRGB8888;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
 void UpdateCursorImage(DrmBuffer* cursor, const SkBitmap& image) {
   SkRect damage;
   image.getBounds(&damage);
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
index 045caad..117a52f7 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -14,6 +14,7 @@
 #include "base/trace_event/trace_event.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/native_pixmap_handle_ozone.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/gbm_device.h"
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
@@ -21,22 +22,6 @@
 
 namespace ui {
 
-namespace {
-
-int GetGbmFormatFromBufferFormat(gfx::BufferFormat fmt) {
-  switch (fmt) {
-    case gfx::BufferFormat::BGRA_8888:
-      return GBM_FORMAT_ARGB8888;
-    case gfx::BufferFormat::BGRX_8888:
-      return GBM_FORMAT_XRGB8888;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
-}  // namespace
-
 GbmBuffer::GbmBuffer(const scoped_refptr<GbmDevice>& gbm,
                      gbm_bo* bo,
                      gfx::BufferUsage usage)
@@ -62,7 +47,7 @@
   if (use_scanout)
     flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
   gbm_bo* bo = gbm_bo_create(gbm->device(), size.width(), size.height(),
-                             GetGbmFormatFromBufferFormat(format), flags);
+                             GetFourCCFormatFromBufferFormat(format), flags);
   if (!bo)
     return nullptr;
 
diff --git a/ui/ozone/public/client_native_pixmap_factory.h b/ui/ozone/public/client_native_pixmap_factory.h
index 943547c..f0593b5c 100644
--- a/ui/ozone/public/client_native_pixmap_factory.h
+++ b/ui/ozone/public/client_native_pixmap_factory.h
@@ -34,13 +34,9 @@
   // Initialize with the given client native pixmap |device_fd|.
   virtual void Initialize(base::ScopedFD device_fd) = 0;
 
-  struct Configuration {
-    gfx::BufferFormat format;
-    gfx::BufferUsage usage;
-  };
-
-  // Gets supported format/usage configurations.
-  virtual std::vector<Configuration> GetSupportedConfigurations() const = 0;
+  // Returns true if format/usage configuration is supported.
+  virtual bool IsConfigurationSupported(gfx::BufferFormat format,
+                                        gfx::BufferUsage usage) const = 0;
 
   // TODO(dshwang): implement it. crbug.com/475633
   // Import the native pixmap from |handle| to be used in non-GPU processes.
diff --git a/ui/snapshot/snapshot_mac.mm b/ui/snapshot/snapshot_mac.mm
index aa4048e..d1eb698 100644
--- a/ui/snapshot/snapshot_mac.mm
+++ b/ui/snapshot/snapshot_mac.mm
@@ -20,7 +20,7 @@
                       std::vector<unsigned char>* png_representation,
                       const gfx::Rect& snapshot_bounds) {
   NSWindow* window = [view window];
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   gfx::Rect screen_bounds = gfx::Rect(NSRectToCGRect([screen frame]));
 
 
diff --git a/ui/views/event_monitor_mac.mm b/ui/views/event_monitor_mac.mm
index cb3e54e9..45578c9 100644
--- a/ui/views/event_monitor_mac.mm
+++ b/ui/views/event_monitor_mac.mm
@@ -30,7 +30,7 @@
 gfx::Point EventMonitor::GetLastMouseLocation() {
   NSPoint mouseLocation = [NSEvent mouseLocation];
   // Flip coordinates to gfx (0,0 in top-left corner) using primary screen.
-  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSScreen* screen = [[NSScreen screens] firstObject];
   mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y;
   return gfx::Point(mouseLocation.x, mouseLocation.y);
 }
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 0ff4fbf2..2670ed6b 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -1081,7 +1081,7 @@
   params.bounds = gfx::Rect(100, 100, 300, 200);
   widget.Init(params);
   widget.Show();
-  NSRect expected = [[[NSScreen screens] objectAtIndex:0] visibleFrame];
+  NSRect expected = [[[NSScreen screens] firstObject] visibleFrame];
   NSRect actual = gfx::ScreenRectToNSRect(widget.GetWorkAreaBoundsInScreen());
   EXPECT_FALSE(NSIsEmptyRect(actual));
   EXPECT_NSEQ(expected, actual);
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.html b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.html
index 73562a4..00c701d 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.html
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.html
@@ -11,7 +11,7 @@
       <div id="container" class="layout vertical flex">
         <template is="dom-repeat" items="[[networks]]">
           <cr-network-list-item network-state="[[item]]"
-              list-item-type="[[listType]]" on-click="onSelected_">
+              list-item-type="[[listType]]" on-tap="onTap_">
           </cr-network-list-item>
         </template>
       </div>
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.js b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.js
index 070614a9..04962be3 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.js
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list.js
@@ -64,12 +64,12 @@
   },
 
   /**
-   * Event triggered when a list item is selected.
+   * Event triggered when a list item is tapped.
    * @param {!{model: {item: !CrOnc.NetworkStateProperties}}} event
    * @private
    */
-  onSelected_: function(event) {
+  onTap_: function(event) {
     this.fire('selected', event.model.item);
-  }
+  },
 });
 })();
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.css b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.css
index 3f1154d..0f1b10f9 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.css
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.css
@@ -58,8 +58,17 @@
   font-size: 14px;
 }
 
-#divButtons paper-icon-button {
+.buttons {
+  align-items: center;
+  display: flex;
+  flex-direction: row;
+}
+
+.buttons paper-icon-button {
   text-align: center;
+}
+
+.known paper-icon-button {
   width: 60px;
 }
 
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.html b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.html
index 869316c..ef98ac0 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.html
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.html
@@ -19,17 +19,22 @@
         <span id="networkStateText" hidden$="[[isListItem_(listItemType)]]">
         </span>
       </div>
-      <div id="divButtons" class="layout horizontal center"
+      <div class="buttons"
+          hidden$="[[!isListItemType_(listItemType, 'visible')]]">
+        <paper-icon-button icon="settings" on-tap="fireShowDetails_">
+        </paper-icon-button>
+      </div>
+      <div class="known buttons"
           hidden$="[[!isListItemType_(listItemType, 'known')]]">
         <paper-icon-button icon="[[sharedIcon_(networkState)]]" disabled>
         </paper-icon-button>
         <paper-icon-button icon="[[preferredIcon_(networkState)]]"
             disabled$="[[isPolicyManaged_(networkState)]]"
-            on-click="fireTogglePreferred_">
+            on-tap="fireTogglePreferred_">
         </paper-icon-button>
         <paper-icon-button icon="clear"
             disabled$="[[isPolicyManaged_(networkState)]]"
-            on-click="fireRemove_">
+            on-tap="fireRemove_">
         </paper-icon-button>
       </div>
     </div>
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.js b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.js
index 6b6aefd05..84d2c137 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.js
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_network_list_item.js
@@ -158,6 +158,16 @@
   },
 
   /**
+   * Fires a 'show-details' event with |this.networkState| as the details.
+   * @param {Event} event
+   * @private
+   */
+  fireShowDetails_: function(event) {
+    this.fire('show-detail', this.networkState);
+    event.stopPropagation();
+  },
+
+  /**
    * Fires the 'toggle-preferred' event with |this.networkState| as the details.
    * @param {Event} event
    * @private
diff --git a/ui/webui/resources/cr_elements/v1_0/network/cr_onc_types.js b/ui/webui/resources/cr_elements/v1_0/network/cr_onc_types.js
index 8ff05c4..1d78635 100644
--- a/ui/webui/resources/cr_elements/v1_0/network/cr_onc_types.js
+++ b/ui/webui/resources/cr_elements/v1_0/network/cr_onc_types.js
@@ -257,31 +257,42 @@
   var type = properties.Type;
   if (type == CrOnc.Type.CELLULAR && properties.Cellular)
     return properties.Cellular.SignalStrength || 0;
-  else if (type == CrOnc.Type.WI_FI && properties.WiFi)
+  if (type == CrOnc.Type.WI_FI && properties.WiFi)
     return properties.WiFi.SignalStrength || 0;
-  else if (type == CrOnc.Type.WI_MAX && properties.WiMAX)
+  if (type == CrOnc.Type.WI_MAX && properties.WiMAX)
     return properties.WiMAX.SignalStrength || 0;
   return 0;
 }
 
 /**
+ * Gets the Managed AutoConnect dictionary from |properties| based on
+ * properties.Type.
+ * @param {!CrOnc.NetworkProperties|undefined}
+ *     properties The ONC network properties or state properties.
+ * @return {!chrome.networkingPrivate.ManagedBoolean|undefined} The AutoConnect
+ *     managed dictionary or undefined.
+ */
+CrOnc.getManagedAutoConnect = function(properties) {
+  var type = properties.Type;
+  if (type == CrOnc.Type.CELLULAR && properties.Cellular)
+    return properties.Cellular.AutoConnect;
+  if (type == CrOnc.Type.VPN && properties.VPN)
+    return properties.VPN.AutoConnect;
+  if (type == CrOnc.Type.WI_FI && properties.WiFi)
+    return properties.WiFi.AutoConnect;
+  if (type == CrOnc.Type.WI_MAX && properties.WiMAX)
+    return properties.WiMAX.AutoConnect;
+  return undefined;
+}
+
+/**
  * Gets the AutoConnect value from |properties| based on properties.Type.
  * @param {!CrOnc.NetworkProperties|undefined}
  *     properties The ONC network properties or state properties.
  * @return {boolean} The AutoConnect value if it exists or false.
  */
 CrOnc.getAutoConnect = function(properties) {
-  var type = properties.Type;
-  /** @type {!chrome.networkingPrivate.ManagedBoolean|undefined} */
-  var autoconnect;
-  if (type == CrOnc.Type.CELLULAR && properties.Cellular)
-    autoconnect = properties.Cellular.AutoConnect;
-  else if (type == CrOnc.Type.VPN && properties.VPN)
-    autoconnect = properties.VPN.AutoConnect;
-  else if (type == CrOnc.Type.WI_FI && properties.WiFi)
-    autoconnect = properties.WiFi.AutoConnect;
-  else if (type == CrOnc.Type.WI_MAX && properties.WiMAX)
-    autoconnect = properties.WiMAX.AutoConnect;
+  var autoconnect = CrOnc.getManagedAutoConnect(properties);
   return !!CrOnc.getActiveValue(autoconnect);
 }