diff --git a/BUILD.gn b/BUILD.gn
index f1af528..deb2f340 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -403,6 +403,7 @@
       "//base:base_junit_tests",
       "//base/android/linker:chromium_android_linker",
       "//build/android/gyp/test:hello_world",
+      "//build/android/rezip",
       "//chrome/android/webapk/shell_apk:webapk",
       "//components/invalidation/impl:components_invalidation_impl_junit_tests",
       "//components/policy/android:components_policy_junit_tests",
diff --git a/DEPS b/DEPS
index b116334..40e836b 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,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': 'ddff43aa5e696dad75c823ebcf8258923c0d0257',
+  'skia_revision': '578f52c6cf6372b88a88a05dee0efc5b67aa9a9c',
   # 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': '97a4bb81ea73f7640508764e71abc49f381f00ea',
+  'v8_revision': 'f8f09ec50b66b07307fca903e3b8108910f92b02',
   # 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.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'd90198385c2ec0429039558b3f2f6b968e5172f4',
+  'angle_revision': 'b8a580a3b2d66ebbf43c7c1c9be703d3af432e4b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '489a5bc40e983273304af2375580900819b1389d',
+  'catapult_revision': '810f05af27d2dedd11d05055a792d1f0dd7cd637',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -228,7 +228,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'ba5e7e291afb87d7eccf524fa26f0ed81f8a34cd', # commit position 15707
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '4f48669246d80eca01be30540878ffeccc374c0c', # commit position 15753
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -427,7 +427,7 @@
 
     # Userspace interface to kernel DRM services.
     'src/third_party/libdrm/src':
-      Var('chromium_git') + '/chromiumos/third_party/libdrm.git' + '@' + '4c4778123b7b11f750577199644f5d6820851dbb',
+      Var('chromium_git') + '/chromiumos/third_party/libdrm.git' + '@' + '0ce18bedd3e62d4784fa755403801934ba171084',
 
     # Display server protocol for Linux.
     'src/third_party/wayland/src':
diff --git a/WATCHLISTS b/WATCHLISTS
index 0982bc27..a5330aa 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1492,7 +1492,6 @@
                         'piman+watch@chromium.org'],
     'auto_bisect': ['auto-bisect-reviews@chromium.org'],
     'autofill': ['estade+watch@chromium.org',
-                 'jdonnelly+autofillwatch@chromium.org',
                  'rouslan+autofill@chromium.org',
                  'vabr+watchlistautofill@chromium.org',
                  'mathp+autofillwatch@chromium.org',
@@ -1647,6 +1646,7 @@
     'blink_paint' : ['blink-reviews-paint@chromium.org',
                      'dongseong.hwang@intel.com'],
     'blink_permissions': ['mlamouri+watch-blink@chromium.org'],
+    'blink_platform': ['kinuko+watch@chromium.org'],
     'blink_platform_graphics': ['ajuma+watch@chromium.org',
                                 'blink-reviews-platform-graphics@chromium.org',
                                 'cabanier@adobe.com',
@@ -1822,8 +1822,7 @@
     'developer_recommended_flags': ['scheib+watch@chromium.org'],
     'devtools': ['devtools-reviews@chromium.org',
                  'pfeldman@chromium.org'],
-    'disk_cache': ['kinuko+cache@chromium.org',
-                   'gavinp+disk@chromium.org'],
+    'disk_cache': ['gavinp+disk@chromium.org'],
     'download': ['asanka@chromium.org'],
     'downloads_ui': ['asanka@chromium.org',
                      'dbeam+watch-downloads@chromium.org'],
diff --git a/android_webview/tools/apk_merger.py b/android_webview/tools/apk_merger.py
index 713a58d..74eac37 100755
--- a/android_webview/tools/apk_merger.py
+++ b/android_webview/tools/apk_merger.py
@@ -135,7 +135,8 @@
 
 
 def SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, zipalign_path,
-                    keystore_path, key_name, key_password):
+                    keystore_path, key_name, key_password,
+                    page_align_shared_libraries):
   try:
     finalize_apk.JarSigner(
         keystore_path,
@@ -148,6 +149,7 @@
 
   try:
     finalize_apk.AlignApk(zipalign_path,
+                          page_align_shared_libraries,
                           signed_tmp_apk,
                           new_apk)
   except build_utils.CalledProcessError as e:
@@ -222,8 +224,7 @@
   parser.add_argument('--key_name', required=True)
   parser.add_argument('--key_password', required=True)
   parser.add_argument('--shared_library')
-  parser.add_argument('--page-align-shared-libraries', action='store_true',
-                      help='Obsolete, but remains for backwards compatibility')
+  parser.add_argument('--page-align-shared-libraries', action='store_true')
   parser.add_argument('--uncompress-shared-libraries', action='store_true')
   parser.add_argument('--debug', action='store_true')
   # This option shall only used in debug build, see http://crbug.com/631494.
@@ -249,7 +250,8 @@
     MergeApk(args, tmp_apk, tmp_dir_32, tmp_dir_64)
 
     SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, args.zipalign_path,
-                    args.keystore_path, args.key_name, args.key_password)
+                    args.keystore_path, args.key_name, args.key_password,
+                    args.page_align_shared_libraries)
 
   except ApkMergeFailure as e:
     print e
diff --git a/ash/common/system/tray/actionable_view.cc b/ash/common/system/tray/actionable_view.cc
index 2dd90ed05..bc97b678 100644
--- a/ash/common/system/tray/actionable_view.cc
+++ b/ash/common/system/tray/actionable_view.cc
@@ -42,14 +42,10 @@
 }
 
 void ActionableView::OnPaintFocus(gfx::Canvas* canvas) {
-  gfx::RectF rect(GetFocusBounds());
+  gfx::RectF rect(GetLocalBounds());
   canvas->DrawSolidFocusRect(rect, kFocusBorderColor, kFocusBorderThickness);
 }
 
-gfx::Rect ActionableView::GetFocusBounds() {
-  return GetLocalBounds();
-}
-
 void ActionableView::HandlePerformActionResult(bool action_performed,
                                                const ui::Event& event) {
   AnimateInkDrop(action_performed ? views::InkDropState::ACTION_TRIGGERED
diff --git a/ash/common/system/tray/actionable_view.h b/ash/common/system/tray/actionable_view.h
index bcb71349..9181912 100644
--- a/ash/common/system/tray/actionable_view.h
+++ b/ash/common/system/tray/actionable_view.h
@@ -48,9 +48,6 @@
   // color. Subclasses can override to change the default settings.
   virtual void OnPaintFocus(gfx::Canvas* canvas);
 
-  // Returns the bounds to paint the focus rectangle in.
-  virtual gfx::Rect GetFocusBounds();
-
   // Performs an action when user clicks on the view (on mouse-press event), or
   // presses a key when this view is in focus. Returns true if the event has
   // been handled and an action was performed. Returns false otherwise.
diff --git a/ash/common/system/tray/tray_background_view.cc b/ash/common/system/tray/tray_background_view.cc
index 59858820..17ac4b59 100644
--- a/ash/common/system/tray/tray_background_view.cc
+++ b/ash/common/system/tray/tray_background_view.cc
@@ -560,16 +560,18 @@
   ActionableView::HandlePerformActionResult(action_performed, event);
 }
 
-gfx::Rect TrayBackgroundView::GetFocusBounds() {
+void TrayBackgroundView::OnPaintFocus(gfx::Canvas* canvas) {
   // The tray itself expands to the right and bottom edge of the screen to make
   // sure clicking on the edges brings up the popup. However, the focus border
   // should be only around the container.
-  return GetContentsBounds();
-}
-
-void TrayBackgroundView::OnPaintFocus(gfx::Canvas* canvas) {
-  gfx::RectF paint_bounds(GetFocusBounds());
-  paint_bounds.Inset(gfx::Insets(2, -2));
+  gfx::RectF paint_bounds;
+  if (MaterialDesignController::IsShelfMaterial()) {
+    paint_bounds = gfx::RectF(GetBackgroundBounds());
+    paint_bounds.Inset(gfx::Insets(-kFocusBorderThickness));
+  } else {
+    paint_bounds = gfx::RectF(GetContentsBounds());
+    paint_bounds.Inset(gfx::Insets(1));
+  }
   canvas->DrawSolidFocusRect(paint_bounds, kFocusBorderColor,
                              kFocusBorderThickness);
 }
diff --git a/ash/common/system/tray/tray_background_view.h b/ash/common/system/tray/tray_background_view.h
index e56fb78f..4788f23 100644
--- a/ash/common/system/tray/tray_background_view.h
+++ b/ash/common/system/tray/tray_background_view.h
@@ -142,7 +142,6 @@
   bool PerformAction(const ui::Event& event) override;
   void HandlePerformActionResult(bool action_performed,
                                  const ui::Event& event) override;
-  gfx::Rect GetFocusBounds() override;
   void OnPaintFocus(gfx::Canvas* canvas) override;
 
  private:
diff --git a/base/bit_cast.h b/base/bit_cast.h
index c9514bc..90dd925 100644
--- a/base/bit_cast.h
+++ b/base/bit_cast.h
@@ -9,6 +9,7 @@
 #include <type_traits>
 
 #include "base/compiler_specific.h"
+#include "base/template_util.h"
 #include "build/build_config.h"
 
 // bit_cast<Dest,Source> is a template function that implements the equivalent
@@ -63,34 +64,10 @@
 inline Dest bit_cast(const Source& source) {
   static_assert(sizeof(Dest) == sizeof(Source),
                 "bit_cast requires source and destination to be the same size");
-
-#if (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) || \
-     (defined(__clang__) && defined(_LIBCPP_VERSION)))
-  // GCC 5.1 contains the first libstdc++ with is_trivially_copyable.
-  // Assume libc++ Just Works: is_trivially_copyable added on May 13th 2011.
-  // However, with libc++ when GCC is the compiler the trait is buggy, see
-  // crbug.com/607158, so fall back to the less strict variant for non-clang.
-  static_assert(std::is_trivially_copyable<Dest>::value,
-                "non-trivially-copyable bit_cast is undefined");
-  static_assert(std::is_trivially_copyable<Source>::value,
-                "non-trivially-copyable bit_cast is undefined");
-#elif HAS_FEATURE(is_trivially_copyable)
-  // The compiler supports an equivalent intrinsic.
-  static_assert(__is_trivially_copyable(Dest),
-                "non-trivially-copyable bit_cast is undefined");
-  static_assert(__is_trivially_copyable(Source),
-                "non-trivially-copyable bit_cast is undefined");
-#elif COMPILER_GCC
-  // Fallback to compiler intrinsic on GCC and clang (which pretends to be
-  // GCC). This isn't quite the same as is_trivially_copyable but it'll do for
-  // our purpose.
-  static_assert(__has_trivial_copy(Dest),
-                "non-trivially-copyable bit_cast is undefined");
-  static_assert(__has_trivial_copy(Source),
-                "non-trivially-copyable bit_cast is undefined");
-#else
-  // Do nothing, let the bots handle it.
-#endif
+  static_assert(base::is_trivially_copyable<Dest>::value,
+                "bit_cast requires the destination type to be copyable");
+  static_assert(base::is_trivially_copyable<Source>::value,
+                "bit_cast requires the source type to be copyable");
 
   Dest dest;
   memcpy(&dest, &source, sizeof(dest));
diff --git a/base/bit_cast_unittest.cc b/base/bit_cast_unittest.cc
index 757b0c65..f36d3fe 100644
--- a/base/bit_cast_unittest.cc
+++ b/base/bit_cast_unittest.cc
@@ -27,6 +27,5 @@
   EXPECT_EQ(1, b);
 }
 
-
 }  // namespace
 }  // namespace base
diff --git a/base/supports_user_data.cc b/base/supports_user_data.cc
index 6ba3ff6..d511055 100644
--- a/base/supports_user_data.cc
+++ b/base/supports_user_data.cc
@@ -9,12 +9,13 @@
 namespace base {
 
 SupportsUserData::SupportsUserData() {
-  // Harmless to construct on a different thread to subsequent usage.
-  thread_checker_.DetachFromThread();
+  // Harmless to construct on a different execution sequence to subsequent
+  // usage.
+  sequence_checker_.DetachFromSequence();
 }
 
 SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(sequence_checker_.CalledOnValidSequence());
   DataMap::const_iterator found = user_data_.find(key);
   if (found != user_data_.end())
     return found->second.get();
@@ -22,21 +23,21 @@
 }
 
 void SupportsUserData::SetUserData(const void* key, Data* data) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(sequence_checker_.CalledOnValidSequence());
   user_data_[key] = WrapUnique(data);
 }
 
 void SupportsUserData::RemoveUserData(const void* key) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(sequence_checker_.CalledOnValidSequence());
   user_data_.erase(key);
 }
 
-void SupportsUserData::DetachUserDataThread() {
-  thread_checker_.DetachFromThread();
+void SupportsUserData::DetachFromSequence() {
+  sequence_checker_.DetachFromSequence();
 }
 
 SupportsUserData::~SupportsUserData() {
-  DCHECK(thread_checker_.CalledOnValidThread() || user_data_.empty());
+  DCHECK(sequence_checker_.CalledOnValidSequence() || user_data_.empty());
   DataMap local_user_data;
   user_data_.swap(local_user_data);
   // Now this->user_data_ is empty, and any destructors called transitively from
diff --git a/base/supports_user_data.h b/base/supports_user_data.h
index a9f1c93..a4c7c9f 100644
--- a/base/supports_user_data.h
+++ b/base/supports_user_data.h
@@ -11,6 +11,10 @@
 #include "base/base_export.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+
+// TODO(gab): Removing this include causes IWYU failures in other headers,
+// remove it in a follow- up CL.
 #include "base/threading/thread_checker.h"
 
 namespace base {
@@ -26,7 +30,7 @@
   // class to any class with a virtual destructor.
   class BASE_EXPORT Data {
    public:
-    virtual ~Data() {}
+    virtual ~Data() = default;
   };
 
   // The user data allows the clients to associate data with this object.
@@ -38,10 +42,10 @@
   void RemoveUserData(const void* key);
 
   // SupportsUserData is not thread-safe, and on debug build will assert it is
-  // only used on one thread. Calling this method allows the caller to hand
-  // the SupportsUserData instance across threads. Use only if you are taking
-  // full control of the synchronization of that hand over.
-  void DetachUserDataThread();
+  // only used on one execution sequence. Calling this method allows the caller
+  // to hand the SupportsUserData instance across execution sequences. Use only
+  // if you are taking full control of the synchronization of that hand over.
+  void DetachFromSequence();
 
  protected:
   virtual ~SupportsUserData();
@@ -52,7 +56,7 @@
   // Externally-defined data accessible by key.
   DataMap user_data_;
   // Guards usage of |user_data_|
-  ThreadChecker thread_checker_;
+  SequenceChecker sequence_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(SupportsUserData);
 };
diff --git a/base/template_util.h b/base/template_util.h
index 1bfc1ac..0dc6c95 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -23,6 +23,23 @@
 #define CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
 #endif
 
+// Some chromeos bots are using experimental 5.0 for some reason
+// which has partial support for type_traits, but misses a smaller subset
+// while removing some of the older non-standard stuff.
+#define CR_GLIBCXX_5_0_0 20150123
+#if defined(__GLIBCXX__) && (__GLIBCXX__ == CR_GLIBCXX_5_0_0)
+#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
+#endif
+
+// This hacks around using gcc with libc++ which has some incompatibilies.
+// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538
+// TODO(danakj): Remove this when android builders are all using a newer version
+// of gcc, or the android ndk is updated to a newer libc++ that works with older
+// gcc versions.
+#if !defined(__clang__) && defined(_LIBCPP_VERSION)
+#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
+#endif
+
 namespace base {
 
 template <class T> struct is_non_const_reference : std::false_type {};
@@ -126,8 +143,53 @@
 using is_trivially_destructible = std::is_trivially_destructible<T>;
 #endif
 
+// is_trivially_copyable is especially hard to get right.
+// - Older versions of libstdc++ will fail to have it like they do for other
+//   type traits. In this case we should provide it based on compiler
+//   intrinsics. This is covered by the CR_USE_FALLBACKS_FOR_OLD_GLIBCXX define.
+// - An experimental release of gcc includes most of type_traits but misses
+//   is_trivially_copyable, so we still have to avoid using libstdc++ in this
+//   case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
+// - When compiling libc++ from before r239653, with a gcc compiler, the
+//   std::is_trivially_copyable can fail. So we need to work around that by not
+//   using the one in libc++ in this case. This is covered by the
+//   CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in
+//   https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that
+//   in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1.
+// - In both of the above cases we are using the gcc compiler. When defining
+//   this ourselves on compiler intrinsics, the __is_trivially_copyable()
+//   intrinsic is not available on gcc before version 5.1 (see the discussion in
+//   https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for
+//   that version.
+// - When __is_trivially_copyable() is not available because we are on gcc older
+//   than 5.1, we need to fall back to something, so we use __has_trivial_copy()
+//   instead based on what was done one-off in bit_cast() previously.
+
+// TODO(crbug.com/554293): Remove this when all platforms have this in the std
+// namespace and it works with gcc as needed.
+#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX) ||              \
+    defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
+    defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
+template <typename T>
+struct is_trivially_copyable {
+// TODO(danakj): Remove this when android builders are all using a newer version
+// of gcc, or the android ndk is updated to a newer libc++ that does this for
+// us.
+#if _GNUC_VER >= 501
+  static constexpr bool value = __is_trivially_copyable(T);
+#else
+  static constexpr bool value = __has_trivial_copy(T);
+#endif
+};
+#else
+template <class T>
+using is_trivially_copyable = std::is_trivially_copyable<T>;
+#endif
+
 }  // namespace base
 
 #undef CR_USE_FALLBACKS_FOR_OLD_GLIBCXX
+#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
+#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
 
 #endif  // BASE_TEMPLATE_UTIL_H_
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py
index 5d87625..82ac496ed 100755
--- a/build/android/gyp/apkbuilder.py
+++ b/build/android/gyp/apkbuilder.py
@@ -163,15 +163,12 @@
   """Add native libraries to APK."""
   for path in native_libs:
     basename = os.path.basename(path)
+    apk_path = 'lib/%s/%s' % (android_abi, basename)
 
     compress = None
-    if (uncompress and os.path.splitext(basename)[1] == '.so'
-        and 'android_linker' not in basename):
+    if (uncompress and os.path.splitext(basename)[1] == '.so'):
       compress = False
-      # Add prefix to prevent android install from extracting upon install.
-      basename = 'crazy.' + basename
 
-    apk_path = 'lib/%s/%s' % (android_abi, basename)
     build_utils.AddToZipHermetic(out_apk,
                                  apk_path,
                                  src_path=path,
@@ -268,9 +265,8 @@
                               options.uncompress_shared_libraries)
 
         for name in sorted(options.native_lib_placeholders):
-          # Note: Empty libs files are ignored by md5check (can cause issues
-          # with stale builds when the only change is adding/removing
-          # placeholders).
+          # Empty libs files are ignored by md5check, but rezip requires them
+          # to be empty in order to identify them as placeholders.
           apk_path = 'lib/%s/%s' % (options.android_abi, name)
           build_utils.AddToZipHermetic(out_apk, apk_path, data='')
 
diff --git a/build/android/gyp/finalize_apk.py b/build/android/gyp/finalize_apk.py
index ecb5ebfe..532d001f 100755
--- a/build/android/gyp/finalize_apk.py
+++ b/build/android/gyp/finalize_apk.py
@@ -21,6 +21,32 @@
 
 from util import build_utils
 
+def RenameInflateAndAddPageAlignment(
+    rezip_apk_jar_path, in_zip_file, out_zip_file):
+  rezip_apk_cmd = [
+      'java',
+      '-classpath',
+      rezip_apk_jar_path,
+      'RezipApk',
+      'renamealign',
+      in_zip_file,
+      out_zip_file,
+    ]
+  build_utils.CheckOutput(rezip_apk_cmd)
+
+
+def ReorderAndAlignApk(rezip_apk_jar_path, in_zip_file, out_zip_file):
+  rezip_apk_cmd = [
+      'java',
+      '-classpath',
+      rezip_apk_jar_path,
+      'RezipApk',
+      'reorder',
+      in_zip_file,
+      out_zip_file,
+    ]
+  build_utils.CheckOutput(rezip_apk_cmd)
+
 
 def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path):
   shutil.copy(unsigned_path, signed_path)
@@ -36,15 +62,14 @@
   build_utils.CheckOutput(sign_cmd)
 
 
-def AlignApk(zipalign_path, unaligned_path, final_path):
-  # Note -p will page align native libraries (files ending with .so), but
-  # only those that are stored uncompressed.
+def AlignApk(zipalign_path, package_align, unaligned_path, final_path):
   align_cmd = [
       zipalign_path,
-      '-p',
-      '-f',
+      '-f'
       ]
 
+  if package_align:
+    align_cmd += ['-p']
 
   align_cmd += [
       '4',  # 4 bytes
@@ -60,13 +85,23 @@
   parser = optparse.OptionParser()
   build_utils.AddDepfileOption(parser)
 
+  parser.add_option('--rezip-apk-jar-path',
+                    help='Path to the RezipApk jar file.')
   parser.add_option('--zipalign-path', help='Path to the zipalign tool.')
+  parser.add_option('--page-align-shared-libraries',
+                    action='store_true',
+                    help='Page align shared libraries.')
   parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.')
   parser.add_option('--final-apk-path',
       help='Path to output signed and aligned APK.')
   parser.add_option('--key-path', help='Path to keystore for signing.')
   parser.add_option('--key-passwd', help='Keystore password')
   parser.add_option('--key-name', help='Keystore name')
+  parser.add_option('--stamp', help='Path to touch on success.')
+  parser.add_option('--load-library-from-zip', type='int',
+      help='If non-zero, build the APK such that the library can be loaded ' +
+           'directly from the zip file using the crazy linker. The library ' +
+           'will be renamed, uncompressed and page aligned.')
 
   options, _ = parser.parse_args()
 
@@ -75,9 +110,14 @@
     options.key_path,
   ]
 
+  if options.load_library_from_zip:
+    input_paths.append(options.rezip_apk_jar_path)
+
   input_strings = [
+    options.load_library_from_zip,
     options.key_name,
     options.key_passwd,
+    options.page_align_shared_libraries,
   ]
 
   build_utils.CallAndWriteDepfileIfStale(
@@ -89,34 +129,57 @@
       output_paths=[options.final_apk_path])
 
 
-def _NormalizeZip(path):
-  with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk:
-    with zipfile.ZipFile(path, 'r') as zi:
-      with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo:
-        for info in zi.infolist():
-          # Ignore 'extended local file headers'. Python doesn't write them
-          # properly (see https://bugs.python.org/issue1742205) which causes
-          # zipalign to miscalculate alignment. Since we don't use them except
-          # for alignment anyway, we write a stripped file here and let
-          # zipalign add them properly later. eLFHs are controlled by 'general
-          # purpose bit flag 03' (0x08) so we mask that out.
-          info.flag_bits = info.flag_bits & 0xF7
-
-          info.date_time = build_utils.HERMETIC_TIMESTAMP
-          zo.writestr(info, zi.read(info.filename))
-
-    shutil.copy(hermetic_signed_apk.name, path)
-
-
 def FinalizeApk(options):
-  with tempfile.NamedTemporaryFile() as signed_apk_path_tmp:
+  with tempfile.NamedTemporaryFile() as signed_apk_path_tmp, \
+      tempfile.NamedTemporaryFile() as apk_to_sign_tmp:
+
+    if options.load_library_from_zip:
+      # We alter the name of the library so that the Android Package Manager
+      # does not extract it into a separate file. This must be done before
+      # signing, as the filename is part of the signed manifest. At the same
+      # time we uncompress the library, which is necessary so that it can be
+      # loaded directly from the APK.
+      # Move the library to a page boundary by adding a page alignment file.
+      apk_to_sign = apk_to_sign_tmp.name
+      RenameInflateAndAddPageAlignment(
+          options.rezip_apk_jar_path, options.unsigned_apk_path, apk_to_sign)
+    else:
+      apk_to_sign = options.unsigned_apk_path
+
     signed_apk_path = signed_apk_path_tmp.name
     JarSigner(options.key_path, options.key_name, options.key_passwd,
-              options.unsigned_apk_path, signed_apk_path)
-    # Make the newly added signing files hermetic.
-    _NormalizeZip(signed_apk_path)
+              apk_to_sign, signed_apk_path)
 
-    AlignApk(options.zipalign_path, signed_apk_path, options.final_apk_path)
+    # Make the signing files hermetic.
+    with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk:
+      with zipfile.ZipFile(signed_apk_path, 'r') as zi:
+        with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo:
+          for info in zi.infolist():
+            # Ignore 'extended local file headers'. Python doesn't write them
+            # properly (see https://bugs.python.org/issue1742205) which causes
+            # zipalign to miscalculate alignment. Since we don't use them except
+            # for alignment anyway, we write a stripped file here and let
+            # zipalign add them properly later. eLFHs are controlled by 'general
+            # purpose bit flag 03' (0x08) so we mask that out.
+            info.flag_bits = info.flag_bits & 0xF7
+
+            info.date_time = build_utils.HERMETIC_TIMESTAMP
+            zo.writestr(info, zi.read(info.filename))
+
+      shutil.copy(hermetic_signed_apk.name, signed_apk_path)
+
+    if options.load_library_from_zip:
+      # Reorder the contents of the APK. This re-establishes the canonical
+      # order which means the library will be back at its page aligned location.
+      # This step also aligns uncompressed items to 4 bytes.
+      ReorderAndAlignApk(
+          options.rezip_apk_jar_path, signed_apk_path, options.final_apk_path)
+    else:
+      # Align uncompressed items to 4 bytes
+      AlignApk(options.zipalign_path,
+               options.page_align_shared_libraries,
+               signed_apk_path,
+               options.final_apk_path)
 
 
 if __name__ == '__main__':
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 08ae4f3..0205b7c0 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -373,7 +373,7 @@
   @classmethod
   def _GetTimeoutScaleFromAnnotations(cls, annotations):
     try:
-      return int(annotations.get('TimeoutScale', 1))
+      return int(annotations.get('TimeoutScale', {}).get('value', 1))
     except ValueError as e:
       logging.warning("Non-integer value of TimeoutScale ignored. (%s)", str(e))
       return 1
diff --git a/build/android/pylib/local/device/local_device_perf_test_run.py b/build/android/pylib/local/device/local_device_perf_test_run.py
index 7bd9d9b3..b34c360 100644
--- a/build/android/pylib/local/device/local_device_perf_test_run.py
+++ b/build/android/pylib/local/device/local_device_perf_test_run.py
@@ -81,6 +81,7 @@
 
   def _TestSetUp(self, test):
     if (self._test_instance.collect_chartjson_data
+        or self._test_instance.collect_json_data
         or self._tests[test].get('archive_output_dir')):
       self._output_dir = tempfile.mkdtemp()
 
@@ -105,7 +106,11 @@
         exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
             cmd, timeout, cwd=cwd, shell=True)
       end_time = time.time()
-      json_output = self._test_instance.ReadChartjsonOutput(self._output_dir)
+      chart_json_output = self._test_instance.ReadChartjsonOutput(
+          self._output_dir)
+      json_output = ''
+      if self._test_instance.collect_json_data:
+        json_output = self._test_instance.ReadJsonOutput(self._output_dir)
       if exit_code == 0:
         result_type = base_test_result.ResultType.PASS
       else:
@@ -114,10 +119,12 @@
       end_time = time.time()
       exit_code = -1
       output = e.output
+      chart_json_output = ''
       json_output = ''
       result_type = base_test_result.ResultType.TIMEOUT
     return self._ProcessTestResult(test, cmd, start_time, end_time, exit_code,
-                                   output, json_output, result_type)
+                                   output, chart_json_output, json_output,
+                                   result_type)
 
   def _CreateCmd(self, test):
     cmd = []
@@ -126,6 +133,8 @@
     cmd.append(self._tests[test]['cmd'])
     if self._output_dir:
       cmd.append('--output-dir=%s' % self._output_dir)
+    if self._test_instance.collect_json_data:
+      cmd.append('--output-format=json')
     return ' '.join(self._ExtendCmd(cmd))
 
   def _ExtendCmd(self, cmd): # pylint: disable=no-self-use
@@ -142,7 +151,7 @@
     raise NotImplementedError
 
   def _ProcessTestResult(self, test, cmd, start_time, end_time, exit_code,
-                         output, json_output, result_type):
+                         output, chart_json_output, json_output, result_type):
     if exit_code is None:
       exit_code = -1
 
@@ -158,7 +167,8 @@
     persisted_result = {
         'name': test,
         'output': [output],
-        'chartjson': json_output,
+        'chartjson': chart_json_output,
+        'json': json_output,
         'archive_bytes': archive_bytes,
         'exit_code': exit_code,
         'actual_exit_code': actual_exit_code,
diff --git a/build/android/pylib/perf/perf_test_instance.py b/build/android/pylib/perf/perf_test_instance.py
index 3121d8d..3ccc8d3 100644
--- a/build/android/pylib/perf/perf_test_instance.py
+++ b/build/android/pylib/perf/perf_test_instance.py
@@ -61,6 +61,7 @@
     super(PerfTestInstance, self).__init__()
 
     self._collect_chartjson_data = args.collect_chartjson_data
+    self._collect_json_data = args.collect_json_data
     self._dry_run = args.dry_run
     self._flaky_steps = args.flaky_steps
     self._output_dir_archive_path = args.output_dir_archive_path
@@ -73,6 +74,7 @@
     self._min_battery_level = args.min_battery_level
     self._no_timeout = args.no_timeout
     self._output_chartjson_data = args.output_chartjson_data
+    self._output_json_data = args.output_json_data
     self._output_json_list = args.output_json_list
     self._print_step = args.print_step
     self._single_step = (
@@ -107,7 +109,7 @@
           data['has_archive'] = persisted_result['archive_bytes'] is not None
         step_values.append(data)
 
-      with file(self._output_json_list, 'w') as o:
+      with file(self.output_json_list, 'w') as o:
         o.write(json.dumps(step_values))
       return base_test_result.ResultType.PASS
     except KeyError:
@@ -137,13 +139,17 @@
           i, persisted_outputs[i])
     print output_formatted
 
-    if self._output_chartjson_data:
-      with file(self._output_chartjson_data, 'w') as f:
+    if self.output_json_data:
+      with file(self.output_json_data, 'w') as f:
+        f.write(persisted_result['json'])
+
+    if self.output_chartjson_data:
+      with file(self.output_chartjson_data, 'w') as f:
         f.write(persisted_result['chartjson'])
 
-    if self._output_dir_archive_path:
+    if self.output_dir_archive_path:
       if persisted_result['archive_bytes'] is not None:
-        with file(self._output_dir_archive_path, 'wb') as f:
+        with file(self.output_dir_archive_path, 'wb') as f:
           f.write(persisted_result['archive_bytes'])
       else:
         logging.error('The output dir was not archived.')
@@ -170,6 +176,21 @@
                     ' the test.')
       return ''
 
+  @staticmethod
+  def ReadJsonOutput(output_dir):
+    if not output_dir:
+      return ''
+    json_output_path = os.path.join(output_dir, 'results.json')
+    try:
+      with open(json_output_path) as f:
+        return f.read()
+    except IOError:
+      logging.exception('Exception when reading results.json.')
+      logging.error('This usually means that telemetry did not run, so it could'
+                    ' not generate the file. Please check the device running'
+                    ' the test.')
+      return ''
+
   def WriteBuildBotJson(self, output_dir):
     """Write metadata about the buildbot environment to the output dir."""
     if not output_dir or not self._write_buildbot_json:
@@ -186,6 +207,10 @@
     return self._collect_chartjson_data
 
   @property
+  def collect_json_data(self):
+    return self._collect_json_data
+
+  @property
   def dry_run(self):
     return self._dry_run
 
@@ -218,6 +243,10 @@
     return self._output_dir_archive_path
 
   @property
+  def output_json_data(self):
+    return self._output_json_data
+
+  @property
   def output_json_list(self):
     return self._output_json_list
 
diff --git a/build/android/rezip/BUILD.gn b/build/android/rezip/BUILD.gn
new file mode 100644
index 0000000..1ace37c
--- /dev/null
+++ b/build/android/rezip/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+java_library("rezip") {
+  jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
+  java_files = [ "RezipApk.java" ]
+}
diff --git a/build/android/rezip/RezipApk.java b/build/android/rezip/RezipApk.java
new file mode 100644
index 0000000..43d75447
--- /dev/null
+++ b/build/android/rezip/RezipApk.java
@@ -0,0 +1,448 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+
+/**
+ * Command line tool used to build APKs which support loading the native code library
+ * directly from the APK file. To construct the APK we rename the native library by
+ * adding the prefix "crazy." to the filename. This is done to prevent the Android
+ * Package Manager from extracting the library. The native code must be page aligned
+ * and uncompressed. The page alignment is implemented by adding a zero filled file
+ * in front of the the native code library. This tool is designed so that running
+ * SignApk and/or zipalign on the resulting APK does not break the page alignment.
+ * This is achieved by outputing the filenames in the same canonical order used
+ * by SignApk and adding the same alignment fields added by zipalign.
+ */
+class RezipApk {
+    // Alignment to use for non-compressed files (must match zipalign).
+    private static final int ALIGNMENT = 4;
+
+    // Alignment to use for non-compressed *.so files
+    private static final int LIBRARY_ALIGNMENT = 4096;
+
+    // Files matching this pattern are not copied to the output when adding alignment.
+    // When reordering and verifying the APK they are copied to the end of the file.
+    private static Pattern sMetaFilePattern =
+            Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert))|("
+                    + Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
+
+    // Pattern for matching a shared library in the APK
+    private static Pattern sLibraryPattern = Pattern.compile("^lib/[^/]*/lib.*[.]so$");
+    // Pattern for match the crazy linker in the APK
+    private static Pattern sCrazyLinkerPattern =
+            Pattern.compile("^lib/[^/]*/libchromium_android_linker.so$");
+    // Pattern for matching a crazy loaded shared library in the APK
+    private static Pattern sCrazyLibraryPattern = Pattern.compile("^lib/[^/]*/crazy.lib.*[.]so$");
+
+    private static boolean isLibraryFilename(String filename) {
+        return sLibraryPattern.matcher(filename).matches()
+                && !sCrazyLinkerPattern.matcher(filename).matches();
+    }
+
+    private static boolean isCrazyLibraryFilename(String filename) {
+        return sCrazyLibraryPattern.matcher(filename).matches();
+    }
+
+    private static String renameLibraryForCrazyLinker(String filename) {
+        int lastSlash = filename.lastIndexOf('/');
+        // We rename the library, so that the Android Package Manager
+        // no longer extracts the library.
+        return filename.substring(0, lastSlash + 1) + "crazy." + filename.substring(lastSlash + 1);
+    }
+
+    /**
+     * Wraps another output stream, counting the number of bytes written.
+     */
+    private static class CountingOutputStream extends OutputStream {
+        private long mCount = 0;
+        private OutputStream mOut;
+
+        public CountingOutputStream(OutputStream out) {
+            this.mOut = out;
+        }
+
+        /** Returns the number of bytes written. */
+        public long getCount() {
+            return mCount;
+        }
+
+        @Override public void write(byte[] b, int off, int len) throws IOException {
+            mOut.write(b, off, len);
+            mCount += len;
+        }
+
+        @Override public void write(int b) throws IOException {
+            mOut.write(b);
+            mCount++;
+        }
+
+        @Override public void close() throws IOException {
+            mOut.close();
+        }
+
+        @Override public void flush() throws IOException {
+            mOut.flush();
+        }
+    }
+
+    private static String outputName(JarEntry entry, boolean rename) {
+        String inName = entry.getName();
+        if (rename && entry.getSize() > 0 && isLibraryFilename(inName)) {
+            return renameLibraryForCrazyLinker(inName);
+        }
+        return inName;
+    }
+
+    /**
+     * Comparator used to sort jar entries from the input file.
+     * Sorting is done based on the output filename (which maybe renamed).
+     * Filenames are in natural string order, except that filenames matching
+     * the meta-file pattern are always after other files. This is so the manifest
+     * and signature are at the end of the file after any alignment file.
+     */
+    private static class EntryComparator implements Comparator<JarEntry> {
+        private boolean mRename;
+
+        public EntryComparator(boolean rename) {
+            mRename = rename;
+        }
+
+        @Override
+        public int compare(JarEntry j1, JarEntry j2) {
+            String o1 = outputName(j1, mRename);
+            String o2 = outputName(j2, mRename);
+            boolean o1Matches = sMetaFilePattern.matcher(o1).matches();
+            boolean o2Matches = sMetaFilePattern.matcher(o2).matches();
+            if (o1Matches != o2Matches) {
+                return o1Matches ? 1 : -1;
+            } else {
+                return o1.compareTo(o2);
+            }
+        }
+    }
+
+    // Build an ordered list of jar entries. The jar entries from the input are
+    // sorted based on the output filenames (which maybe renamed). If |omitMetaFiles|
+    // is true do not include the jar entries for the META-INF files.
+    // Entries are ordered in the deterministic order used by SignApk.
+    private static List<JarEntry> getOutputFileOrderEntries(
+            JarFile jar, boolean omitMetaFiles, boolean rename) {
+        List<JarEntry> entries = new ArrayList<JarEntry>();
+        for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
+            JarEntry entry = e.nextElement();
+            if (entry.isDirectory()) {
+                continue;
+            }
+            if (omitMetaFiles && sMetaFilePattern.matcher(entry.getName()).matches()) {
+                continue;
+            }
+            entries.add(entry);
+        }
+
+        // We sort the input entries by name. When present META-INF files
+        // are sorted to the end.
+        Collections.sort(entries, new EntryComparator(rename));
+        return entries;
+    }
+
+    /**
+     * Add a zero filled alignment file at this point in the zip file,
+     * The added file will be added before |name| and after |prevName|.
+     * The size of the alignment file is such that the location of the
+     * file |name| will be on a LIBRARY_ALIGNMENT boundary.
+     *
+     * Note this arrangement is devised so that running SignApk and/or zipalign on the resulting
+     * file will not alter the alignment.
+     *
+     * @param offset number of bytes into the output file at this point.
+     * @param timestamp time in millis since the epoch to include in the header.
+     * @param name the name of the library filename.
+     * @param prevName the name of the previous file in the archive (or null).
+     * @param out jar output stream to write the alignment file to.
+     *
+     * @throws IOException if the output file can not be written.
+     */
+    private static void addAlignmentFile(
+            long offset, long timestamp, String name, String prevName,
+            JarOutputStream out) throws IOException {
+
+        // Compute the start and alignment of the library, as if it was next.
+        int headerSize = JarFile.LOCHDR + name.length();
+        long libOffset = offset + headerSize;
+        int libNeeded = LIBRARY_ALIGNMENT - (int) (libOffset % LIBRARY_ALIGNMENT);
+        if (libNeeded == LIBRARY_ALIGNMENT) {
+            // Already aligned, no need to added alignment file.
+            return;
+        }
+
+        // Check that there is not another file between the library and the
+        // alignment file.
+        String alignName = name.substring(0, name.length() - 2) + "align";
+        if (prevName != null && prevName.compareTo(alignName) >= 0) {
+            throw new UnsupportedOperationException(
+                "Unable to insert alignment file, because there is "
+                + "another file in front of the file to be aligned. "
+                + "Other file: " + prevName + " Alignment file: " + alignName
+                + " file: " + name);
+        }
+
+        // Compute the size of the alignment file header.
+        headerSize = JarFile.LOCHDR + alignName.length();
+        // We are going to add an alignment file of type STORED. This file
+        // will itself induce a zipalign alignment adjustment.
+        int extraNeeded =
+                (ALIGNMENT - (int) ((offset + headerSize) % ALIGNMENT)) % ALIGNMENT;
+        headerSize += extraNeeded;
+
+        if (libNeeded < headerSize + 1) {
+            // The header was bigger than the alignment that we need, add another page.
+            libNeeded += LIBRARY_ALIGNMENT;
+        }
+        // Compute the size of the alignment file.
+        libNeeded -= headerSize;
+
+        // Build the header for the alignment file.
+        byte[] zeroBuffer = new byte[libNeeded];
+        JarEntry alignEntry = new JarEntry(alignName);
+        alignEntry.setMethod(JarEntry.STORED);
+        alignEntry.setSize(libNeeded);
+        alignEntry.setTime(timestamp);
+        CRC32 crc = new CRC32();
+        crc.update(zeroBuffer);
+        alignEntry.setCrc(crc.getValue());
+
+        if (extraNeeded != 0) {
+            alignEntry.setExtra(new byte[extraNeeded]);
+        }
+
+        // Output the alignment file.
+        out.putNextEntry(alignEntry);
+        out.write(zeroBuffer);
+        out.closeEntry();
+        out.flush();
+    }
+
+    // Make a JarEntry for the output file which corresponds to the input
+    // file. The output file will be called |name|. The output file will always
+    // be uncompressed (STORED). If the input is not STORED it is necessary to inflate
+    // it to compute the CRC and size of the output entry.
+    private static JarEntry makeStoredEntry(String name, JarEntry inEntry, JarFile in)
+            throws IOException {
+        JarEntry outEntry = new JarEntry(name);
+        outEntry.setMethod(JarEntry.STORED);
+
+        if (inEntry.getMethod() == JarEntry.STORED) {
+            outEntry.setCrc(inEntry.getCrc());
+            outEntry.setSize(inEntry.getSize());
+        } else {
+            // We are inflating the file. We need to compute the CRC and size.
+            byte[] buffer = new byte[4096];
+            CRC32 crc = new CRC32();
+            int size = 0;
+            int num;
+            InputStream data = in.getInputStream(inEntry);
+            while ((num = data.read(buffer)) > 0) {
+                crc.update(buffer, 0, num);
+                size += num;
+            }
+            data.close();
+            outEntry.setCrc(crc.getValue());
+            outEntry.setSize(size);
+        }
+        return outEntry;
+    }
+
+    /**
+     * Copy the contents of the input APK file to the output APK file. If |rename| is
+     * true then non-empty libraries (*.so) in the input will be renamed by prefixing
+     * "crazy.". This is done to prevent the Android Package Manager extracting the
+     * library. Note the crazy linker itself is not renamed, for bootstrapping reasons.
+     * Empty libraries are not renamed (they are in the APK to workaround a bug where
+     * the Android Package Manager fails to delete old versions when upgrading).
+     * There must be exactly one "crazy" library in the output stream. The "crazy"
+     * library will be uncompressed and page aligned in the output stream. Page
+     * alignment is implemented by adding a zero filled file, regular alignment is
+     * implemented by adding a zero filled extra field to the zip file header. If
+     * |addAlignment| is true a page alignment file is added, otherwise the "crazy"
+     * library must already be page aligned. Care is taken so that the output is generated
+     * in the same way as SignApk. This is important so that running SignApk and
+     * zipalign on the output does not break the page alignment. The archive may not
+     * contain a "*.apk" as SignApk has special nested signing logic that we do not
+     * support.
+     *
+     * @param in The input APK File.
+     * @param out The output APK stream.
+     * @param countOut Counting output stream (to measure the current offset).
+     * @param addAlignment Whether to add the alignment file or just check.
+     * @param rename Whether to rename libraries to be "crazy".
+     *
+     * @throws IOException if the output file can not be written.
+     */
+    private static void rezip(
+            JarFile in, JarOutputStream out, CountingOutputStream countOut,
+            boolean addAlignment, boolean rename) throws IOException {
+
+        List<JarEntry> entries = getOutputFileOrderEntries(in, addAlignment, rename);
+        long timestamp = System.currentTimeMillis();
+        byte[] buffer = new byte[4096];
+        boolean firstEntry = true;
+        String prevName = null;
+        int numCrazy = 0;
+        for (JarEntry inEntry : entries) {
+            // Rename files, if specied.
+            String name = outputName(inEntry, rename);
+            if (name.endsWith(".apk")) {
+                throw new UnsupportedOperationException(
+                        "Nested APKs are not supported: " + name);
+            }
+
+            // Build the header.
+            JarEntry outEntry = null;
+            boolean isCrazy = isCrazyLibraryFilename(name);
+            if (isCrazy) {
+                // "crazy" libraries are alway output uncompressed (STORED).
+                outEntry = makeStoredEntry(name, inEntry, in);
+                numCrazy++;
+                if (numCrazy > 1) {
+                    throw new UnsupportedOperationException(
+                            "Found more than one library\n"
+                            + "Multiple libraries are not supported for APKs that use "
+                            + "'load_library_from_zip'.\n"
+                            + "See crbug/388223.\n"
+                            + "Note, check that your build is clean.\n"
+                            + "An unclean build can incorrectly incorporate old "
+                            + "libraries in the APK.");
+                }
+            } else if (inEntry.getMethod() == JarEntry.STORED) {
+                // Preserve the STORED method of the input entry.
+                outEntry = new JarEntry(inEntry);
+                outEntry.setExtra(null);
+            } else {
+                // Create a new entry so that the compressed len is recomputed.
+                outEntry = new JarEntry(name);
+            }
+            outEntry.setTime(timestamp);
+
+            // Compute and add alignment
+            long offset = countOut.getCount();
+            if (firstEntry) {
+                // The first entry in a jar file has an extra field of
+                // four bytes that you can't get rid of; any extra
+                // data you specify in the JarEntry is appended to
+                // these forced four bytes.  This is JAR_MAGIC in
+                // JarOutputStream; the bytes are 0xfeca0000.
+                firstEntry = false;
+                offset += 4;
+            }
+            if (outEntry.getMethod() == JarEntry.STORED) {
+                if (isCrazy) {
+                    if (addAlignment) {
+                        addAlignmentFile(offset, timestamp, name, prevName, out);
+                    }
+                    // We check that we did indeed get to a page boundary.
+                    offset = countOut.getCount() + JarFile.LOCHDR + name.length();
+                    if ((offset % LIBRARY_ALIGNMENT) != 0) {
+                        throw new AssertionError(
+                                "Library was not page aligned when verifying page alignment. "
+                                + "Library name: " + name + " Expected alignment: "
+                                + LIBRARY_ALIGNMENT + "Offset: " + offset + " Error: "
+                                + (offset % LIBRARY_ALIGNMENT));
+                    }
+                } else {
+                    // This is equivalent to zipalign.
+                    offset += JarFile.LOCHDR + name.length();
+                    int needed = (ALIGNMENT - (int) (offset % ALIGNMENT)) % ALIGNMENT;
+                    if (needed != 0) {
+                        outEntry.setExtra(new byte[needed]);
+                    }
+                }
+            }
+            out.putNextEntry(outEntry);
+
+            // Copy the data from the input to the output
+            int num;
+            InputStream data = in.getInputStream(inEntry);
+            while ((num = data.read(buffer)) > 0) {
+                out.write(buffer, 0, num);
+            }
+            data.close();
+            out.closeEntry();
+            out.flush();
+            prevName = name;
+        }
+        if (numCrazy == 0) {
+            throw new AssertionError("There was no crazy library in the archive");
+        }
+    }
+
+    private static void usage() {
+        System.err.println("Usage: prealignapk (addalignment|reorder) input.apk output.apk");
+        System.err.println("\"crazy\" libraries are always inflated in the output");
+        System.err.println(
+                "  renamealign  - rename libraries with \"crazy.\" prefix and add alignment file");
+        System.err.println("  align        - add alignment file");
+        System.err.println("  reorder      - re-creates canonical ordering and checks alignment");
+        System.exit(2);
+    }
+
+    public static void main(String[] args) throws IOException {
+        if (args.length != 3) usage();
+
+        boolean addAlignment = false;
+        boolean rename = false;
+        if (args[0].equals("renamealign")) {
+            // Normal case. Before signing we rename the library and add an alignment file.
+            addAlignment = true;
+            rename = true;
+        } else if (args[0].equals("align")) {
+            // LGPL compliance case. Before signing, we add an alignment file to a
+            // reconstructed APK which already contains the "crazy" library.
+            addAlignment = true;
+            rename = false;
+        } else if (args[0].equals("reorder")) {
+            // Normal case. After jarsigning we write the file in the canonical order and check.
+            addAlignment = false;
+        } else {
+            usage();
+        }
+
+        String inputFilename = args[1];
+        String outputFilename = args[2];
+
+        JarFile inputJar = null;
+        FileOutputStream outputFile = null;
+
+        try {
+            inputJar = new JarFile(new File(inputFilename), true);
+            outputFile = new FileOutputStream(outputFilename);
+
+            CountingOutputStream outCount = new CountingOutputStream(outputFile);
+            JarOutputStream outputJar = new JarOutputStream(outCount);
+
+            // Match the compression level used by SignApk.
+            outputJar.setLevel(9);
+
+            rezip(inputJar, outputJar, outCount, addAlignment, rename);
+            outputJar.close();
+        } finally {
+            if (inputJar != null) inputJar.close();
+            if (outputFile != null) outputFile.close();
+        }
+    }
+}
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index af1b07d..39e87bc 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -502,15 +502,25 @@
 
   group.add_argument(
       '--output-json-list', type=os.path.realpath,
-      help='Write a simple list of names from --steps into the given file.')
+      help='Writes a JSON list of information for each --steps into the given '
+           'file. Information includes runtime and device affinity for each '
+           '--steps.')
   group.add_argument(
       '--collect-chartjson-data',
       action='store_true',
-      help='Cache the chartjson output from each step for later use.')
+      help='Cache the telemetry chartjson output from each step for later use.')
   group.add_argument(
       '--output-chartjson-data',
       type=os.path.realpath,
-      help='Write out chartjson into the given file.')
+      help='Writes telemetry chartjson formatted output into the given file.')
+  group.add_argument(
+      '--collect-json-data',
+      action='store_true',
+      help='Cache the telemetry JSON output from each step for later use.')
+  group.add_argument(
+      '--output-json-data',
+      type=os.path.realpath,
+      help='Writes telemetry JSON formatted output into the given file.')
   # TODO(rnephew): Remove this when everything moves to new option in platform
   # mode.
   group.add_argument(
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 6883600..f6052ae 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -32,6 +32,7 @@
 
   # TODO(agrieve): Rename targets below to match above patterns.
   "*android_webview/glue:glue",
+  "//build/android/rezip:rezip",
   "//chrome/test/android/cast_emulator:cast_emulator",
 ]
 
@@ -1384,6 +1385,7 @@
   #   keystore_path: Path to keystore to use for signing.
   #   keystore_name: Key alias to use.
   #   keystore_password: Keystore password.
+  #   rezip_apk: Whether to add crazy-linker alignment.
   template("finalize_apk") {
     action(target_name) {
       deps = []
@@ -1426,6 +1428,20 @@
         "--key-passwd",
         invoker.keystore_password,
       ]
+      if (defined(invoker.rezip_apk) && invoker.rezip_apk) {
+        deps += [ "//build/android/rezip" ]
+        _rezip_jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
+        args += [
+          "--load-library-from-zip=1",
+          "--rezip-apk-jar-path",
+          rebase_path(_rezip_jar_path, root_build_dir),
+        ]
+      }
+
+      if (defined(invoker.page_align_shared_libraries) &&
+          invoker.page_align_shared_libraries) {
+        args += [ "--page-align-shared-libraries" ]
+      }
     }
   }
 
@@ -1668,9 +1684,6 @@
                                "uncompress_shared_libraries",
                                "write_asset_list",
                              ])
-      if (!defined(uncompress_shared_libraries)) {
-        uncompress_shared_libraries = _load_library_from_apk
-      }
       deps = _deps + [ ":${_package_resources_target_name}" ]
       native_libs = _native_libs + _native_libs_even_when_incremental
 
@@ -1691,9 +1704,6 @@
                                "secondary_native_libs",
                                "uncompress_shared_libraries",
                              ])
-      if (!defined(uncompress_shared_libraries)) {
-        uncompress_shared_libraries = _load_library_from_apk
-      }
       _dex_target = "//build/android/incremental_install:bootstrap_java__dex"
       deps = _incremental_deps + [
                ":${_incremental_package_resources_target_name}",
@@ -1720,11 +1730,14 @@
 
     _finalize_apk_rule_name = "${target_name}__finalize"
     finalize_apk(_finalize_apk_rule_name) {
+      forward_variables_from(invoker, [ "page_align_shared_libraries" ])
+
       input_apk_path = _packaged_apk_path
       output_apk_path = _final_apk_path
       keystore_path = _keystore_path
       keystore_name = _keystore_name
       keystore_password = _keystore_password
+      rezip_apk = _load_library_from_apk
 
       public_deps = [
         # Generator of the _packaged_apk_path this target takes as input.
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 6d286fe..2a45db4 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2007,7 +2007,15 @@
     _extra_native_libs_deps = []
     assert(_extra_native_libs_deps == [])  # Mark as used.
     _extra_native_libs_even_when_incremental = []
+    _extra_native_libs_even_when_incremental_deps = []
+    assert(_extra_native_libs_even_when_incremental_deps == [])  # Mark as used.
     if (_native_libs_deps != []) {
+      # zipalign can't align gdb_server, don't pack gdbserver temporarily.
+      if (is_debug && (!defined(invoker.page_align_shared_libraries) ||
+                       !invoker.page_align_shared_libraries)) {
+        _extra_native_libs_even_when_incremental = [ android_gdbserver ]
+      }
+
       if (_use_chromium_linker) {
         _extra_native_libs =
             [ "$root_shlib_dir/libchromium_android_linker$shlib_extension" ]
@@ -2029,6 +2037,7 @@
                                "deps",
                                "extensions_to_not_compress",
                                "language_splits",
+                               "page_align_shared_libraries",
                                "public_deps",
                                "secondary_native_libs",
                                "shared_resources",
@@ -2078,6 +2087,7 @@
            _extra_native_libs_even_when_incremental != []) &&
           !_create_abi_split) {
         deps += _native_libs_deps + _extra_native_libs_deps +
+                _extra_native_libs_even_when_incremental_deps +
                 [ _native_libs_file_arg_dep ]
         native_libs_filearg = _native_libs_file_arg
         native_libs = _extra_native_libs
@@ -2138,7 +2148,9 @@
                                  "public_deps",
                                ])
 
-        incremental_deps = deps + [ ":$_manifest_rule" ]
+        incremental_deps =
+            deps + _extra_native_libs_even_when_incremental_deps +
+            [ ":$_manifest_rule" ]
         deps = []
         deps = incremental_deps + _native_libs_deps + _extra_native_libs_deps +
                [ _native_libs_file_arg_dep ]
diff --git a/cc/blimp/layer_tree_host_remote.cc b/cc/blimp/layer_tree_host_remote.cc
index 466a4209..442be97 100644
--- a/cc/blimp/layer_tree_host_remote.cc
+++ b/cc/blimp/layer_tree_host_remote.cc
@@ -11,7 +11,6 @@
 #include "cc/blimp/engine_picture_cache.h"
 #include "cc/blimp/picture_data_conversions.h"
 #include "cc/blimp/remote_compositor_bridge.h"
-#include "cc/output/begin_frame_args.h"
 #include "cc/output/compositor_frame_sink.h"
 #include "cc/proto/compositor_message.pb.h"
 #include "cc/proto/gfx_conversions.h"
@@ -371,8 +370,10 @@
   current_pipeline_stage_ = FramePipelineStage::ANIMATE;
   base::TimeTicks now = base::TimeTicks::Now();
   client_->BeginMainFrame(BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, now, now + kDefaultFrameInterval,
+      BEGINFRAME_FROM_HERE, begin_frame_source_.source_id(),
+      begin_frame_number_, now, now + kDefaultFrameInterval,
       kDefaultFrameInterval, BeginFrameArgs::NORMAL));
+  begin_frame_number_++;
   // We don't run any animations on the layer because threaded animations are
   // disabled.
   // TODO(khushalsagar): Revisit this when adding support for animations.
diff --git a/cc/blimp/layer_tree_host_remote.h b/cc/blimp/layer_tree_host_remote.h
index e7c3c8b7..b1c1748 100644
--- a/cc/blimp/layer_tree_host_remote.h
+++ b/cc/blimp/layer_tree_host_remote.h
@@ -10,6 +10,8 @@
 #include "cc/base/cc_export.h"
 #include "cc/blimp/remote_compositor_bridge_client.h"
 #include "cc/debug/layer_tree_debug_state.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/scheduler/begin_frame_source.h"
 #include "cc/surfaces/surface_sequence_generator.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
@@ -126,6 +128,10 @@
   bool visible_ = false;
   bool defer_commits_ = false;
 
+  // Provides |source_id| for BeginFrameArgs we create.
+  StubBeginFrameSource begin_frame_source_;
+  uint64_t begin_frame_number_ = BeginFrameArgs::kStartingFrameNumber;
+
   // In threaded/single-threaded mode, the LayerTree and Layers expect scroll/
   // scale updates to come from the impl thread only during the main frame.
   // Since we synchronize state outside of that, this is set so we can
diff --git a/cc/ipc/begin_frame_args.mojom b/cc/ipc/begin_frame_args.mojom
index 8b16c62a..ad53658 100644
--- a/cc/ipc/begin_frame_args.mojom
+++ b/cc/ipc/begin_frame_args.mojom
@@ -18,6 +18,8 @@
   mojo.common.mojom.TimeTicks frame_time;
   mojo.common.mojom.TimeTicks deadline;
   mojo.common.mojom.TimeDelta interval;
+  uint64 sequence_number;
+  uint32 source_id;
   BeginFrameArgsType type;
   bool on_critical_path;
 };
diff --git a/cc/ipc/begin_frame_args_struct_traits.h b/cc/ipc/begin_frame_args_struct_traits.h
index bf3c5a8..4d81339 100644
--- a/cc/ipc/begin_frame_args_struct_traits.h
+++ b/cc/ipc/begin_frame_args_struct_traits.h
@@ -24,6 +24,14 @@
     return args.interval;
   }
 
+  static uint64_t sequence_number(const cc::BeginFrameArgs& args) {
+    return args.sequence_number;
+  }
+
+  static uint32_t source_id(const cc::BeginFrameArgs& args) {
+    return args.source_id;
+  }
+
   static cc::mojom::BeginFrameArgsType type(const cc::BeginFrameArgs& args) {
     return static_cast<cc::mojom::BeginFrameArgsType>(args.type);
   }
@@ -39,6 +47,8 @@
         !data.ReadInterval(&out->interval)) {
       return false;
     }
+    out->source_id = data.source_id();
+    out->sequence_number = data.sequence_number();
     out->type =
         static_cast<cc::BeginFrameArgs::BeginFrameArgsType>(data.type());
     out->on_critical_path = data.on_critical_path();
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index b90e250..1410de7 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -159,6 +159,8 @@
   IPC_STRUCT_TRAITS_MEMBER(frame_time)
   IPC_STRUCT_TRAITS_MEMBER(deadline)
   IPC_STRUCT_TRAITS_MEMBER(interval)
+  IPC_STRUCT_TRAITS_MEMBER(sequence_number)
+  IPC_STRUCT_TRAITS_MEMBER(source_id)
   IPC_STRUCT_TRAITS_MEMBER(type)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/cc/output/begin_frame_args.cc b/cc/output/begin_frame_args.cc
index 7ef2c484..f7a35ff 100644
--- a/cc/output/begin_frame_args.cc
+++ b/cc/output/begin_frame_args.cc
@@ -24,26 +24,37 @@
   return "???";
 }
 
+constexpr uint64_t BeginFrameArgs::kInvalidFrameNumber;
+constexpr uint64_t BeginFrameArgs::kStartingFrameNumber;
+
 BeginFrameArgs::BeginFrameArgs()
     : frame_time(base::TimeTicks()),
       deadline(base::TimeTicks()),
       interval(base::TimeDelta::FromMicroseconds(-1)),
+      sequence_number(kInvalidFrameNumber),
+      source_id(0),
       type(BeginFrameArgs::INVALID),
-      on_critical_path(true) {
-}
+      on_critical_path(true) {}
 
-BeginFrameArgs::BeginFrameArgs(base::TimeTicks frame_time,
+BeginFrameArgs::BeginFrameArgs(uint32_t source_id,
+                               uint64_t sequence_number,
+                               base::TimeTicks frame_time,
                                base::TimeTicks deadline,
                                base::TimeDelta interval,
                                BeginFrameArgs::BeginFrameArgsType type)
     : frame_time(frame_time),
       deadline(deadline),
       interval(interval),
+      sequence_number(sequence_number),
+      source_id(source_id),
       type(type),
       on_critical_path(true) {
+  DCHECK_LT(kInvalidFrameNumber, sequence_number);
 }
 
 BeginFrameArgs BeginFrameArgs::Create(BeginFrameArgs::CreationLocation location,
+                                      uint32_t source_id,
+                                      uint64_t sequence_number,
                                       base::TimeTicks frame_time,
                                       base::TimeTicks deadline,
                                       base::TimeDelta interval,
@@ -51,9 +62,11 @@
   DCHECK_NE(type, BeginFrameArgs::INVALID);
   DCHECK_NE(type, BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX);
 #ifdef NDEBUG
-  return BeginFrameArgs(frame_time, deadline, interval, type);
+  return BeginFrameArgs(source_id, sequence_number, frame_time, deadline,
+                        interval, type);
 #else
-  BeginFrameArgs args = BeginFrameArgs(frame_time, deadline, interval, type);
+  BeginFrameArgs args = BeginFrameArgs(source_id, sequence_number, frame_time,
+                                       deadline, interval, type);
   args.created_from = location;
   return args;
 #endif
@@ -70,6 +83,8 @@
 void BeginFrameArgs::AsValueInto(base::trace_event::TracedValue* state) const {
   state->SetString("type", "BeginFrameArgs");
   state->SetString("subtype", TypeToString(type));
+  state->SetInteger("source_id", source_id);
+  state->SetInteger("sequence_number", sequence_number);
   state->SetDouble("frame_time_us", frame_time.ToInternalValue());
   state->SetDouble("deadline_us", deadline.ToInternalValue());
   state->SetDouble("interval_us", interval.InMicroseconds());
diff --git a/cc/output/begin_frame_args.h b/cc/output/begin_frame_args.h
index 78ddce99..a75f446e8 100644
--- a/cc/output/begin_frame_args.h
+++ b/cc/output/begin_frame_args.h
@@ -5,6 +5,7 @@
 #ifndef CC_OUTPUT_BEGIN_FRAME_ARGS_H_
 #define CC_OUTPUT_BEGIN_FRAME_ARGS_H_
 
+#include <stdint.h>
 #include <memory>
 
 #include "base/location.h"
@@ -49,6 +50,9 @@
   };
   static const char* TypeToString(BeginFrameArgsType type);
 
+  static constexpr uint64_t kInvalidFrameNumber = 0;
+  static constexpr uint64_t kStartingFrameNumber = 1;
+
   // Creates an invalid set of values.
   BeginFrameArgs();
 
@@ -63,6 +67,8 @@
   // created by searching for "BeginFrameArgs::Create".
   // The location argument should **always** be BEGINFRAME_FROM_HERE macro.
   static BeginFrameArgs Create(CreationLocation location,
+                               uint32_t source_id,
+                               uint64_t sequence_number,
                                base::TimeTicks frame_time,
                                base::TimeTicks deadline,
                                base::TimeDelta interval,
@@ -84,11 +90,21 @@
   base::TimeTicks frame_time;
   base::TimeTicks deadline;
   base::TimeDelta interval;
+
+  // |source_id| and |sequence_number| identify a BeginFrame within a single
+  // process and are set by the original BeginFrameSource that created the
+  // BeginFrameArgs. When |source_id| of consecutive BeginFrameArgs changes,
+  // observers should expect the continuity of |sequence_number| to break.
+  uint64_t sequence_number;
+  uint32_t source_id;  // |source_id| after |sequence_number| for packing.
+
   BeginFrameArgsType type;
   bool on_critical_path;
 
  private:
-  BeginFrameArgs(base::TimeTicks frame_time,
+  BeginFrameArgs(uint32_t source_id,
+                 uint64_t sequence_number,
+                 base::TimeTicks frame_time,
                  base::TimeTicks deadline,
                  base::TimeDelta interval,
                  BeginFrameArgsType type);
diff --git a/cc/output/begin_frame_args_unittest.cc b/cc/output/begin_frame_args_unittest.cc
index eb63cc7..65e0e9f6 100644
--- a/cc/output/begin_frame_args_unittest.cc
+++ b/cc/output/begin_frame_args_unittest.cc
@@ -14,56 +14,79 @@
 
 TEST(BeginFrameArgsTest, Helpers) {
   // Quick create methods work
-  BeginFrameArgs args0 = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+  BeginFrameArgs args0 =
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
   EXPECT_TRUE(args0.IsValid()) << args0;
 
   BeginFrameArgs args1 =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 0, -1);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 0, 0, -1);
   EXPECT_FALSE(args1.IsValid()) << args1;
 
   BeginFrameArgs args2 =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 1, 2, 3);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 10, 1, 2, 3);
   EXPECT_TRUE(args2.IsValid()) << args2;
+  EXPECT_EQ(123u, args2.source_id);
+  EXPECT_EQ(10u, args2.sequence_number);
   EXPECT_EQ(1, args2.frame_time.ToInternalValue());
   EXPECT_EQ(2, args2.deadline.ToInternalValue());
   EXPECT_EQ(3, args2.interval.ToInternalValue());
   EXPECT_EQ(BeginFrameArgs::NORMAL, args2.type);
 
   BeginFrameArgs args4 = CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, 1, 2, 3, BeginFrameArgs::MISSED);
+      BEGINFRAME_FROM_HERE, 234, 20, 1, 2, 3, BeginFrameArgs::MISSED);
   EXPECT_TRUE(args4.IsValid()) << args4;
+  EXPECT_EQ(234u, args4.source_id);
+  EXPECT_EQ(20u, args4.sequence_number);
   EXPECT_EQ(1, args4.frame_time.ToInternalValue());
   EXPECT_EQ(2, args4.deadline.ToInternalValue());
   EXPECT_EQ(3, args4.interval.ToInternalValue());
   EXPECT_EQ(BeginFrameArgs::MISSED, args4.type);
 
   // operator==
-  EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6),
-            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6));
+  EXPECT_EQ(
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6),
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6));
 
   EXPECT_NONFATAL_FAILURE(
-      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9,
-                                               BeginFrameArgs::MISSED),
-                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9)),
+      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7,
+                                               8, 9, BeginFrameArgs::MISSED),
+                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7,
+                                               8, 9)),
       "");
 
   EXPECT_NONFATAL_FAILURE(
-      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6),
-                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9)),
+      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 4,
+                                               5, 6),
+                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7,
+                                               8, 9)),
+      "");
+
+  EXPECT_NONFATAL_FAILURE(
+      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7,
+                                               8, 9),
+                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 40, 7,
+                                               8, 9)),
+      "");
+
+  EXPECT_NONFATAL_FAILURE(
+      EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7,
+                                               8, 9),
+                CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 234, 30, 7,
+                                               8, 9)),
       "");
 
   // operator<<
   std::stringstream out1;
   out1 << args1;
-  EXPECT_EQ("BeginFrameArgs(NORMAL, 0, 0, -1us)", out1.str());
+  EXPECT_EQ("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)", out1.str());
   std::stringstream out2;
   out2 << args2;
-  EXPECT_EQ("BeginFrameArgs(NORMAL, 1, 2, 3us)", out2.str());
+  EXPECT_EQ("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)", out2.str());
 
   // PrintTo
-  EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 0, 0, -1us)"),
+  EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)"),
             ::testing::PrintToString(args1));
-  EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 1, 2, 3us)"),
+  EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)"),
             ::testing::PrintToString(args2));
 }
 
@@ -74,10 +97,12 @@
   EXPECT_TRUE(args1.on_critical_path);
 
   BeginFrameArgs args2 = BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, base::TimeTicks::FromInternalValue(1),
+      BEGINFRAME_FROM_HERE, 123, 10, base::TimeTicks::FromInternalValue(1),
       base::TimeTicks::FromInternalValue(2),
       base::TimeDelta::FromInternalValue(3), BeginFrameArgs::NORMAL);
   EXPECT_TRUE(args2.IsValid()) << args2;
+  EXPECT_EQ(123u, args2.source_id) << args2;
+  EXPECT_EQ(10u, args2.sequence_number) << args2;
   EXPECT_EQ(1, args2.frame_time.ToInternalValue()) << args2;
   EXPECT_EQ(2, args2.deadline.ToInternalValue()) << args2;
   EXPECT_EQ(3, args2.interval.ToInternalValue()) << args2;
@@ -88,7 +113,7 @@
 TEST(BeginFrameArgsTest, Location) {
   tracked_objects::Location expected_location = BEGINFRAME_FROM_HERE;
 
-  BeginFrameArgs args = CreateBeginFrameArgsForTesting(expected_location);
+  BeginFrameArgs args = CreateBeginFrameArgsForTesting(expected_location, 0, 1);
   EXPECT_EQ(expected_location.ToString(), args.created_from.ToString());
 }
 #endif
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc
index 4c859a7..a7ba684d 100644
--- a/cc/scheduler/begin_frame_source.cc
+++ b/cc/scheduler/begin_frame_source.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/atomic_sequence_num.h"
 #include "base/auto_reset.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -25,7 +26,7 @@
 static const double kDoubleTickDivisor = 2.0;
 }
 
-// BeginFrameObserverBase -----------------------------------------------
+// BeginFrameObserverBase -------------------------------------------------
 BeginFrameObserverBase::BeginFrameObserverBase()
     : last_begin_frame_args_(), dropped_begin_frame_args_(0) {
 }
@@ -33,9 +34,12 @@
 const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const {
   return last_begin_frame_args_;
 }
+
 void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) {
   DCHECK(args.IsValid());
   DCHECK(args.frame_time >= last_begin_frame_args_.frame_time);
+  DCHECK(args.sequence_number > last_begin_frame_args_.sequence_number ||
+         args.source_id != last_begin_frame_args_.source_id);
   bool used = OnBeginFrameDerivedImpl(args);
   if (used) {
     last_begin_frame_args_ = args;
@@ -44,17 +48,31 @@
   }
 }
 
+// BeginFrameSource -------------------------------------------------------
+namespace {
+static base::StaticAtomicSequenceNumber g_next_source_id;
+}  // namespace
+
+BeginFrameSource::BeginFrameSource() : source_id_(g_next_source_id.GetNext()) {}
+
+uint32_t BeginFrameSource::source_id() const {
+  return source_id_;
+}
+
+// StubBeginFrameSource ---------------------------------------------------
 bool StubBeginFrameSource::IsThrottled() const {
   return true;
 }
 
-// SyntheticBeginFrameSource ---------------------------------------------
+// SyntheticBeginFrameSource ----------------------------------------------
 SyntheticBeginFrameSource::~SyntheticBeginFrameSource() = default;
 
-// BackToBackBeginFrameSource --------------------------------------------
+// BackToBackBeginFrameSource ---------------------------------------------
 BackToBackBeginFrameSource::BackToBackBeginFrameSource(
     std::unique_ptr<DelayBasedTimeSource> time_source)
-    : time_source_(std::move(time_source)), weak_factory_(this) {
+    : time_source_(std::move(time_source)),
+      next_sequence_number_(BeginFrameArgs::kStartingFrameNumber),
+      weak_factory_(this) {
   time_source_->SetClient(this);
   // The time_source_ ticks immediately, so we SetActive(true) for a single
   // tick when we need it, and keep it as SetActive(false) otherwise.
@@ -97,8 +115,9 @@
   base::TimeTicks frame_time = time_source_->LastTickTime();
   base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval();
   BeginFrameArgs args = BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, frame_time, frame_time + default_interval,
-      default_interval, BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time,
+      frame_time + default_interval, default_interval, BeginFrameArgs::NORMAL);
+  next_sequence_number_++;
 
   // This must happen after getting the LastTickTime() from the time source.
   time_source_->SetActive(false);
@@ -113,7 +132,8 @@
 // DelayBasedBeginFrameSource ---------------------------------------------
 DelayBasedBeginFrameSource::DelayBasedBeginFrameSource(
     std::unique_ptr<DelayBasedTimeSource> time_source)
-    : time_source_(std::move(time_source)) {
+    : time_source_(std::move(time_source)),
+      next_sequence_number_(BeginFrameArgs::kStartingFrameNumber) {
   time_source_->SetClient(this);
 }
 
@@ -142,9 +162,10 @@
 BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs(
     base::TimeTicks frame_time,
     BeginFrameArgs::BeginFrameArgsType type) {
-  return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time,
-                                time_source_->NextTickTime(),
-                                time_source_->Interval(), type);
+  uint64_t sequence_number = next_sequence_number_++;
+  return BeginFrameArgs::Create(
+      BEGINFRAME_FROM_HERE, source_id(), sequence_number, frame_time,
+      time_source_->NextTickTime(), time_source_->Interval(), type);
 }
 
 void DelayBasedBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
@@ -154,14 +175,33 @@
   observers_.insert(obs);
   obs->OnBeginFrameSourcePausedChanged(false);
   time_source_->SetActive(true);
-  BeginFrameArgs args = CreateBeginFrameArgs(
-      time_source_->NextTickTime() - time_source_->Interval(),
-      BeginFrameArgs::MISSED);
+
+  // Missed args should correspond to |current_begin_frame_args_| (particularly,
+  // have the same sequence number) if |current_begin_frame_args_| still
+  // correspond to the last time the time source should have ticked. This may
+  // not be the case if OnTimerTick() has never run yet, the time source was
+  // inactive before AddObserver() was called, or the interval changed. In such
+  // a case, we create new args with a new sequence number.
+  base::TimeTicks last_or_missed_tick_time =
+      time_source_->NextTickTime() - time_source_->Interval();
+  if (current_begin_frame_args_.IsValid() &&
+      current_begin_frame_args_.frame_time == last_or_missed_tick_time &&
+      current_begin_frame_args_.interval == time_source_->Interval()) {
+    // Ensure that the args have the right type.
+    current_begin_frame_args_.type = BeginFrameArgs::MISSED;
+  } else {
+    // The args are not up to date and we need to create new ones with the
+    // missed tick's time and a new sequence number.
+    current_begin_frame_args_ =
+        CreateBeginFrameArgs(last_or_missed_tick_time, BeginFrameArgs::MISSED);
+  }
+
   BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
   if (!last_args.IsValid() ||
-      (args.frame_time >
-       last_args.frame_time + args.interval / kDoubleTickDivisor)) {
-    obs->OnBeginFrame(args);
+      (current_begin_frame_args_.frame_time >
+       last_args.frame_time +
+           current_begin_frame_args_.interval / kDoubleTickDivisor)) {
+    obs->OnBeginFrame(current_begin_frame_args_);
   }
 }
 
@@ -179,18 +219,21 @@
 }
 
 void DelayBasedBeginFrameSource::OnTimerTick() {
-  BeginFrameArgs args = CreateBeginFrameArgs(time_source_->LastTickTime(),
-                                             BeginFrameArgs::NORMAL);
+  current_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime(),
+                                                   BeginFrameArgs::NORMAL);
   std::unordered_set<BeginFrameObserver*> observers(observers_);
   for (auto* obs : observers) {
     BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
     if (!last_args.IsValid() ||
-        (args.frame_time >
-         last_args.frame_time + args.interval / kDoubleTickDivisor))
-      obs->OnBeginFrame(args);
+        (current_begin_frame_args_.frame_time >
+         last_args.frame_time +
+             current_begin_frame_args_.interval / kDoubleTickDivisor)) {
+      obs->OnBeginFrame(current_begin_frame_args_);
+    }
   }
 }
 
+// ExternalBeginFrameSource -----------------------------------------------
 ExternalBeginFrameSource::ExternalBeginFrameSource(
     ExternalBeginFrameSourceClient* client)
     : client_(client) {
@@ -214,6 +257,9 @@
     BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
     if (!last_args.IsValid() ||
         (missed_begin_frame_args_.frame_time > last_args.frame_time)) {
+      DCHECK((missed_begin_frame_args_.source_id != last_args.source_id) ||
+             (missed_begin_frame_args_.sequence_number >
+              last_args.sequence_number));
       obs->OnBeginFrame(missed_begin_frame_args_);
     }
   }
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h
index bde01afd..c25fff6 100644
--- a/cc/scheduler/begin_frame_source.h
+++ b/cc/scheduler/begin_frame_source.h
@@ -26,8 +26,10 @@
   virtual ~BeginFrameObserver() {}
 
   // The |args| given to OnBeginFrame is guaranteed to have
-  // |args|.IsValid()==true and have |args|.frame_time
-  // field be strictly greater than the previous call.
+  // |args|.IsValid()==true. If |args|.source_id did not change between
+  // invocations, |args|.sequence_number is guaranteed to be be strictly greater
+  // than the previous call. Further, |args|.frame_time is guaranteed to be
+  // greater than or equal to the previous call even if the source_id changes.
   //
   // Side effects: This function can (and most of the time *will*) change the
   // return value of the LastUsedBeginFrameArgs method. See the documentation
@@ -99,6 +101,7 @@
 // all BeginFrameSources *must* provide.
 class CC_EXPORT BeginFrameSource {
  public:
+  BeginFrameSource();
   virtual ~BeginFrameSource() {}
 
   // DidFinishFrame provides back pressure to a frame source about frame
@@ -116,6 +119,15 @@
   // Returns false if the begin frame source will just continue to produce
   // begin frames without waiting.
   virtual bool IsThrottled() const = 0;
+
+  // Returns an identifier for this BeginFrameSource. Guaranteed unique within a
+  // process, but not across processes. This is used to create BeginFrames that
+  // originate at this source. Note that BeginFrameSources may pass on
+  // BeginFrames created by other sources, with different IDs.
+  uint32_t source_id() const;
+
+ private:
+  uint32_t source_id_;
 };
 
 // A BeginFrameSource that does nothing.
@@ -167,6 +179,7 @@
   std::unique_ptr<DelayBasedTimeSource> time_source_;
   std::unordered_set<BeginFrameObserver*> observers_;
   std::unordered_set<BeginFrameObserver*> pending_begin_frame_observers_;
+  uint64_t next_sequence_number_;
   base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(BackToBackBeginFrameSource);
@@ -204,6 +217,8 @@
   std::unordered_set<BeginFrameObserver*> observers_;
   base::TimeTicks last_timebase_;
   base::TimeDelta authoritative_interval_;
+  BeginFrameArgs current_begin_frame_args_;
+  uint64_t next_sequence_number_;
 
   DISALLOW_COPY_AND_ASSIGN(DelayBasedBeginFrameSource);
 };
diff --git a/cc/scheduler/begin_frame_source_unittest.cc b/cc/scheduler/begin_frame_source_unittest.cc
index 450e154..8250c6c 100644
--- a/cc/scheduler/begin_frame_source_unittest.cc
+++ b/cc/scheduler/begin_frame_source_unittest.cc
@@ -19,6 +19,17 @@
 namespace cc {
 namespace {
 
+// BeginFrameSource testing ----------------------------------------------------
+TEST(BeginFrameSourceTest, SourceIdsAreUnique) {
+  StubBeginFrameSource source1;
+  StubBeginFrameSource source2;
+  StubBeginFrameSource source3;
+  EXPECT_NE(source1.source_id(), source2.source_id());
+  EXPECT_NE(source1.source_id(), source3.source_id());
+  EXPECT_NE(source2.source_id(), source3.source_id());
+}
+
+// BackToBackBeginFrameSource testing ------------------------------------------
 class BackToBackBeginFrameSourceTest : public ::testing::Test {
  protected:
   static const int64_t kDeadline;
@@ -55,20 +66,23 @@
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
   EXPECT_TRUE(task_runner_->HasPendingTasks());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
+                          1100 + kDeadline, kInterval);
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(obs_.get(), 0);
   task_runner_->RunPendingTasks();
 }
 
 TEST_F(BackToBackBeginFrameSourceTest,
-       DidFinishFrameThenRemoveObserverProducesNoFrame) {
+       RemoveObserverThenDidFinishFrameProducesNoFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   source_->RemoveObserver(obs_.get());
@@ -82,10 +96,11 @@
 }
 
 TEST_F(BackToBackBeginFrameSourceTest,
-       RemoveObserverThenDidFinishFrameProducesNoFrame) {
+       DidFinishFrameThenRemoveObserverProducesNoFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
@@ -100,7 +115,8 @@
        TogglingObserverThenDidFinishFrameProducesCorrectFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
@@ -116,7 +132,8 @@
   now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
   // The begin frame is posted at the time when the observer was added,
   // so it ignores changes to "now" afterward.
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1110, 1110 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110,
+                          1110 + kDeadline, kInterval);
   EXPECT_TRUE(task_runner_->HasPendingTasks());
   task_runner_->RunPendingTasks();
 }
@@ -125,7 +142,8 @@
        DidFinishFrameThenTogglingObserverProducesCorrectFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
@@ -141,7 +159,8 @@
   now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
   // Ticks at the time at which the observer was added, ignoring the
   // last change to "now".
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1120, 1120 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120,
+                          1120 + kDeadline, kInterval);
   EXPECT_TRUE(task_runner_->HasPendingTasks());
   task_runner_->RunPendingTasks();
 }
@@ -157,7 +176,8 @@
 TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameRemainingFrames) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   // Runs the pending begin frame.
   task_runner_->RunPendingTasks();
   // While running the begin frame, the next frame was cancelled, this
@@ -174,7 +194,8 @@
   source_->DidFinishFrame(obs_.get(), 1);
   EXPECT_FALSE(task_runner_->HasPendingTasks());
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
+                          1100 + kDeadline, kInterval);
   source_->DidFinishFrame(obs_.get(), 0);
   EXPECT_EQ(base::TimeDelta(), task_runner_->DelayToNextTaskTime());
   task_runner_->RunPendingTasks();
@@ -183,28 +204,32 @@
 TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(obs_.get(), 0);
   source_->DidFinishFrame(obs_.get(), 0);
   source_->DidFinishFrame(obs_.get(), 0);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
+                          1100 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(obs_.get(), 0);
   source_->DidFinishFrame(obs_.get(), 0);
   source_->DidFinishFrame(obs_.get(), 0);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1200, 1200 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200,
+                          1200 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 }
 
 TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
+                          1000 + kDeadline, kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
@@ -212,7 +237,8 @@
   now_src_->Advance(base::TimeDelta::FromMicroseconds(50));
   // Ticks at the time the last frame finished, so ignores the last change to
   // "now".
-  EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
+                          1100 + kDeadline, kInterval);
 
   EXPECT_TRUE(task_runner_->HasPendingTasks());
   task_runner_->RunPendingTasks();
@@ -226,15 +252,19 @@
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
   source_->AddObserver(&obs2);
 
-  EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
+                          kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(&obs1, 0);
   source_->DidFinishFrame(&obs2, 0);
-  EXPECT_BEGIN_FRAME_USED(obs1, 1100, 1100 + kDeadline, kInterval);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
+                          kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
@@ -251,18 +281,21 @@
 
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
   source_->AddObserver(&obs1);
-  EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
   source_->AddObserver(&obs2);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(&obs1, 0);
-  EXPECT_BEGIN_FRAME_USED(obs1, 1200, 1200 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   source_->DidFinishFrame(&obs1, 0);
@@ -275,7 +308,8 @@
 
   now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
   source_->DidFinishFrame(&obs2, 0);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1300, 1300 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   source_->DidFinishFrame(&obs2, 0);
@@ -289,8 +323,10 @@
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
   source_->AddObserver(&obs1);
   source_->AddObserver(&obs2);
-  EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1000, 1000 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
+                          kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   // |obs1| finishes first.
@@ -303,8 +339,10 @@
 
   // Because the begin frame source already ticked when |obs1| finished,
   // we see it as the frame time for both observers.
-  EXPECT_BEGIN_FRAME_USED(obs1, 1100, 1100 + kDeadline, kInterval);
-  EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
+                          kInterval);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
+                          kInterval);
   task_runner_->RunPendingTasks();
 
   source_->DidFinishFrame(&obs1, 0);
@@ -341,18 +379,20 @@
        AddObserverCallsOnBeginFrameWithMissedTick) {
   now_src_->Advance(base::TimeDelta::FromMicroseconds(9010));
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000,
+                                 10000);
   source_->AddObserver(obs_.get());  // Should cause the last tick to be sent
   // No tasks should need to be run for this to occur.
 }
 
 TEST_F(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) {
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
+                                 10000);
   source_->AddObserver(obs_.get());
   EXPECT_EQ(10000, task_runner_->NextTaskTime().ToInternalValue());
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
   now_src_->Advance(base::TimeDelta::FromMicroseconds(9010));
   task_runner_->RunPendingTasks();
 }
@@ -361,11 +401,12 @@
   task_runner_->SetAutoAdvanceNowToPendingTasks(true);
 
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
+                                 10000);
   source_->AddObserver(obs_.get());
-  EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 20000, 30000, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 30000, 40000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(30001));
 
   source_->RemoveObserver(obs_.get());
@@ -376,21 +417,22 @@
 TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) {
   task_runner_->SetAutoAdvanceNowToPendingTasks(true);
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
+                                 10000);
   source_->AddObserver(obs_.get());
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 20000, 30000, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 30000, 40000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(30001));
 
   // Update the vsync information
   source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(27500),
                                    base::TimeDelta::FromMicroseconds(10001));
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 40000, 47502, 10001);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 47502, 57503, 10001);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 57503, 67504, 10001);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(60000));
 }
 
@@ -399,25 +441,26 @@
   source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(500),
                                    base::TimeDelta::FromMicroseconds(10000));
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 500, 10500, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 500, 10500,
+                                 10000);
   source_->AddObserver(obs_.get());
 
-  EXPECT_BEGIN_FRAME_USED(*obs_, 10500, 20500, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 20500, 30500, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10500, 20500, 10000);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20500, 30500, 10000);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(20501));
 
   // This will keep the same timebase, so 500, 9999
   source_->SetAuthoritativeVSyncInterval(
       base::TimeDelta::FromMicroseconds(9999));
-  EXPECT_BEGIN_FRAME_USED(*obs_, 30500, 40496, 9999);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 40496, 50495, 9999);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30500, 40496, 9999);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40496, 50495, 9999);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(40497));
 
   // Change the vsync params, but the new interval will be ignored.
   source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(400),
                                    base::TimeDelta::FromMicroseconds(1));
-  EXPECT_BEGIN_FRAME_USED(*obs_, 50495, 60394, 9999);
-  EXPECT_BEGIN_FRAME_USED(*obs_, 60394, 70393, 9999);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 50495, 60394, 9999);
+  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 60394, 70393, 9999);
   task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(60395));
 }
 
@@ -427,25 +470,28 @@
   // now_src_ starts off at 1000.
   task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(9010));
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(obs1, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000,
+                                 10000);
   source_->AddObserver(&obs1);  // Should cause the last tick to be sent
   // No tasks should need to be run for this to occur.
 
-  EXPECT_BEGIN_FRAME_USED(obs1, 20000, 30000, 10000);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000);
   task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
 
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(obs2, 20000, 30000, 10000);
+  // Sequence number unchanged for missed frame with time of last normal frame.
+  EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 20000, 30000,
+                                 10000);
   source_->AddObserver(&obs2);  // Should cause the last tick to be sent
   // No tasks should need to be run for this to occur.
 
-  EXPECT_BEGIN_FRAME_USED(obs1, 30000, 40000, 10000);
-  EXPECT_BEGIN_FRAME_USED(obs2, 30000, 40000, 10000);
+  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000);
   task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
 
   source_->RemoveObserver(&obs1);
 
-  EXPECT_BEGIN_FRAME_USED(obs2, 40000, 50000, 10000);
+  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000);
   task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
 
   source_->RemoveObserver(&obs2);
@@ -457,7 +503,7 @@
   StrictMock<MockBeginFrameObserver> obs;
 
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(obs, 0, 10000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000);
   source_->AddObserver(&obs);
 
   source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(5000),
@@ -471,7 +517,7 @@
   source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(10000),
                                    base::TimeDelta::FromInternalValue(10000));
   now_src_->Advance(base::TimeDelta::FromInternalValue(5000));
-  EXPECT_BEGIN_FRAME_USED(obs, 10000, 20000, 10000);
+  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
   task_runner_->RunPendingTasks();
 }
 
@@ -479,7 +525,7 @@
   StrictMock<MockBeginFrameObserver> obs;
 
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(obs, 0, 10000, 10000);
+  EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000);
   source_->AddObserver(&obs);
   source_->RemoveObserver(&obs);
 
@@ -489,6 +535,8 @@
 
   // No missed frame received.
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  // This does not cause a missed BeginFrame, but the sequence number is
+  // incremented, because the possible missed frame has different time/interval.
   source_->AddObserver(&obs);
   source_->RemoveObserver(&obs);
 
@@ -497,7 +545,10 @@
                                    base::TimeDelta::FromInternalValue(10000));
   now_src_->Advance(base::TimeDelta::FromInternalValue(5000));
   EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(obs, 10000, 20000, 10000);
+  // Sequence number is incremented again, because the missed frame has
+  // different time/interval.
+  EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 3, 10000, 20000,
+                                 10000);
   source_->AddObserver(&obs);
   source_->RemoveObserver(&obs);
 }
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index ace12c6..64f4acd3 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/run_loop.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "cc/output/begin_frame_args.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
 #include "cc/test/ordered_simple_task_runner.h"
@@ -236,7 +237,9 @@
   SchedulerTest()
       : now_src_(new base::SimpleTestTickClock()),
         task_runner_(new OrderedSimpleTaskRunner(now_src_.get(), true)),
-        fake_external_begin_frame_source_(nullptr) {
+        fake_external_begin_frame_source_(nullptr),
+        fake_compositor_timing_history_(nullptr),
+        next_begin_frame_number_(BeginFrameArgs::kStartingFrameNumber) {
     now_src_->Advance(base::TimeDelta::FromMicroseconds(10000));
     // A bunch of tests require NowTicks()
     // to be > BeginFrameArgs::DefaultInterval()
@@ -405,8 +408,10 @@
     // Creep the time forward so that any BeginFrameArgs is not equal to the
     // last one otherwise we violate the BeginFrameSource contract.
     now_src_->Advance(BeginFrameArgs::DefaultInterval());
-    BeginFrameArgs args =
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src());
+    BeginFrameArgs args = CreateBeginFrameArgsForTesting(
+        BEGINFRAME_FROM_HERE, fake_external_begin_frame_source_->source_id(),
+        next_begin_frame_number_, now_src());
+    next_begin_frame_number_++;
     fake_external_begin_frame_source_->TestOnBeginFrame(args);
     return args;
   }
@@ -435,6 +440,7 @@
   std::unique_ptr<FakeSchedulerClient> client_;
   std::unique_ptr<TestScheduler> scheduler_;
   FakeCompositorTimingHistory* fake_compositor_timing_history_;
+  uint64_t next_begin_frame_number_;
 };
 
 TEST_F(SchedulerTest, InitializeCompositorFrameSinkDoesNotBeginImplFrame) {
@@ -1332,8 +1338,10 @@
 
   // Advance frame and create a begin frame.
   now_src_->Advance(BeginFrameArgs::DefaultInterval());
-  BeginFrameArgs args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src());
+  BeginFrameArgs args = CreateBeginFrameArgsForTesting(
+      BEGINFRAME_FROM_HERE, fake_external_begin_frame_source_->source_id(),
+      next_begin_frame_number_, now_src());
+  next_begin_frame_number_++;
 
   // Deliver this begin frame super late.
   now_src_->Advance(BeginFrameArgs::DefaultInterval() * 100);
diff --git a/cc/surfaces/display_scheduler_unittest.cc b/cc/surfaces/display_scheduler_unittest.cc
index ec9c221..c7b5bd81 100644
--- a/cc/surfaces/display_scheduler_unittest.cc
+++ b/cc/surfaces/display_scheduler_unittest.cc
@@ -89,8 +89,9 @@
     base::TimeTicks frame_time = now_src_.NowTicks();
     base::TimeDelta interval = BeginFrameArgs::DefaultInterval();
     base::TimeTicks deadline = frame_time + interval;
+    // FakeBeginFrameSource deals with |source_id| and |sequence_number|.
     fake_begin_frame_source_.TestOnBeginFrame(
-        BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
+        BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, 0, 1, frame_time, deadline,
                                interval, BeginFrameArgs::NORMAL));
   }
 
diff --git a/cc/test/begin_frame_args_test.cc b/cc/test/begin_frame_args_test.cc
index 8b4dfd3b..7ce80b5 100644
--- a/cc/test/begin_frame_args_test.cc
+++ b/cc/test/begin_frame_args_test.cc
@@ -12,15 +12,20 @@
 namespace cc {
 
 BeginFrameArgs CreateBeginFrameArgsForTesting(
-    BeginFrameArgs::CreationLocation location) {
-  return CreateBeginFrameArgsForTesting(location, base::TimeTicks::Now());
+    BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number) {
+  return CreateBeginFrameArgsForTesting(location, source_id, sequence_number,
+                                        base::TimeTicks::Now());
 }
 
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     base::TimeTicks frame_time) {
   return BeginFrameArgs::Create(
-      location, frame_time,
+      location, source_id, sequence_number, frame_time,
       frame_time + BeginFrameArgs::DefaultInterval() -
           BeginFrameArgs::DefaultEstimatedParentDrawTime(),
       BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL);
@@ -28,40 +33,51 @@
 
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     int64_t frame_time,
     int64_t deadline,
     int64_t interval) {
-  return BeginFrameArgs::Create(
-      location, base::TimeTicks::FromInternalValue(frame_time),
-      base::TimeTicks::FromInternalValue(deadline),
-      base::TimeDelta::FromInternalValue(interval), BeginFrameArgs::NORMAL);
+  return BeginFrameArgs::Create(location, source_id, sequence_number,
+                                base::TimeTicks::FromInternalValue(frame_time),
+                                base::TimeTicks::FromInternalValue(deadline),
+                                base::TimeDelta::FromInternalValue(interval),
+                                BeginFrameArgs::NORMAL);
 }
 
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     int64_t frame_time,
     int64_t deadline,
     int64_t interval,
     BeginFrameArgs::BeginFrameArgsType type) {
-  return BeginFrameArgs::Create(
-      location, base::TimeTicks::FromInternalValue(frame_time),
-      base::TimeTicks::FromInternalValue(deadline),
-      base::TimeDelta::FromInternalValue(interval), type);
+  return BeginFrameArgs::Create(location, source_id, sequence_number,
+                                base::TimeTicks::FromInternalValue(frame_time),
+                                base::TimeTicks::FromInternalValue(deadline),
+                                base::TimeDelta::FromInternalValue(interval),
+                                type);
 }
 
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     base::SimpleTestTickClock* now_src) {
   base::TimeTicks now = now_src->NowTicks();
   return BeginFrameArgs::Create(
-      location, now, now + BeginFrameArgs::DefaultInterval() -
-                         BeginFrameArgs::DefaultEstimatedParentDrawTime(),
+      location, source_id, sequence_number, now,
+      now + BeginFrameArgs::DefaultInterval() -
+          BeginFrameArgs::DefaultEstimatedParentDrawTime(),
       BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL);
 }
 
 bool operator==(const BeginFrameArgs& lhs, const BeginFrameArgs& rhs) {
-  return (lhs.type == rhs.type) && (lhs.frame_time == rhs.frame_time) &&
-         (lhs.deadline == rhs.deadline) && (lhs.interval == rhs.interval);
+  return (lhs.type == rhs.type) && (lhs.source_id == rhs.source_id) &&
+         (lhs.sequence_number == rhs.sequence_number) &&
+         (lhs.frame_time == rhs.frame_time) && (lhs.deadline == rhs.deadline) &&
+         (lhs.interval == rhs.interval);
 }
 
 ::std::ostream& operator<<(::std::ostream& os, const BeginFrameArgs& args) {
@@ -71,6 +87,7 @@
 
 void PrintTo(const BeginFrameArgs& args, ::std::ostream* os) {
   *os << "BeginFrameArgs(" << BeginFrameArgs::TypeToString(args.type) << ", "
+      << args.source_id << ", " << args.sequence_number << ", "
       << args.frame_time.ToInternalValue() << ", "
       << args.deadline.ToInternalValue() << ", "
       << args.interval.InMicroseconds() << "us)";
diff --git a/cc/test/begin_frame_args_test.h b/cc/test/begin_frame_args_test.h
index d798094..57cee81f 100644
--- a/cc/test/begin_frame_args_test.h
+++ b/cc/test/begin_frame_args_test.h
@@ -18,17 +18,25 @@
 
 // Functions for quickly creating BeginFrameArgs
 BeginFrameArgs CreateBeginFrameArgsForTesting(
-    BeginFrameArgs::CreationLocation location);
+    BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number);
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     base::TimeTicks frame_time);
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     int64_t frame_time,
     int64_t deadline,
     int64_t interval);
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     int64_t frame_time,
     int64_t deadline,
     int64_t interval,
@@ -38,6 +46,8 @@
 // OrderSimpleTaskRunner.
 BeginFrameArgs CreateBeginFrameArgsForTesting(
     BeginFrameArgs::CreationLocation location,
+    uint32_t source_id,
+    uint64_t sequence_number,
     base::SimpleTestTickClock* now_src);
 
 // gtest helpers -- these *must* be in the same namespace as the types they
diff --git a/cc/test/begin_frame_source_test.cc b/cc/test/begin_frame_source_test.cc
index ef1c033..997aa995 100644
--- a/cc/test/begin_frame_source_test.cc
+++ b/cc/test/begin_frame_source_test.cc
@@ -35,6 +35,8 @@
         FROM_HERE_WITH_EXPLICIT_FUNCTION(
             "MockBeginFrameObserver::kDefaultBeginFrameArgs"),
 #endif
+        0,
+        BeginFrameArgs::kStartingFrameNumber,
         -1,
         -1,
         -1);
diff --git a/cc/test/begin_frame_source_test.h b/cc/test/begin_frame_source_test.h
index cde725b..8be41ae 100644
--- a/cc/test/begin_frame_source_test.h
+++ b/cc/test/begin_frame_source_test.h
@@ -12,24 +12,28 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 // Macros to help set up expected calls on the MockBeginFrameObserver.
-#define EXPECT_BEGIN_FRAME_DROP(obs, frame_time, deadline, interval)      \
-  EXPECT_CALL((obs),                                                      \
-              OnBeginFrame(CreateBeginFrameArgsForTesting(                \
-                  BEGINFRAME_FROM_HERE, frame_time, deadline, interval))) \
-      .Times(1)                                                           \
+#define EXPECT_BEGIN_FRAME_DROP(obs, source_id, sequence_number, frame_time, \
+                                deadline, interval)                          \
+  EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting(            \
+                         BEGINFRAME_FROM_HERE, source_id, sequence_number,   \
+                         frame_time, deadline, interval)))                   \
+      .Times(1)                                                              \
       .InSequence((obs).sequence)
 
-#define EXPECT_BEGIN_FRAME_USED(obs, frame_time, deadline, interval)      \
-  EXPECT_CALL((obs),                                                      \
-              OnBeginFrame(CreateBeginFrameArgsForTesting(                \
-                  BEGINFRAME_FROM_HERE, frame_time, deadline, interval))) \
-      .InSequence((obs).sequence)                                         \
+#define EXPECT_BEGIN_FRAME_USED(obs, source_id, sequence_number, frame_time, \
+                                deadline, interval)                          \
+  EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting(            \
+                         BEGINFRAME_FROM_HERE, source_id, sequence_number,   \
+                         frame_time, deadline, interval)))                   \
+      .InSequence((obs).sequence)                                            \
       .WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args)))
 
-#define EXPECT_BEGIN_FRAME_USED_MISSED(obs, frame_time, deadline, interval)    \
-  EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting(              \
-                         BEGINFRAME_FROM_HERE, frame_time, deadline, interval, \
-                         BeginFrameArgs::MISSED)))                             \
+#define EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_id, sequence_number,        \
+                                       frame_time, deadline, interval)         \
+  EXPECT_CALL(                                                                 \
+      (obs), OnBeginFrame(CreateBeginFrameArgsForTesting(                      \
+                 BEGINFRAME_FROM_HERE, source_id, sequence_number, frame_time, \
+                 deadline, interval, BeginFrameArgs::MISSED)))                 \
       .InSequence((obs).sequence)                                              \
       .WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args)))
 
@@ -40,24 +44,29 @@
 
 // Macros to send BeginFrameArgs on a FakeBeginFrameSink (and verify resulting
 // observer behaviour).
-#define SEND_BEGIN_FRAME(args_equal_to, source, frame_time, deadline, \
-                         interval)                                    \
-  {                                                                   \
-    BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs();  \
-    BeginFrameArgs new_args = CreateBeginFrameArgsForTesting(         \
-        BEGINFRAME_FROM_HERE, frame_time, deadline, interval);        \
-    ASSERT_FALSE(old_args == new_args);                               \
-    (source).TestOnBeginFrame(new_args);                              \
-    EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs());  \
+#define SEND_BEGIN_FRAME(args_equal_to, source, sequence_number, frame_time, \
+                         deadline, interval)                                 \
+  {                                                                          \
+    BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs();         \
+    BeginFrameArgs new_args = CreateBeginFrameArgsForTesting(                \
+        BEGINFRAME_FROM_HERE, (source).source_id(), sequence_number,         \
+        frame_time, deadline, interval);                                     \
+    ASSERT_FALSE(old_args == new_args);                                      \
+    (source).TestOnBeginFrame(new_args);                                     \
+    EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs());         \
   }
 
 // When dropping LastUsedBeginFrameArgs **shouldn't** change.
-#define SEND_BEGIN_FRAME_DROP(source, frame_time, deadline, interval) \
-  SEND_BEGIN_FRAME(old_args, source, frame_time, deadline, interval);
+#define SEND_BEGIN_FRAME_DROP(source, sequence_number, frame_time, deadline, \
+                              interval)                                      \
+  SEND_BEGIN_FRAME(old_args, source, sequence_number, frame_time, deadline,  \
+                   interval);
 
 // When used LastUsedBeginFrameArgs **should** be updated.
-#define SEND_BEGIN_FRAME_USED(source, frame_time, deadline, interval) \
-  SEND_BEGIN_FRAME(new_args, source, frame_time, deadline, interval);
+#define SEND_BEGIN_FRAME_USED(source, sequence_number, frame_time, deadline, \
+                              interval)                                      \
+  SEND_BEGIN_FRAME(new_args, source, sequence_number, frame_time, deadline,  \
+                   interval);
 
 namespace cc {
 
diff --git a/cc/test/begin_frame_source_test_unittest.cc b/cc/test/begin_frame_source_test_unittest.cc
index 601da31..d3765cc 100644
--- a/cc/test/begin_frame_source_test_unittest.cc
+++ b/cc/test/begin_frame_source_test_unittest.cc
@@ -14,110 +14,110 @@
 TEST(MockBeginFrameObserverTest, FailOnMissingCalls) {
   EXPECT_MOCK_FAILURE({
     ::testing::NiceMock<MockBeginFrameObserver> obs;
-    EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
-    EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300);
 
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2,
+                                                    400, 600, 300));
   });
 }
 
 TEST(MockBeginFrameObserverTest, FailOnMultipleCalls) {
   EXPECT_MOCK_FAILURE({
     ::testing::NiceMock<MockBeginFrameObserver> obs;
-    EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
-    EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300);
 
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1,
+                                                    100, 200, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1,
+                                                    100, 200, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2,
+                                                    400, 600, 300));
   });
 }
 
 TEST(MockBeginFrameObserverTest, FailOnWrongCallOrder) {
   EXPECT_MOCK_FAILURE({
     ::testing::NiceMock<MockBeginFrameObserver> obs;
-    EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
-    EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300);
+    EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300);
 
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
-    obs.OnBeginFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2,
+                                                    400, 600, 300));
+    obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1,
+                                                    100, 200, 300));
   });
 }
 
 TEST(MockBeginFrameObserverTest, ExpectOnBeginFrame) {
   ::testing::NiceMock<MockBeginFrameObserver> obs;
-  EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
-  EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
-  EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
+  EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300);
+  EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300);
+  EXPECT_BEGIN_FRAME_USED(obs, 0, 3, 700, 900, 300);
 
   EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
             MockBeginFrameObserver::kDefaultBeginFrameArgs);
 
   obs.OnBeginFrame(CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, 100, 200,
+      BEGINFRAME_FROM_HERE, 0, 1, 100, 200,
       300));  // One call to LastUsedBeginFrameArgs
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200,
+                                           300));
 
   obs.OnBeginFrame(CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, 400, 600,
+      BEGINFRAME_FROM_HERE, 0, 2, 400, 600,
       300));  // Multiple calls to LastUsedBeginFrameArgs
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 400, 600,
+                                           300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 400, 600,
+                                           300));
 
   obs.OnBeginFrame(CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, 700, 900,
+      BEGINFRAME_FROM_HERE, 0, 3, 700, 900,
       300));  // No calls to LastUsedBeginFrameArgs
 }
 
 TEST(MockBeginFrameObserverTest, ExpectOnBeginFrameStatus) {
   ::testing::NiceMock<MockBeginFrameObserver> obs;
-  EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
-  EXPECT_BEGIN_FRAME_DROP(obs, 400, 600, 300);
-  EXPECT_BEGIN_FRAME_DROP(obs, 450, 650, 300);
-  EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
+  EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300);
+  EXPECT_BEGIN_FRAME_DROP(obs, 0, 2, 400, 600, 300);
+  EXPECT_BEGIN_FRAME_DROP(obs, 0, 3, 450, 650, 300);
+  EXPECT_BEGIN_FRAME_USED(obs, 0, 4, 700, 900, 300);
 
   EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
             MockBeginFrameObserver::kDefaultBeginFrameArgs);
 
   // Used
-  obs.OnBeginFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
+  obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1,
+                                                  100, 200, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200,
+                                           300));
 
   // Dropped
-  obs.OnBeginFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300));
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
+  obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2,
+                                                  400, 600, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200,
+                                           300));
 
   // Dropped
-  obs.OnBeginFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 450, 650, 300));
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300));
+  obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3,
+                                                  450, 650, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200,
+                                           300));
 
   // Used
-  obs.OnBeginFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300));
-  EXPECT_EQ(
-      obs.LastUsedBeginFrameArgs(),
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300));
+  obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4,
+                                                  700, 900, 300));
+  EXPECT_EQ(obs.LastUsedBeginFrameArgs(),
+            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4, 700, 900,
+                                           300));
 }
 
 }  // namespace
diff --git a/cc/test/fake_external_begin_frame_source.cc b/cc/test/fake_external_begin_frame_source.cc
index 1a15b09..23051fa 100644
--- a/cc/test/fake_external_begin_frame_source.cc
+++ b/cc/test/fake_external_begin_frame_source.cc
@@ -65,9 +65,12 @@
 void FakeExternalBeginFrameSource::TestOnBeginFrame(
     const BeginFrameArgs& args) {
   DCHECK(CalledOnValidThread());
+  BeginFrameArgs modified_args = args;
+  modified_args.source_id = source_id();
+  modified_args.sequence_number = next_begin_frame_number_++;
   std::set<BeginFrameObserver*> observers(observers_);
   for (auto* obs : observers)
-    obs->OnBeginFrame(args);
+    obs->OnBeginFrame(modified_args);
   if (tick_automatically_)
     PostTestOnBeginFrame();
 }
@@ -77,10 +80,12 @@
       base::Bind(&FakeExternalBeginFrameSource::TestOnBeginFrame,
                  weak_ptr_factory_.GetWeakPtr()));
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(begin_frame_task_.callback(),
-                 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE)),
+      FROM_HERE, base::Bind(begin_frame_task_.callback(),
+                            CreateBeginFrameArgsForTesting(
+                                BEGINFRAME_FROM_HERE, source_id(),
+                                next_begin_frame_number_)),
       base::TimeDelta::FromMilliseconds(milliseconds_per_frame_));
+  next_begin_frame_number_++;
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_external_begin_frame_source.h b/cc/test/fake_external_begin_frame_source.h
index 8f2d4f09..ced3b3b 100644
--- a/cc/test/fake_external_begin_frame_source.h
+++ b/cc/test/fake_external_begin_frame_source.h
@@ -10,6 +10,7 @@
 #include "base/cancelable_callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/non_thread_safe.h"
+#include "cc/output/begin_frame_args.h"
 #include "cc/scheduler/begin_frame_source.h"
 
 namespace cc {
@@ -49,6 +50,7 @@
   const double milliseconds_per_frame_;
   Client* client_ = nullptr;
   bool paused_ = false;
+  uint64_t next_begin_frame_number_ = BeginFrameArgs::kStartingFrameNumber;
   std::set<BeginFrameObserver*> observers_;
   base::CancelableCallback<void(const BeginFrameArgs&)> begin_frame_task_;
   base::WeakPtrFactory<FakeExternalBeginFrameSource> weak_ptr_factory_;
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc
index 445b63d..d6b2cd07 100644
--- a/cc/test/fake_layer_tree_host_impl.cc
+++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -40,7 +40,7 @@
   // Start an impl frame so tests have a valid frame_time to work with.
   base::TimeTicks time_ticks = base::TimeTicks::FromInternalValue(1);
   WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, time_ticks));
 }
 
 FakeLayerTreeHostImpl::~FakeLayerTreeHostImpl() {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index c540c24d..b2fd2e17 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -207,7 +207,7 @@
     host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
     // Set the BeginFrameArgs so that methods which use it are able to.
     host_impl_->WillBeginImplFrame(CreateBeginFrameArgsForTesting(
-        BEGINFRAME_FROM_HERE,
+        BEGINFRAME_FROM_HERE, 0, 1,
         base::TimeTicks() + base::TimeDelta::FromMilliseconds(1)));
     host_impl_->DidFinishImplFrame();
 
@@ -362,8 +362,8 @@
   }
 
   LayerImpl* SetupScrollAndContentsLayers(const gfx::Size& content_size) {
-    LayerImpl* scroll_layer = CreateScrollAndContentsLayers(
-        host_impl_->active_tree(), content_size);
+    LayerImpl* scroll_layer =
+        CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
     host_impl_->active_tree()->DidBecomeActive();
     return scroll_layer;
   }
@@ -748,10 +748,11 @@
   DrawFrame();
 
   // We should not crash if the tree is replaced while we are scrolling.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->active_tree()->DetachLayers();
 
   scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
@@ -780,9 +781,8 @@
       host_impl_->GetEventListenerProperties(EventListenerClass::kMouseWheel));
 
   // But they don't influence the actual handling of the scroll gestures.
-  InputHandler::ScrollStatus status =
-    host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                   InputHandler::WHEEL);
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
   EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
   EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
@@ -1130,64 +1130,64 @@
 
   // Trying to scroll to the left/top will not succeed.
   EXPECT_FALSE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
           .did_scroll);
   EXPECT_FALSE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
           .did_scroll);
   EXPECT_FALSE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
           .did_scroll);
 
   // Scrolling to the right/bottom will succeed.
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get())
           .did_scroll);
 
   // Scrolling to left/top will now succeed.
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
           .did_scroll);
 
   // Scrolling diagonally against an edge will succeed.
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
           .did_scroll);
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get())
           .did_scroll);
 
   // Trying to scroll more than the available space will also succeed.
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get())
           .did_scroll);
 }
 
@@ -1211,10 +1211,11 @@
   DrawFrame();
   gfx::Point scroll_position(10, 10);
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL)
+          .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset());
 
@@ -1229,10 +1230,11 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL)
+          .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
 
@@ -1245,10 +1247,11 @@
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL)
+          .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
 
@@ -1345,7 +1348,7 @@
 
   base::TimeTicks now = base::TimeTicks::Now();
   host_impl_->WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now));
 
   // TODO(crbug.com/551134): We always request a new frame and a draw for
   // animations that are on the pending tree, but we don't need to do that
@@ -1457,7 +1460,7 @@
 
   base::TimeTicks now = base::TimeTicks::Now();
   host_impl_->WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now));
   EXPECT_TRUE(did_request_next_frame_);
   did_request_next_frame_ = false;
 
@@ -1537,7 +1540,7 @@
 
   base::TimeTicks now = base::TimeTicks::Now();
   host_impl_->WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now));
 
   host_impl_->ActivateAnimations();
   host_impl_->Animate();
@@ -1651,8 +1654,9 @@
 
     gfx::Vector2d scroll_delta(0, 10);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::WHEEL)
                   .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
@@ -1680,9 +1684,8 @@
   inner_scroll_layer->SetDrawsContent(true);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(500, 500),
-      outer_scroll_layer->MaxScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(500, 500),
+                   outer_scroll_layer->MaxScrollOffset());
 
   host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
                           InputHandler::TOUCHSCREEN);
@@ -1692,12 +1695,10 @@
   host_impl_->ScrollEnd(EndState().get());
 
   // Sanity check - we're zoomed in, starting from the origin.
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(0, 0),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(0, 0),
-      inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
+                   outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
+                   inner_scroll_layer->CurrentScrollOffset());
 
   // Scroll down - only the inner viewport should scroll.
   host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
@@ -1706,12 +1707,10 @@
       UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get());
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(50, 50),
-      inner_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(0, 0),
-      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
+                   inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
+                   outer_scroll_layer->CurrentScrollOffset());
 
   // Scroll down - outer viewport should start scrolling after the inner is at
   // its maximum.
@@ -1721,12 +1720,10 @@
       UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000.f, 1000.f)).get());
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(250, 250),
-      inner_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(300, 300),
-      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(250, 250),
+                   inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(300, 300),
+                   outer_scroll_layer->CurrentScrollOffset());
 }
 
 // Make sure scrolls smaller than a unit applied to the viewport don't get
@@ -1747,9 +1744,8 @@
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   // Sanity checks.
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(500, 500),
-      outer_scroll_layer->MaxScrollOffset());
+  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());
 
@@ -1758,12 +1754,10 @@
                           InputHandler::TOUCHSCREEN);
   host_impl_->ScrollBy(
       UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f)).get());
-  EXPECT_VECTOR2DF_EQ(
-      gfx::Vector2dF(0.125f, 0.125f),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR2DF_EQ(
-      gfx::Vector2dF(0, 0),
-      inner_scroll_layer->CurrentScrollOffset());
+  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(EndState().get());
 
   host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
@@ -1773,12 +1767,10 @@
                           InputHandler::TOUCHSCREEN);
   host_impl_->ScrollBy(
       UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f)).get());
-  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());
+  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(EndState().get());
 }
 
@@ -1800,44 +1792,37 @@
   inner_scroll_layer->SetDrawsContent(true);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(500, 500),
-      outer_scroll_layer->MaxScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(500, 500),
+                   outer_scroll_layer->MaxScrollOffset());
 
   host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
                           InputHandler::TOUCHSCREEN);
   host_impl_->PinchGestureBegin();
 
   host_impl_->PinchGestureUpdate(2, gfx::Point(250, 250));
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(0, 0),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(125, 125),
-      inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
+                   outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(125, 125),
+                   inner_scroll_layer->CurrentScrollOffset());
 
   // Needed so that the pinch is accounted for in draw properties.
   DrawFrame();
 
   host_impl_->ScrollBy(
       UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10.f, 10.f)).get());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(0, 0),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(130, 130),
-      inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
+                   outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(130, 130),
+                   inner_scroll_layer->CurrentScrollOffset());
 
   DrawFrame();
 
   host_impl_->ScrollBy(
       UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400.f, 400.f)).get());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(80, 80),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(250, 250),
-      inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(80, 80),
+                   outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(250, 250),
+                   inner_scroll_layer->CurrentScrollOffset());
 
   host_impl_->PinchGestureEnd();
   host_impl_->ScrollEnd(EndState().get());
@@ -1950,12 +1935,9 @@
       UpdateState(gfx::Point(0, 0), gfx::Vector2dF(10.f, 20.f)).get());
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(5, 10),
-      inner_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(),
-      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10),
+                   inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset());
 
   // Scroll by the inner viewport's max scroll extent, the remainder
   // should bubble up to the outer viewport.
@@ -1965,12 +1947,10 @@
       UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get());
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(50, 50),
-      inner_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(5, 10),
-      outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
+                   inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10),
+                   outer_scroll_layer->CurrentScrollOffset());
 
   // Scroll by the outer viewport's max scroll extent, it should all go to the
   // outer viewport.
@@ -1980,12 +1960,10 @@
       UpdateState(gfx::Point(0, 0), gfx::Vector2dF(190.f, 180.f)).get());
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(100, 100),
-      outer_scroll_layer->CurrentScrollOffset());
-  EXPECT_VECTOR_EQ(
-      gfx::Vector2dF(50, 50),
-      inner_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(100, 100),
+                   outer_scroll_layer->CurrentScrollOffset());
+  EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
+                   inner_scroll_layer->CurrentScrollOffset());
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
@@ -2021,9 +1999,9 @@
 
   // Set up two scrolling children of the root, one of which is a scroll parent
   // to the other. Scrolls shouldn't bubbling from the child.
-  LayerImpl *parent;
-  LayerImpl *child;
-  LayerImpl *child_clip;
+  LayerImpl* parent;
+  LayerImpl* child;
+  LayerImpl* child_clip;
 
   std::unique_ptr<LayerImpl> scroll_parent_clip =
       LayerImpl::Create(host_impl_->active_tree(), 6);
@@ -2104,7 +2082,6 @@
   }
 }
 
-
 TEST_F(LayerTreeHostImplTest, PinchGesture) {
   SetupScrollAndContentsLayers(gfx::Size(100, 100));
   host_impl_->SetViewportSize(gfx::Size(50, 50));
@@ -2336,14 +2313,14 @@
 
   float min_page_scale = 0.5f;
   float max_page_scale = 4.f;
-  base::TimeTicks start_time = base::TimeTicks() +
-                               base::TimeDelta::FromSeconds(1);
+  base::TimeTicks start_time =
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1);
   base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
   base::TimeTicks halfway_through_animation = start_time + duration / 2;
   base::TimeTicks end_time = start_time + duration;
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Non-anchor zoom-in
   {
@@ -2367,6 +2344,7 @@
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
     begin_frame_args.frame_time = start_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_redraw_);
@@ -2376,6 +2354,7 @@
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
     begin_frame_args.frame_time = halfway_through_animation;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_redraw_);
@@ -2386,6 +2365,7 @@
     did_request_next_frame_ = false;
     did_request_commit_ = false;
     begin_frame_args.frame_time = end_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_commit_);
@@ -2425,6 +2405,7 @@
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
     begin_frame_args.frame_time = start_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_redraw_);
@@ -2435,6 +2416,7 @@
     did_request_commit_ = false;
     did_request_next_frame_ = false;
     begin_frame_args.frame_time = end_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_redraw_);
@@ -2461,14 +2443,14 @@
 
   float min_page_scale = 0.5f;
   float max_page_scale = 4.f;
-  base::TimeTicks start_time = base::TimeTicks() +
-                               base::TimeDelta::FromSeconds(1);
+  base::TimeTicks start_time =
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1);
   base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
   base::TimeTicks halfway_through_animation = start_time + duration / 2;
   base::TimeTicks end_time = start_time + duration;
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Anchor zoom with unchanged page scale should not change scroll or scale.
   {
@@ -2485,17 +2467,20 @@
                                           duration)));
     host_impl_->ActivateSyncTree();
     begin_frame_args.frame_time = start_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     host_impl_->DidFinishImplFrame();
 
     begin_frame_args.frame_time = halfway_through_animation;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_redraw_);
     host_impl_->DidFinishImplFrame();
 
     begin_frame_args.frame_time = end_time;
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_TRUE(did_request_commit_);
@@ -2511,9 +2496,8 @@
 TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
   host_impl_->CreatePendingTree();
   host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
-  CreateScrollAndContentsLayers(
-      host_impl_->pending_tree(),
-      gfx::Size(100, 100));
+  CreateScrollAndContentsLayers(host_impl_->pending_tree(),
+                                gfx::Size(100, 100));
   host_impl_->pending_tree()->BuildPropertyTreesForTesting();
   host_impl_->ActivateSyncTree();
   DrawFrame();
@@ -2527,8 +2511,8 @@
                                                        max_page_scale);
   host_impl_->ActivateSyncTree();
 
-  base::TimeTicks start_time = base::TimeTicks() +
-                               base::TimeDelta::FromSeconds(1);
+  base::TimeTicks start_time =
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1);
   base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
   base::TimeTicks third_through_animation = start_time + duration / 3;
   base::TimeTicks halfway_through_animation = start_time + duration / 2;
@@ -2536,7 +2520,7 @@
   float target_scale = 2.f;
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   scroll_layer->layer_tree_impl()
       ->property_trees()
@@ -2562,6 +2546,7 @@
       std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation(
           gfx::Vector2d(), false, target_scale, duration)));
   begin_frame_args.frame_time = halfway_through_animation;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_FALSE(did_request_next_frame_);
@@ -2572,7 +2557,7 @@
   // It should also clear the pointer on the sync tree.
   host_impl_->ActivateSyncTree();
   EXPECT_EQ(nullptr,
-      host_impl_->sync_tree()->TakePendingPageScaleAnimation().get());
+            host_impl_->sync_tree()->TakePendingPageScaleAnimation().get());
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_TRUE(did_request_next_frame_);
 
@@ -2585,6 +2570,7 @@
   did_request_redraw_ = false;
   did_request_next_frame_ = false;
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_TRUE(did_request_redraw_);
@@ -2594,6 +2580,7 @@
   did_request_redraw_ = false;
   did_request_next_frame_ = false;
   begin_frame_args.frame_time = third_through_animation;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_TRUE(did_request_redraw_);
@@ -2606,6 +2593,7 @@
   did_request_redraw_ = false;
   did_request_next_frame_ = false;
   begin_frame_args.frame_time = halfway_through_animation;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_TRUE(did_request_redraw_);
@@ -2616,6 +2604,7 @@
   did_request_next_frame_ = false;
   did_request_commit_ = false;
   begin_frame_args.frame_time = end_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_TRUE(did_request_commit_);
@@ -2644,7 +2633,7 @@
   base::TimeTicks end_time = start_time + duration;
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f);
   scroll_layer->layer_tree_impl()
@@ -2658,18 +2647,21 @@
           gfx::Vector2d(), false, 2.f, duration)));
   host_impl_->ActivateSyncTree();
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_FALSE(did_complete_page_scale_animation_);
   host_impl_->DidFinishImplFrame();
 
   begin_frame_args.frame_time = halfway_through_animation;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_FALSE(did_complete_page_scale_animation_);
   host_impl_->DidFinishImplFrame();
 
   begin_frame_args.frame_time = end_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_TRUE(did_complete_page_scale_animation_);
@@ -2720,7 +2712,7 @@
                           0) {}
 
   BeginFrameArgs CurrentBeginFrameArgs() const override {
-    return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE,
+    return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1,
                                           fake_current_physical_time_);
   }
 
@@ -2807,7 +2799,7 @@
 
     // Before the scrollbar animation exists, we should not get redraws.
     BeginFrameArgs begin_frame_args =
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now);
+        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, fake_now);
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_FALSE(did_request_next_frame_);
@@ -2844,7 +2836,7 @@
     if (expecting_animations) {
       // Before the scrollbar animation begins, we should not get redraws.
       begin_frame_args =
-          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now);
+          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3, fake_now);
       host_impl_->WillBeginImplFrame(begin_frame_args);
       host_impl_->Animate();
       EXPECT_FALSE(did_request_next_frame_);
@@ -2864,7 +2856,7 @@
 
       // After the scrollbar animation begins, we should start getting redraws.
       begin_frame_args =
-          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now);
+          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4, fake_now);
       host_impl_->WillBeginImplFrame(begin_frame_args);
       host_impl_->Animate();
       EXPECT_TRUE(did_request_next_frame_);
@@ -2902,7 +2894,7 @@
       // Scrolling should have stopped the animation, so we should not be
       // getting redraws.
       begin_frame_args =
-          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now);
+          CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 5, fake_now);
       host_impl_->WillBeginImplFrame(begin_frame_args);
       host_impl_->Animate();
       EXPECT_FALSE(did_request_next_frame_);
@@ -3306,10 +3298,11 @@
   }
 
   // Scrolling should update metadata immediately.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   {
     CompositorFrameMetadata metadata =
@@ -4591,8 +4584,7 @@
   host_impl_->ScrollEnd(EndState().get());
 
   // The viewport offset shouldn't have changed.
-  EXPECT_EQ(viewport_offset,
-            host_impl_->active_tree()->TotalScrollOffset());
+  EXPECT_EQ(viewport_offset, host_impl_->active_tree()->TotalScrollOffset());
 
   // Scroll the viewports to max scroll offset.
   SetScrollOffsetDelta(outer_scroll, gfx::Vector2dF(0, 200.f));
@@ -4629,9 +4621,9 @@
   // Browser controls were hidden by 25px so the inner viewport should have
   // expanded by that much.
   LayerImpl* outer_container =
-            host_impl_->active_tree()->OuterViewportContainerLayer();
+      host_impl_->active_tree()->OuterViewportContainerLayer();
   LayerImpl* inner_container =
-            host_impl_->active_tree()->InnerViewportContainerLayer();
+      host_impl_->active_tree()->InnerViewportContainerLayer();
   EXPECT_EQ(gfx::SizeF(100.f, 100.f + 25.f),
             inner_container->BoundsForScrolling());
 
@@ -4826,12 +4818,13 @@
       BOTH, SHOWN, true);
   base::TimeTicks start_time = base::TimeTicks::Now();
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Pump an animation frame to put some delta in the browser controls.
   {
     begin_frame_args.frame_time =
         start_time + base::TimeDelta::FromMilliseconds(50);
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     host_impl_->UpdateAnimationState(true);
@@ -4858,6 +4851,7 @@
   {
     begin_frame_args.frame_time =
         start_time + base::TimeDelta::FromMilliseconds(200);
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     host_impl_->UpdateAnimationState(true);
@@ -4914,10 +4908,11 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_TRUE(did_request_redraw_);
@@ -4939,10 +4934,11 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_TRUE(did_request_redraw_);
@@ -5064,10 +5060,11 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5114,10 +5111,11 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5216,10 +5214,11 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta(scroll_delta);
   gfx::ScrollOffset expected_max_scroll(outer_scroll->MaxScrollOffset());
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5279,10 +5278,11 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(-8, -7);
-    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL)
-                  .thread);
+    EXPECT_EQ(
+        InputHandler::SCROLL_ON_IMPL_THREAD,
+        host_impl_
+            ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+            .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -5350,8 +5350,9 @@
   {
     gfx::Vector2d scroll_delta(0, -10);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point()).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
@@ -5378,8 +5379,9 @@
     // The next time we scroll we should only scroll the parent.
     scroll_delta = gfx::Vector2d(0, -3);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -5400,8 +5402,9 @@
     // should still scroll the child.
     scroll_delta = gfx::Vector2d(0, 7);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -5424,8 +5427,9 @@
 
     scroll_delta = gfx::Vector2d(0, -2);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(1, 1)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer());
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -5473,8 +5477,9 @@
   {
     gfx::Vector2d scroll_delta(0, 4);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::WHEEL)
                   .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
@@ -5549,10 +5554,11 @@
   host_impl_->active_tree()->DidBecomeActive();
 
   // Scrolling should still work even though we did not draw yet.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL)
+          .thread);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
@@ -5591,10 +5597,11 @@
   // Reset and scroll down with the wheel.
   SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5769,8 +5776,7 @@
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
-  LayerImpl* scroll_layer =
-      SetupScrollAndContentsLayers(gfx::Size(100, 100));
+  LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 
   // Scale the layer to twice its normal size.
   int scale = 2;
@@ -5804,10 +5810,11 @@
   // Reset and scroll down with the wheel.
   SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5866,29 +5873,17 @@
   }
   void DeliverInputForBeginFrame() override {}
 
-  gfx::ScrollOffset last_set_scroll_offset() {
-    return last_set_scroll_offset_;
-  }
+  gfx::ScrollOffset last_set_scroll_offset() { return last_set_scroll_offset_; }
 
-  gfx::ScrollOffset max_scroll_offset() const {
-    return max_scroll_offset_;
-  }
+  gfx::ScrollOffset max_scroll_offset() const { return max_scroll_offset_; }
 
-  gfx::SizeF scrollable_size() const {
-    return scrollable_size_;
-  }
+  gfx::SizeF scrollable_size() const { return scrollable_size_; }
 
-  float page_scale_factor() const {
-    return page_scale_factor_;
-  }
+  float page_scale_factor() const { return page_scale_factor_; }
 
-  float min_page_scale_factor() const {
-    return min_page_scale_factor_;
-  }
+  float min_page_scale_factor() const { return min_page_scale_factor_; }
 
-  float max_page_scale_factor() const {
-    return max_page_scale_factor_;
-  }
+  float max_page_scale_factor() const { return max_page_scale_factor_; }
 
  private:
   gfx::ScrollOffset last_set_scroll_offset_;
@@ -6050,10 +6045,11 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // In-bounds scrolling does not affect overscroll.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   scroll_result = host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   EXPECT_TRUE(scroll_result.did_scroll);
@@ -6212,8 +6208,9 @@
   {
     gfx::Vector2d scroll_delta(0, -10);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point()).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     scroll_result =
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -6226,8 +6223,9 @@
     // should still not reach the root layer.
     scroll_delta = gfx::Vector2d(0, -30);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
@@ -6249,8 +6247,9 @@
     // should scroll the child.
     scroll_delta = gfx::Vector2d(0, 70);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     scroll_result =
@@ -6275,8 +6274,9 @@
   {
     gfx::Vector2d scroll_delta(0, 8);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
+                                InputHandler::WHEEL)
                   .thread);
     scroll_result =
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -6315,10 +6315,11 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // Even though the layer can't scroll the overscroll still happens.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   scroll_result = host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   EXPECT_FALSE(scroll_result.did_scroll);
@@ -6343,8 +6344,9 @@
     // of the content. unnecessary glow effect calls shouldn't be
     // called while scrolling up without reaching the edge of the content.
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
-                                      InputHandler::WHEEL)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
+                                InputHandler::WHEEL)
                   .thread);
     scroll_result = host_impl_->ScrollBy(
         UpdateState(gfx::Point(), gfx::Vector2dF(0, 100)).get());
@@ -6362,8 +6364,9 @@
     // unusedrootDelta should be subtracted from applied delta so that
     // unwanted glow effect calls are not called.
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
+                                InputHandler::NON_BUBBLING_GESTURE)
                   .thread);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->FlingScrollBegin().thread);
@@ -6384,8 +6387,9 @@
     // TestCase to check  kEpsilon, which prevents minute values to trigger
     // gloweffect without reaching edge.
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
-                                      InputHandler::WHEEL)
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
+                                InputHandler::WHEEL)
                   .thread);
     scroll_result = host_impl_->ScrollBy(
         UpdateState(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f)).get());
@@ -6898,15 +6902,10 @@
 
     TileDrawQuad* test_blending_draw_quad =
         render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
-    test_blending_draw_quad->SetNew(shared_quad_state,
-                                    quad_rect_,
-                                    opaque_rect,
-                                    visible_quad_rect,
-                                    resource_id_,
+    test_blending_draw_quad->SetNew(shared_quad_state, quad_rect_, opaque_rect,
+                                    visible_quad_rect, resource_id_,
                                     gfx::RectF(0.f, 0.f, 1.f, 1.f),
-                                    gfx::Size(1, 1),
-                                    false,
-                                    false);
+                                    gfx::Size(1, 1), false, false);
     test_blending_draw_quad->visible_rect = quad_visible_rect_;
     EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending());
     EXPECT_EQ(has_render_surface_, !!render_surface());
@@ -7254,10 +7253,10 @@
 
 class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
  protected:
-  LayerTreeHostImplViewportCoveredTest() :
-      gutter_quad_material_(DrawQuad::SOLID_COLOR),
-      child_(NULL),
-      did_activate_pending_tree_(false) {}
+  LayerTreeHostImplViewportCoveredTest()
+      : gutter_quad_material_(DrawQuad::SOLID_COLOR),
+        child_(NULL),
+        did_activate_pending_tree_(false) {}
 
   std::unique_ptr<CompositorFrameSink> CreateFakeCompositorFrameSink(
       bool software) {
@@ -7548,11 +7547,12 @@
   EXPECT_SCOPED(TestLayerIsLargerThanViewportWithOnDraw());
 }
 
-class FakeDrawableLayerImpl: public LayerImpl {
+class FakeDrawableLayerImpl : public LayerImpl {
  public:
   static std::unique_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
     return base::WrapUnique(new FakeDrawableLayerImpl(tree_impl, id));
   }
+
  protected:
   FakeDrawableLayerImpl(LayerTreeImpl* tree_impl, int id)
       : LayerImpl(tree_impl, id) {}
@@ -7583,7 +7583,7 @@
   layer_tree_host_impl->SetVisible(true);
   layer_tree_host_impl->InitializeRenderer(compositor_frame_sink.get());
   layer_tree_host_impl->WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2));
   layer_tree_host_impl->SetViewportSize(gfx::Size(500, 500));
 
   std::unique_ptr<LayerImpl> root =
@@ -7687,8 +7687,8 @@
     gfx::Rect visible_quad_rect(quad_rect);
     SolidColorDrawQuad* my_quad =
         render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-    my_quad->SetNew(
-        shared_quad_state, quad_rect, visible_quad_rect, gray, false);
+    my_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, gray,
+                    false);
   }
 
  private:
@@ -7712,7 +7712,7 @@
   my_host_impl->SetVisible(true);
   my_host_impl->InitializeRenderer(compositor_frame_sink);
   my_host_impl->WillBeginImplFrame(
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2));
   my_host_impl->SetViewportSize(gfx::Size(100, 100));
 
   /*
@@ -7841,9 +7841,8 @@
   root_layer->SetBounds(gfx::Size(10, 10));
   root_layer->test_properties()->force_render_surface = true;
 
-  scoped_refptr<VideoFrame> softwareFrame =
-      media::VideoFrame::CreateColorFrame(
-          gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
+  scoped_refptr<VideoFrame> softwareFrame = media::VideoFrame::CreateColorFrame(
+      gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
   FakeVideoFrameProvider provider;
   provider.set_frame(softwareFrame);
   std::unique_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create(
@@ -7873,10 +7872,8 @@
 class MockDrawQuadsToFillScreenContext : public TestWebGraphicsContext3D {
  public:
   MOCK_METHOD1(useProgram, void(GLuint program));
-  MOCK_METHOD4(drawElements, void(GLenum mode,
-                                  GLsizei count,
-                                  GLenum type,
-                                  GLintptr offset));
+  MOCK_METHOD4(drawElements,
+               void(GLenum mode, GLsizei count, GLenum type, GLintptr offset));
 };
 
 TEST_F(LayerTreeHostImplTest, HasTransparentBackground) {
@@ -8612,10 +8609,11 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL)
-                  .thread);
+    EXPECT_EQ(
+        InputHandler::SCROLL_ON_IMPL_THREAD,
+        host_impl_
+            ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+            .thread);
 
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->FlingScrollBegin().thread);
@@ -8735,10 +8733,11 @@
 
   // We should have scrolled |child_scroll| even though it does not move
   // any layer that is a drawn RSLL member.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
 
   EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -8761,8 +8760,8 @@
           host_impl_->compositor_frame_sink());
 
   ui::LatencyInfo latency_info;
-  latency_info.AddLatencyNumber(
-      ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, 0);
+  latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0,
+                                0);
   std::unique_ptr<SwapPromise> swap_promise(
       new LatencyInfoSwapPromise(latency_info));
   host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
@@ -8925,8 +8924,8 @@
                                 InputHandler::TOUCHSCREEN)
                   .thread);
     EXPECT_TRUE(
-        host_impl_->ScrollBy(
-                      UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
+        host_impl_
+            ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
             .did_scroll);
     host_impl_->ScrollEnd(EndState().get());
 
@@ -8943,8 +8942,8 @@
                                 InputHandler::TOUCHSCREEN)
                   .thread);
     EXPECT_TRUE(
-        host_impl_->ScrollBy(
-                      UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
+        host_impl_
+            ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
             .did_scroll);
     host_impl_->ScrollEnd(EndState().get());
 
@@ -8977,7 +8976,7 @@
       ->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(),
                                                      gfx::ScrollOffset(0, 10));
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   EXPECT_FALSE(did_request_redraw_);
@@ -9110,10 +9109,11 @@
 
   LayerImpl* viewport_layer = host_impl_->InnerViewportScrollLayer();
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+          .thread);
   EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), viewport_layer->CurrentScrollOffset());
 
@@ -9121,8 +9121,8 @@
   // directly through to the viewport.
   const float delta = top_controls_height_;
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get())
           .did_scroll);
   EXPECT_FLOAT_EQ(0,
                   host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9130,8 +9130,8 @@
                    viewport_layer->CurrentScrollOffset());
 
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get())
           .did_scroll);
   EXPECT_FLOAT_EQ(0,
                   host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9162,8 +9162,8 @@
   const float residue = 35;
   float offset = top_controls_height_ - residue;
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
   EXPECT_FLOAT_EQ(-offset,
                   host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9184,7 +9184,7 @@
   // The browser controls should properly animate until finished, despite the
   // scroll offset being at the origin.
   BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, base::TimeTicks::Now());
+      BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
   while (did_request_next_frame_) {
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
@@ -9194,6 +9194,7 @@
         host_impl_->browser_controls_manager()->ControlsTopOffset();
 
     begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5);
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
     EXPECT_EQ(gfx::Vector2dF().ToString(),
@@ -9246,8 +9247,8 @@
   const float residue = 15;
   float offset = top_controls_height_ - residue;
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
   EXPECT_FLOAT_EQ(-offset,
                   host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9267,7 +9268,7 @@
 
   // Animate the browser controls to the limit.
   BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, base::TimeTicks::Now());
+      BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
   while (did_request_next_frame_) {
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
@@ -9277,6 +9278,7 @@
         host_impl_->browser_controls_manager()->ControlsTopOffset();
 
     begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5);
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
 
@@ -9322,8 +9324,8 @@
   const float residue = 15;
   float offset = top_controls_height_ - residue;
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
   EXPECT_FLOAT_EQ(-offset,
                   host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9343,7 +9345,7 @@
 
   // Animate the browser controls to the limit.
   BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting(
-      BEGINFRAME_FROM_HERE, base::TimeTicks::Now());
+      BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now());
   while (did_request_next_frame_) {
     did_request_redraw_ = false;
     did_request_next_frame_ = false;
@@ -9353,6 +9355,7 @@
         host_impl_->browser_controls_manager()->ControlsTopOffset();
 
     begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5);
+    begin_frame_args.sequence_number++;
     host_impl_->WillBeginImplFrame(begin_frame_args);
     host_impl_->Animate();
 
@@ -9391,8 +9394,8 @@
 
   float offset = 50;
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
   EXPECT_EQ(-offset,
             host_impl_->browser_controls_manager()->ControlsTopOffset());
@@ -9400,15 +9403,15 @@
             scroll_layer->CurrentScrollOffset().ToString());
 
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
   EXPECT_EQ(gfx::Vector2dF(0, offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
 
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get())
           .did_scroll);
 
   // Should have fully scrolled
@@ -9418,28 +9421,29 @@
   float overscrollamount = 10;
 
   // Overscroll the content
-  EXPECT_FALSE(
-      host_impl_->ScrollBy(UpdateState(gfx::Point(),
-                                       gfx::Vector2d(0, overscrollamount))
-                               .get())
-          .did_scroll);
+  EXPECT_FALSE(host_impl_
+                   ->ScrollBy(UpdateState(gfx::Point(),
+                                          gfx::Vector2d(0, overscrollamount))
+                                  .get())
+                   .did_scroll);
   EXPECT_EQ(gfx::Vector2dF(0, 2 * offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
   EXPECT_EQ(gfx::Vector2dF(0, overscrollamount).ToString(),
             host_impl_->accumulated_root_overscroll().ToString());
 
-  EXPECT_TRUE(host_impl_->ScrollBy(UpdateState(gfx::Point(),
-                                               gfx::Vector2d(0, -2 * offset))
-                                       .get())
-                  .did_scroll);
+  EXPECT_TRUE(
+      host_impl_
+          ->ScrollBy(
+              UpdateState(gfx::Point(), gfx::Vector2d(0, -2 * offset)).get())
+          .did_scroll);
   EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
   EXPECT_EQ(-offset,
             host_impl_->browser_controls_manager()->ControlsTopOffset());
 
   EXPECT_TRUE(
-      host_impl_->ScrollBy(
-                    UpdateState(gfx::Point(), gfx::Vector2d(0, -offset)).get())
+      host_impl_
+          ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -offset)).get())
           .did_scroll);
   EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -10290,7 +10294,7 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -10300,6 +10304,7 @@
   EXPECT_EQ(host_impl_->OuterViewportScrollLayer(), scrolling_layer);
 
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10309,6 +10314,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10324,6 +10330,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(200);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10335,6 +10342,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(250);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10371,8 +10379,9 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(200);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
   host_impl_->DidFinishImplFrame();
@@ -10389,6 +10398,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(250);
+  begin_frame_args.sequence_number++;
   // This is when the animation above gets promoted to STARTING.
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
@@ -10396,6 +10406,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(300);
+  begin_frame_args.sequence_number++;
   // This is when the animation above gets ticked.
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
@@ -10419,7 +10430,7 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Create animation with a 100ms delay.
   EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -10432,6 +10443,7 @@
 
   // First tick, animation is started.
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset());
@@ -10441,6 +10453,7 @@
   // the duration due to delay is 100ms.
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y());
@@ -10457,6 +10470,7 @@
   // target was called with a large value of jank.
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(100);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->UpdateAnimationState(true);
   EXPECT_LT(100, scrolling_layer->CurrentScrollOffset().y());
@@ -10475,7 +10489,7 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Perform animated scroll.
   EXPECT_EQ(
@@ -10485,6 +10499,7 @@
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
 
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10497,6 +10512,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10505,10 +10521,11 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Perform instant scroll.
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(0, y)).get(),
-                                    InputHandler::WHEEL)
-                .thread);
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_
+          ->ScrollBegin(BeginState(gfx::Point(0, y)).get(), InputHandler::WHEEL)
+          .thread);
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y),
                                                       InputHandler::WHEEL));
   host_impl_->ScrollBy(
@@ -10546,7 +10563,7 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Perform animated scroll.
   EXPECT_EQ(
@@ -10556,6 +10573,7 @@
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
 
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10568,6 +10586,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10600,7 +10619,7 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -10608,6 +10627,7 @@
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10617,6 +10637,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10632,6 +10653,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(200);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10643,6 +10665,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(250);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10676,7 +10699,7 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(250);
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
   EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(10.f, 20.f))
                 .thread);
@@ -10684,6 +10707,7 @@
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer());
 
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(begin_frame_args, start_time);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10),
                    inner_scroll_layer->CurrentScrollOffset());
@@ -10701,6 +10725,7 @@
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer());
 
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(begin_frame_args,
                            start_time + base::TimeDelta::FromMilliseconds(350));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
@@ -10718,6 +10743,7 @@
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(outer_scroll_layer, host_impl_->CurrentlyScrollingLayer());
 
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(begin_frame_args,
                            start_time + base::TimeDelta::FromMilliseconds(850));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
@@ -10735,6 +10761,7 @@
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer());
 
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(
       begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1200));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0),
@@ -10764,7 +10791,7 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(50);
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
       host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(90, 90)).thread);
@@ -10772,6 +10799,7 @@
   host_impl_->UpdateAnimationState(true);
   EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer());
 
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(begin_frame_args, start_time);
   float inner_x = inner_scroll_layer->CurrentScrollOffset().x();
   float inner_y = inner_scroll_layer->CurrentScrollOffset().y();
@@ -10790,6 +10818,7 @@
 
   // Verify that all the delta is applied to the inner viewport and nothing is
   // carried forward.
+  begin_frame_args.sequence_number++;
   BeginImplFrameAndAnimate(begin_frame_args,
                            start_time + base::TimeDelta::FromMilliseconds(350));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50),
@@ -10816,7 +10845,7 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -10824,6 +10853,7 @@
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10833,6 +10863,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(50);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10850,6 +10881,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(200);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10861,6 +10893,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(250);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10885,12 +10918,13 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(500, 500));
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
 
   begin_frame_args.frame_time = start_time;
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -10904,6 +10938,7 @@
 
   begin_frame_args.frame_time =
       start_time + base::TimeDelta::FromMilliseconds(200);
+  begin_frame_args.sequence_number++;
   host_impl_->WillBeginImplFrame(begin_frame_args);
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -11016,10 +11051,11 @@
     host_impl_->ScrollEnd(EndState().get());
 
     gfx::Vector2dF scroll_delta(0, 5);
-    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL)
-                  .thread);
+    EXPECT_EQ(
+        InputHandler::SCROLL_ON_IMPL_THREAD,
+        host_impl_
+            ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL)
+            .thread);
     EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
 
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -11193,7 +11229,7 @@
 
 TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
   FakeVideoFrameController controller;
 
   host_impl_->WillBeginImplFrame(begin_frame_args);
@@ -11216,7 +11252,7 @@
 
 TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
   BeginFrameArgs begin_frame_args =
-      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+      CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
   FakeVideoFrameController controller;
 
   host_impl_->WillBeginImplFrame(begin_frame_args);
@@ -11226,7 +11262,7 @@
   host_impl_->AddVideoFrameController(&controller);
   EXPECT_FALSE(controller.begin_frame_args().IsValid());
 
-  begin_frame_args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
+  begin_frame_args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3);
   EXPECT_FALSE(controller.begin_frame_args().IsValid());
   host_impl_->WillBeginImplFrame(begin_frame_args);
   EXPECT_TRUE(controller.begin_frame_args().IsValid());
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 593a7dc0..f30413a 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -450,7 +450,7 @@
   }
 
   BeginFrameArgs begin_frame_args(BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, frame_begin_time, base::TimeTicks(),
+      BEGINFRAME_FROM_HERE, 0, 1, frame_begin_time, base::TimeTicks(),
       BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL));
 
   // Start the impl frame.
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 09b345ff..6646b59 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -104,6 +104,7 @@
 
     # Configrations to make android load shared library from APK.
     uncompress_shared_libraries = true
+    page_align_shared_libraries = true
 
     forward_variables_from(invoker, "*")
 
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 3f00b3a..30ad2e99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -74,6 +74,7 @@
 import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
 import org.chromium.chrome.browser.download.DownloadManagerService;
 import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.gsa.ContextReporter;
 import org.chromium.chrome.browser.gsa.GSAAccountChangeListener;
@@ -845,6 +846,14 @@
                 }
             }
         });
+
+        DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
+            @Override
+            public void run() {
+                if (isActivityDestroyed()) return;
+                ForcedSigninProcessor.checkCanSignIn(ChromeActivity.this);
+            }
+        });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
index ba1c4ceb..55e5a68 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
@@ -127,18 +127,28 @@
          */
         private final Activity mActivity;
 
+        /**
+         * The modal dialog that is shown to the user.
+         */
         private Dialog mDialog;
 
         /**
+         * Whether the dialog can be canceled by the user.
+         */
+        private final boolean mCancelable;
+
+        /**
          * Create a new Modal Dialog handler for the specified activity and error code. The
          * specified activity may be used to launch the dialog via
          * {@link Activity#startActivityForResult(android.content.Intent, int)} and also to receive
          * the result via Activity's protected onActivityResult method.
          *
          * @param activity the activity to use
+         * @param cancelable whether the dialog can be canceled by the user
          */
-        public ModalDialog(Activity activity) {
+        public ModalDialog(Activity activity, boolean cancelable) {
             mActivity = activity;
+            mCancelable = cancelable;
         }
 
         /**
@@ -151,13 +161,17 @@
         protected final void handle(final Context context, final int errorCode) {
             mDialog = GoogleApiAvailability.getInstance().getErrorDialog(
                     mActivity, errorCode, NO_RESPONSE_REQUIRED);
+            // This can happen if |errorCode| is ConnectionResult.SERVICE_INVALID.
             if (mDialog != null) {
-                // This can happen if |errorCode| is ConnectionResult.SERVICE_INVALID.
+                mDialog.setCancelable(mCancelable);
                 mDialog.show();
             }
             sErrorHandlerActionHistogramSample.record(ERROR_HANDLER_ACTION_MODAL_DIALOG);
         }
 
+        /**
+         * Cancels the dialog.
+         */
         public void cancelDialog() {
             if (mDialog != null) {
                 mDialog.cancel();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
index 286fb2d..82ac3ab9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
@@ -9,6 +9,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.chrome.browser.services.AndroidEduAndChildAccountHelper;
 import org.chromium.chrome.browser.signin.AccountManagementFragment;
 import org.chromium.chrome.browser.signin.SigninManager;
@@ -90,4 +93,19 @@
             }
         });
     }
+
+    /**
+     * If forced signin is required by policy, check that Google Play Services is available, and
+     * show a non-cancelable dialog otherwise.
+     * @param activity The activity for which to show the dialog.
+     */
+    // TODO(bauerb): Once external dependencies reliably use policy to force sign-in,
+    // consider removing the child account / EDU checks.
+    public static void checkCanSignIn(final ChromeActivity activity) {
+        final Context appContext = activity.getApplicationContext();
+        if (SigninManager.get(appContext).isForceSigninEnabled()) {
+            ExternalAuthUtils.getInstance().canUseGooglePlayServices(appContext,
+                    new UserRecoverableErrorHandler.ModalDialog(activity, false));
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
index 3eb3a43..d313592d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -185,6 +185,31 @@
         mPublisherBar.setLayoutParams(params);
     }
 
+    private static String getAttributionString(SnippetArticle article) {
+        if (article.mPublishTimestampMilliseconds == 0) return article.mPublisher;
+
+        // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this
+        // has never been called before it loads the current time zone from disk. In most likelihood
+        // this will have been called previously and the current time zone will have been cached,
+        // but in some cases (eg instrumentation tests) it will cause a strict mode violation.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        CharSequence relativeTimeSpan;
+        try {
+            long time = SystemClock.elapsedRealtime();
+            relativeTimeSpan =
+                    DateUtils.getRelativeTimeSpanString(article.mPublishTimestampMilliseconds,
+                            System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS);
+            RecordHistogram.recordTimesHistogram("Android.StrictMode.SnippetUIBuildTime",
+                    SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+        // We format the publisher here so that having a publisher name in an RTL language
+        // doesn't mess up the formatting on an LTR device and vice versa.
+        return String.format(PUBLISHER_FORMAT_STRING,
+                BidiFormatter.getInstance().unicodeWrap(article.mPublisher), relativeTimeSpan);
+    }
+
     public void onBindViewHolder(SnippetArticle article, SuggestionsCategoryInfo categoryInfo) {
         super.onBindViewHolder();
 
@@ -196,28 +221,7 @@
         updateLayout();
 
         mHeadlineTextView.setText(mArticle.mTitle);
-
-        // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this
-        // has never been called before it loads the current time zone from disk. In most likelihood
-        // this will have been called previously and the current time zone will have been cached,
-        // but in some cases (eg instrumentation tests) it will cause a strict mode violation.
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            long time = SystemClock.elapsedRealtime();
-            CharSequence relativeTimeSpan = DateUtils.getRelativeTimeSpanString(
-                    mArticle.mPublishTimestampMilliseconds, System.currentTimeMillis(),
-                    DateUtils.MINUTE_IN_MILLIS);
-            RecordHistogram.recordTimesHistogram("Android.StrictMode.SnippetUIBuildTime",
-                    SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS);
-
-            // We format the publisher here so that having a publisher name in an RTL language
-            // doesn't mess up the formatting on an LTR device and vice versa.
-            String publisherAttribution = String.format(PUBLISHER_FORMAT_STRING,
-                    BidiFormatter.getInstance().unicodeWrap(mArticle.mPublisher), relativeTimeSpan);
-            mPublisherTextView.setText(publisherAttribution);
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
+        mPublisherTextView.setText(getAttributionString(mArticle));
 
         // The favicon of the publisher should match the TextView height.
         int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
index d3b204ba..cd62bc1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
@@ -306,8 +306,9 @@
 
     private boolean checkGooglePlayServicesAvailable() {
         ExternalAuthUtils extAuthUtils = ExternalAuthUtils.getInstance();
+        boolean cancelable = !SigninManager.get(getContext()).isForceSigninEnabled();
         mGooglePlayServicesUpdateErrorHandler = new UserRecoverableErrorHandler.ModalDialog(
-                mDelegate.getActivity());
+                mDelegate.getActivity(), cancelable);
         int resultCode = extAuthUtils.canUseGooglePlayServicesResultCode(
                 getContext(), mGooglePlayServicesUpdateErrorHandler);
         if (extAuthUtils.isGooglePlayServicesUpdateRequiredError(resultCode)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index 134b7505..15d00d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -257,6 +257,13 @@
     }
 
     /**
+     * @return Whether force sign-in is enabled by policy.
+     */
+    public boolean isForceSigninEnabled() {
+        return nativeIsForceSigninEnabled(mNativeSigninManagerAndroid);
+    }
+
+    /**
      * Registers a SignInStateObserver to be notified when the user signs in or out of Chrome.
      */
     public void addSignInStateObserver(SignInStateObserver observer) {
@@ -376,7 +383,7 @@
         } else {
             Activity activity = mSignInState.activity;
             UserRecoverableErrorHandler errorHandler = activity != null
-                    ? new UserRecoverableErrorHandler.ModalDialog(activity)
+                    ? new UserRecoverableErrorHandler.ModalDialog(activity, !isForceSigninEnabled())
                     : new UserRecoverableErrorHandler.SystemNotification();
             ExternalAuthUtils.getInstance().canUseGooglePlayServices(mContext, errorHandler);
             Log.w(TAG, "Cancelling the sign-in process as Google Play services is unavailable");
@@ -666,6 +673,7 @@
     private static native void nativeIsUserManaged(String username, Callback<Boolean> callback);
     private native long nativeInit();
     private native boolean nativeIsSigninAllowedByPolicy(long nativeSigninManagerAndroid);
+    private native boolean nativeIsForceSigninEnabled(long nativeSigninManagerAndroid);
     private native void nativeCheckPolicyBeforeSignIn(
             long nativeSigninManagerAndroid, String username);
     private native void nativeFetchPolicyBeforeSignIn(long nativeSigninManagerAndroid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index e9efda1..a39909f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -115,13 +115,14 @@
         recordUpdate(storage, false);
 
         if (fetchedInfo != null) {
-            updateAsync(fetchedInfo, bestIconUrl);
+            updateAsync(fetchedInfo, bestIconUrl, false /* isManifestStale */);
             return;
         }
 
-        // Since we could not fetch the Web Manifest, we do not know what the best icon URL is. Pass
-        // an empty "best icon URL" to tell the server that there is no best icon URL.
-        updateAsync(mInfo, "");
+        // Tell the server that the our version of the Web Manifest might be stale and to ignore
+        // our Web Manifest data if the server's Web Manifest data is newer. This scenario can
+        // occur if the Web Manifest is temporarily unreachable.
+        updateAsync(mInfo, "", true /* isManifestStale */);
     }
 
     /**
@@ -134,14 +135,22 @@
     /**
      * Sends request to WebAPK Server to update WebAPK.
      */
-    protected void updateAsync(WebApkInfo info, String bestIconUrl) {
+    protected void updateAsync(WebApkInfo info, String bestIconUrl, boolean isManifestStale) {
         int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageName());
-        String bestIconMurmur2Hash = info.iconUrlToMurmur2HashMap().get(bestIconUrl);
+        int size = info.iconUrlToMurmur2HashMap().size();
+        String[] iconUrls = new String[size];
+        String[] iconHashes = new String[size];
+        int i = 0;
+        for (Map.Entry<String, String> entry : info.iconUrlToMurmur2HashMap().entrySet()) {
+            iconUrls[i] = entry.getKey();
+            String iconHash = entry.getValue();
+            iconHashes[i] = iconHash != null ? iconHash : "";
+            i++;
+        }
         nativeUpdateAsync(info.id(), info.manifestStartUrl(), info.scopeUri().toString(),
-                info.name(), info.shortName(), bestIconUrl, bestIconMurmur2Hash, info.icon(),
-                info.iconUrlToMurmur2HashMap().keySet().toArray(new String[0]), info.displayMode(),
-                info.orientation(), info.themeColor(), info.backgroundColor(), info.manifestUrl(),
-                info.webApkPackageName(), versionCode);
+                info.name(), info.shortName(), bestIconUrl, info.icon(), iconUrls, iconHashes,
+                info.displayMode(), info.orientation(), info.themeColor(), info.backgroundColor(),
+                info.manifestUrl(), info.webApkPackageName(), versionCode, isManifestStale);
     }
 
     /**
@@ -309,7 +318,8 @@
     }
 
     private static native void nativeUpdateAsync(String id, String startUrl, String scope,
-            String name, String shortName, String bestIconUrl, String bestIconMurmur2Hash,
-            Bitmap bestIcon, String[] iconUrls, int displayMode, int orientation, long themeColor,
-            long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion);
+            String name, String shortName, String bestIconUrl, Bitmap bestIcon, String[] iconUrls,
+            String[] iconHashes, int displayMode, int orientation, long themeColor,
+            long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion,
+            boolean isManifestStale);
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 94e0b1c6..69fd24ae 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -75,7 +75,8 @@
         }
 
         @Override
-        protected void updateAsync(WebApkInfo fetchedInfo, String bestIconUrl) {
+        protected void updateAsync(WebApkInfo fetchedInfo, String bestIconUrl,
+                boolean isManifestStale) {
             mNeedsUpdate = true;
         }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index bca36a9..38d8dd5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -138,7 +138,7 @@
         }
 
         @Override
-        protected void updateAsync(WebApkInfo info, String bestIconUrl) {
+        protected void updateAsync(WebApkInfo info, String bestIconUrl, boolean isManifestStale) {
             mUpdateRequested = true;
             mUpdateName = info.name();
         }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 4e3e5b1..149ac6a5 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -2465,6 +2465,15 @@
   <message name="IDS_OPTIONS_DEVICE_GROUP_STYLUS_SETTINGS_BUTTON" desc="Text on button/link to take user to the 'Stylus' overlay.">
     Stylus settings
   </message>
+  <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_LABEL" desc="Label for dropdown menu in 'Stylus' overlay that lets the user set a preferred note-taking app.">
+    Note-taking app:
+  </message>
+  <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE" desc="Entry in 'Stylus' overlay's note-taking app menu when no apps are available.">
+    None available
+  </message>
+  <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID" desc="Entry in 'Stylus' overlay's note-taking app menu when Android isn't ready yet.">
+    Waiting for Android apps...
+  </message>
   <message name="IDS_OPTIONS_SETTINGS_SET_TIME_BUTTON" desc="In the settings tab, the prompt on the button to open a dialog to set date and time.">
     Set date and time
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 84b8c583..37486eba 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1937,8 +1937,8 @@
         Copy to clipboard
       </message>
       <message name="IDS_DOWNLOAD_NOTIFICATION_ANNOTATE"
-               desc="In the download notification, the text of the button that opens the file (if it is an image) in the Google Keep app so the user can annotate it">
-        Annotate in Google Keep
+               desc="In the download notification, the text of the button that opens the file (if it is an image) in the note-taking app so the user can annotate it">
+        Annotate image
       </message>
 
       <!-- Download Context Menu Items -->
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc
index 757cbf78a..4b30b5d 100644
--- a/chrome/browser/android/signin/signin_manager_android.cc
+++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/signin/oauth2_token_service_delegate_android.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/pref_names.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -269,6 +270,14 @@
   return SigninManagerFactory::GetForProfile(profile_)->IsSigninAllowed();
 }
 
+jboolean SigninManagerAndroid::IsForceSigninEnabled(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  // prefs::kForceBrowserSignin is set in Local State, not in user prefs.
+  PrefService* prefs = g_browser_process->local_state();
+  return prefs->GetBoolean(prefs::kForceBrowserSignin);
+}
+
 jboolean SigninManagerAndroid::IsSignedInOnNative(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h
index b618efc..ea7f1ecd 100644
--- a/chrome/browser/android/signin/signin_manager_android.h
+++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -71,6 +71,10 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
+  jboolean IsForceSigninEnabled(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
   jboolean IsSignedInOnNative(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj);
 
diff --git a/chrome/browser/android/webapk/webapk.proto b/chrome/browser/android/webapk/webapk.proto
index 90b171e..05615266 100644
--- a/chrome/browser/android/webapk/webapk.proto
+++ b/chrome/browser/android/webapk/webapk.proto
@@ -48,6 +48,10 @@
   // The cpu abi of the browser making the request.
   optional string android_abi = 8;
 
+  // If set true, this flag indicates that the Web App Manifest of the site is
+  // no longer available.
+  optional bool stale_manifest = 9;
+
   reserved 4;
 }
 
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 840e1147..a34ddd3 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -111,7 +111,8 @@
 std::unique_ptr<webapk::WebApk> BuildWebApkProtoInBackground(
     const ShortcutInfo& shortcut_info,
     const SkBitmap& shortcut_icon,
-    const std::string& shortcut_icon_murmur2_hash) {
+    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+    bool is_manifest_stale) {
   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
 
   std::unique_ptr<webapk::WebApk> webapk(new webapk::WebApk);
@@ -120,11 +121,7 @@
       base::android::BuildInfo::GetInstance()->package_name());
   webapk->set_requester_application_version(version_info::GetVersionNumber());
   webapk->set_android_abi(getCurrentAbi());
-
-  // TODO(hanxi): crbug.com/665078. Add a flag in WebAPK's proto to indicate
-  // that the Web Manifest data in the proto might be stale.
-  if (shortcut_icon_murmur2_hash.empty())
-    return webapk;
+  webapk->set_stale_manifest(is_manifest_stale);
 
   webapk::WebAppManifest* web_app_manifest = webapk->mutable_manifest();
   web_app_manifest->set_name(base::UTF16ToUTF8(shortcut_info.name));
@@ -144,22 +141,33 @@
   scope->assign(GetScope(shortcut_info).spec());
 
   webapk::Image* best_image = web_app_manifest->add_icons();
-  best_image->set_src(shortcut_info.best_icon_url.spec());
-  best_image->set_hash(shortcut_icon_murmur2_hash);
+  std::string best_icon_url = shortcut_info.best_icon_url.spec();
+  best_image->set_src(best_icon_url);
+  auto it = icon_url_to_murmur2_hash.find(best_icon_url);
+  if (it != icon_url_to_murmur2_hash.end())
+    best_image->set_hash(it->second);
   std::vector<unsigned char> png_bytes;
   gfx::PNGCodec::EncodeBGRASkBitmap(shortcut_icon, false, &png_bytes);
   best_image->set_image_data(&png_bytes.front(), png_bytes.size());
 
-  for (const std::string& icon_url : shortcut_info.icon_urls) {
-    if (icon_url == shortcut_info.best_icon_url.spec())
+  for (const auto& entry : icon_url_to_murmur2_hash) {
+    if (entry.first == shortcut_info.best_icon_url.spec())
       continue;
     webapk::Image* image = web_app_manifest->add_icons();
-    image->set_src(icon_url);
+    image->set_src(entry.first);
+    image->set_hash(entry.second);
   }
 
   return webapk;
 }
 
+// Calls the callback when the |webapk| request is created.
+void OnWebApkProtoBuilt(
+    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
+    std::unique_ptr<webapk::WebApk> webapk) {
+  callback.Run(std::move(webapk));
+}
+
 // Returns task runner for running background tasks.
 scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() {
   return content::BrowserThread::GetBlockingPool()
@@ -250,33 +258,36 @@
   DownloadAppIconAndComputeMurmur2Hash();
 }
 
-void WebApkInstaller::UpdateAsync(content::BrowserContext* browser_context,
-                                  const FinishCallback& finish_callback,
-                                  const std::string& icon_murmur2_hash,
-                                  const std::string& webapk_package,
-                                  int webapk_version) {
+void WebApkInstaller::UpdateAsync(
+    content::BrowserContext* browser_context,
+    const FinishCallback& finish_callback,
+    const std::string& webapk_package,
+    int webapk_version,
+    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+    bool is_manifest_stale) {
   UpdateAsyncWithURLRequestContextGetter(
       Profile::FromBrowserContext(browser_context)->GetRequestContext(),
-      finish_callback, icon_murmur2_hash, webapk_package, webapk_version);
+      finish_callback, webapk_package, webapk_version,
+      icon_url_to_murmur2_hash, is_manifest_stale);
 }
 
 void WebApkInstaller::UpdateAsyncWithURLRequestContextGetter(
     net::URLRequestContextGetter* request_context_getter,
     const FinishCallback& finish_callback,
-    const std::string& icon_murmur2_hash,
     const std::string& webapk_package,
-    int webapk_version) {
+    int webapk_version,
+    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+    bool is_manifest_stale) {
   request_context_getter_ = request_context_getter;
   finish_callback_ = finish_callback;
-  shortcut_icon_murmur2_hash_ = icon_murmur2_hash;
   webapk_package_ = webapk_package;
   webapk_version_ = webapk_version;
   task_type_ = UPDATE;
 
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_,
-                  shortcut_icon_, shortcut_icon_murmur2_hash_),
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
+                 icon_url_to_murmur2_hash, is_manifest_stale),
       base::Bind(&WebApkInstaller::SendUpdateWebApkRequest,
                   weak_ptr_factory_.GetWeakPtr()));
 }
@@ -296,6 +307,17 @@
     OnFailure();
 }
 
+void WebApkInstaller::BuildWebApkProtoInBackgroundForTesting(
+    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
+    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+    bool is_manifest_stale) {
+  base::PostTaskAndReplyWithResult(
+      GetBackgroundTaskRunner().get(), FROM_HERE,
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
+                 icon_url_to_murmur2_hash, is_manifest_stale),
+      base::Bind(&OnWebApkProtoBuilt, callback));
+}
+
 // static
 bool WebApkInstaller::Register(JNIEnv* env) {
   return RegisterNativesImpl(env);
@@ -410,18 +432,24 @@
   timer_.Stop();
   icon_hasher_.reset();
 
-  shortcut_icon_murmur2_hash_ = icon_murmur2_hash;
-
   // An empty hash indicates that |icon_hasher_| encountered an error.
   if (icon_murmur2_hash.empty()) {
     OnFailure();
     return;
   }
 
+  std::map<std::string, std::string> icon_url_to_murmur2_hash;
+  for (const std::string& icon_url : shortcut_info_.icon_urls) {
+    if (icon_url != shortcut_info_.best_icon_url.spec())
+      icon_url_to_murmur2_hash[icon_url] = "";
+    else
+      icon_url_to_murmur2_hash[icon_url] = icon_murmur2_hash;
+  }
+
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_,
-                  shortcut_icon_, shortcut_icon_murmur2_hash_),
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
+                 icon_url_to_murmur2_hash, false /* is_manifest_stale */),
       base::Bind(&WebApkInstaller::SendCreateWebApkRequest,
                  weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index 052f988e0..95ad8fb 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_INSTALLER_H_
 
 #include <jni.h>
+#include <map>
 #include <memory>
 
 #include "base/android/scoped_java_ref.h"
@@ -63,19 +64,22 @@
   // Talks to the Chrome WebAPK server to update a WebAPK on the server and to
   // the Google Play server to install the downloaded WebAPK. Calls |callback|
   // after the request to install the WebAPK is sent to the Google Play server.
-  void UpdateAsync(content::BrowserContext* browser_context,
-                   const FinishCallback& callback,
-                   const std::string& icon_murmur2_hash,
-                   const std::string& webapk_package,
-                   int webapk_version);
+  void UpdateAsync(
+      content::BrowserContext* browser_context,
+      const FinishCallback& callback,
+      const std::string& webapk_package,
+      int webapk_version,
+      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+      bool is_manifest_stale);
 
   // Same as UpdateAsync() but uses the passed in |request_context_getter|.
   void UpdateAsyncWithURLRequestContextGetter(
       net::URLRequestContextGetter* request_context_getter,
       const FinishCallback& callback,
-      const std::string& icon_murmur2_hash,
       const std::string& webapk_package,
-      int webapk_version);
+      int webapk_version,
+      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+      bool is_manifest_stale);
 
   // Sets the timeout for the server requests.
   void SetTimeoutMs(int timeout_ms);
@@ -85,6 +89,13 @@
                          const base::android::JavaParamRef<jobject>& obj,
                          jboolean success);
 
+  // Creates a WebApk install or update request.
+  // Should be used only for testing.
+  void BuildWebApkProtoInBackgroundForTesting(
+      const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
+      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+      bool is_manifest_stale);
+
   // Registers JNI hooks.
   static bool Register(JNIEnv* env);
 
@@ -222,10 +233,6 @@
   // WebAPK app icon.
   const SkBitmap shortcut_icon_;
 
-  // Murmur2 hash of the bitmap at the app icon URL prior to any transformations
-  // being applied to the bitmap (such as encoding/decoding the icon bitmap).
-  std::string shortcut_icon_murmur2_hash_;
-
   // WebAPK server URL.
   GURL server_url_;
 
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 0734459e..b5ad46d 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -128,17 +128,18 @@
   }
 
   void RunUpdateWebApk() {
-    const std::string kIconMurmur2Hash = "0";
     const int kWebApkVersion = 1;
 
-    WebApkInstaller* installer = CreateWebApkInstaller();
+    std::map<std::string, std::string> icon_url_to_murmur2_hash {
+      {best_icon_url_.spec(), "0"} };
 
-    installer->UpdateAsyncWithURLRequestContextGetter(
+    CreateWebApkInstaller()->UpdateAsyncWithURLRequestContextGetter(
         url_request_context_getter_.get(),
         base::Bind(&WebApkInstallerRunner::OnCompleted, base::Unretained(this)),
-        kIconMurmur2Hash,
         kDownloadedWebApkPackageName,
-        kWebApkVersion);
+        kWebApkVersion,
+        icon_url_to_murmur2_hash,
+        false /* is_manifest_stale */);
 
     Run();
   }
@@ -204,6 +205,52 @@
   return std::move(response);
 }
 
+// Builds WebApk proto and blocks till done.
+class BuildProtoRunner {
+ public:
+  BuildProtoRunner() {}
+
+  ~BuildProtoRunner() {}
+
+  void BuildSync(
+      const GURL& best_icon_url,
+      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
+      bool is_manifest_stale) {
+    ShortcutInfo info(GURL::EmptyGURL());
+    info.best_icon_url = best_icon_url;
+
+    // WebApkInstaller owns itself.
+    WebApkInstaller* installer =
+        new TestWebApkInstaller(info, SkBitmap(), false);
+    installer->BuildWebApkProtoInBackgroundForTesting(
+        base::Bind(&BuildProtoRunner::OnBuiltWebApkProto,
+                   base::Unretained(this)),
+        icon_url_to_murmur2_hash,
+        is_manifest_stale);
+
+    base::RunLoop run_loop;
+    on_completed_callback_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  webapk::WebApk* GetWebApkRequest() { return webapk_request_.get(); }
+
+ private:
+  // Called when the |webapk_request_| is populated.
+  void OnBuiltWebApkProto(std::unique_ptr<webapk::WebApk> webapk) {
+    webapk_request_ = std::move(webapk);
+    on_completed_callback_.Run();
+  }
+
+  // The populated webapk::WebApk.
+  std::unique_ptr<webapk::WebApk> webapk_request_;
+
+  // Called after the |webapk_request_| is built.
+  base::Closure on_completed_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(BuildProtoRunner);
+};
+
 }  // anonymous namespace
 
 class WebApkInstallerTest : public ::testing::Test {
@@ -248,6 +295,10 @@
         new WebApkInstallerRunner(best_icon_url_));
   }
 
+  std::unique_ptr<BuildProtoRunner> CreateBuildProtoRunner() {
+    return std::unique_ptr<BuildProtoRunner>(new BuildProtoRunner());
+  }
+
   net::test_server::EmbeddedTestServer* test_server() { return &test_server_; }
 
  private:
@@ -368,3 +419,75 @@
   runner->RunInstallWebApk();
   EXPECT_TRUE(runner->success());
 }
+
+// When there is no Web Manifest available for a site, an empty |best_icon_url|
+// is used to build a WebApk update request. Tests the request can be built
+// properly.
+TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsObsolete) {
+  GURL icon_url_1 = test_server()->GetURL("/icon1.png");
+  GURL icon_url_2 = test_server()->GetURL("/icon2.png");
+  std::string icon_murmur2_hash_1 = "1";
+  std::string icon_murmur2_hash_2 = "2";
+  std::map<std::string, std::string> icon_url_to_murmur2_hash;
+  icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1;
+  icon_url_to_murmur2_hash[icon_url_2.spec()] = icon_murmur2_hash_2;
+
+  std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner();
+  runner->BuildSync(GURL(""), icon_url_to_murmur2_hash,
+                    true /* is_manifest_stale*/);
+  webapk::WebApk* webapk_request = runner->GetWebApkRequest();
+  ASSERT_NE(nullptr, webapk_request);
+
+  webapk::WebAppManifest manifest = webapk_request->manifest();
+  ASSERT_EQ(3, manifest.icons_size());
+
+  webapk::Image icons[3];
+  for (int i = 0; i < 3; ++i)
+    icons[i] = manifest.icons(i);
+
+  EXPECT_EQ("", icons[0].src());
+  EXPECT_FALSE(icons[0].has_hash());
+  EXPECT_TRUE(icons[0].has_image_data());
+
+  EXPECT_EQ(icon_url_1.spec(), icons[1].src());
+  EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash());
+  EXPECT_FALSE(icons[1].has_image_data());
+
+  EXPECT_EQ(icon_url_2.spec(), icons[2].src());
+  EXPECT_EQ(icon_murmur2_hash_2, icons[2].hash());
+  EXPECT_FALSE(icons[2].has_image_data());
+}
+
+// Tests a WebApk install or update request is built properly when the Chrome
+// knows the best icon URL of a site after fetching its Web Manifest.
+TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsAvailable) {
+  GURL icon_url_1 = test_server()->GetURL("/icon.png");
+  GURL best_icon_url = test_server()->GetURL(kBestIconUrl);
+  std::string icon_murmur2_hash_1 = "1";
+  std::string best_icon_murmur2_hash = "0";
+  std::map<std::string, std::string> icon_url_to_murmur2_hash;
+  icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1;
+  icon_url_to_murmur2_hash[best_icon_url.spec()] =
+      best_icon_murmur2_hash;
+
+  std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner();
+  runner->BuildSync(best_icon_url, icon_url_to_murmur2_hash,
+                    false /* is_manifest_stale*/);
+  webapk::WebApk* webapk_request = runner->GetWebApkRequest();
+  ASSERT_NE(nullptr, webapk_request);
+
+  webapk::WebAppManifest manifest = webapk_request->manifest();
+  ASSERT_EQ(2, manifest.icons_size());
+
+  webapk::Image icons[2];
+  for (int i = 0; i < 2; ++i)
+    icons[i] = manifest.icons(i);
+
+  EXPECT_EQ(best_icon_url.spec(), icons[0].src());
+  EXPECT_EQ(best_icon_murmur2_hash, icons[0].hash());
+  EXPECT_TRUE(icons[0].has_image_data());
+
+  EXPECT_EQ(icon_url_1.spec(), icons[1].src());
+  EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash());
+  EXPECT_FALSE(icons[1].has_image_data());
+}
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index a00af61..e8951658 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -53,16 +53,17 @@
     const JavaParamRef<jstring>& java_name,
     const JavaParamRef<jstring>& java_short_name,
     const JavaParamRef<jstring>& java_best_icon_url,
-    const JavaParamRef<jstring>& java_best_icon_murmur2_hash,
     const JavaParamRef<jobject>& java_best_icon_bitmap,
     const JavaParamRef<jobjectArray>& java_icon_urls,
+    const JavaParamRef<jobjectArray>& java_icon_hashes,
     jint java_display_mode,
     jint java_orientation,
     jlong java_theme_color,
     jlong java_background_color,
     const JavaParamRef<jstring>& java_web_manifest_url,
     const JavaParamRef<jstring>& java_webapk_package,
-    jint java_webapk_version) {
+    jint java_webapk_version,
+    jboolean java_is_manifest_stale) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   Profile* profile = ProfileManager::GetLastUsedProfile();
@@ -92,15 +93,19 @@
   base::android::AppendJavaStringArrayToStringVector(
       env, java_icon_urls.obj(), &info.icon_urls);
 
+  std::vector<std::string> icon_hashes;
+  base::android::AppendJavaStringArrayToStringVector(
+      env, java_icon_hashes.obj(), &icon_hashes);
+
+  std::map<std::string, std::string> icon_url_to_murmur2_hash;
+  for (size_t i = 0; i < info.icon_urls.size(); ++i)
+    icon_url_to_murmur2_hash[info.icon_urls[i]] = icon_hashes[i];
+
   gfx::JavaBitmap java_bitmap_lock(java_best_icon_bitmap);
   SkBitmap best_icon_bitmap =
       gfx::CreateSkBitmapFromJavaBitmap(java_bitmap_lock);
   best_icon_bitmap.setImmutable();
 
-  std::string best_icon_murmur2_hash;
-  ConvertJavaStringToUTF8(env, java_best_icon_murmur2_hash,
-                          &best_icon_murmur2_hash);
-
   std::string webapk_package;
   ConvertJavaStringToUTF8(env, java_webapk_package, &webapk_package);
 
@@ -108,5 +113,6 @@
   installer->UpdateAsync(
       profile,
       base::Bind(&WebApkUpdateManager::OnBuiltWebApk, id),
-      best_icon_murmur2_hash, webapk_package, java_webapk_version);
+      webapk_package, java_webapk_version, icon_url_to_murmur2_hash,
+      java_is_manifest_stale);
 }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d85c0a5..0a65e623f 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -359,6 +359,10 @@
     "display/overscan_calibrator.h",
     "display/quirks_manager_delegate_impl.cc",
     "display/quirks_manager_delegate_impl.h",
+    "display/touch_calibrator/touch_calibrator_controller.cc",
+    "display/touch_calibrator/touch_calibrator_controller.h",
+    "display/touch_calibrator/touch_calibrator_view.cc",
+    "display/touch_calibrator/touch_calibrator_view.h",
     "drive/debug_info_collector.cc",
     "drive/debug_info_collector.h",
     "drive/download_handler.cc",
@@ -1449,6 +1453,7 @@
     "certificate_provider/certificate_provider_service_unittest.cc",
     "customization/customization_document_unittest.cc",
     "display/display_preferences_unittest.cc",
+    "display/touch_calibrator/touch_calibrator_controller_unittest.cc",
     "drive/download_handler_unittest.cc",
     "drive/drive_file_stream_reader_unittest.cc",
     "drive/drive_integration_service_unittest.cc",
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
new file mode 100644
index 0000000..29a99197
--- /dev/null
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h"
+
+#include "ash/shell.h"
+#include "ash/touch/touch_transformer_controller.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
+#include "ui/display/screen.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+
+namespace chromeos {
+
+// Time interval after a touch event during which all other touch events are
+// ignored during calibration.
+const base::TimeDelta TouchCalibratorController::kTouchIntervalThreshold =
+    base::TimeDelta::FromMilliseconds(200);
+
+TouchCalibratorController::TouchCalibratorController()
+    : last_touch_timestamp_(base::Time::Now()) {
+  ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
+}
+
+TouchCalibratorController::~TouchCalibratorController() {
+  ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
+  touch_calibrator_views_.clear();
+  StopCalibration();
+}
+
+void TouchCalibratorController::OnDisplayConfigurationChanged() {
+  touch_calibrator_views_.clear();
+  StopCalibration();
+}
+
+void TouchCalibratorController::StartCalibration(
+    const display::Display& target_display) {
+  is_calibrating_ = true;
+  target_display_ = target_display;
+
+  // Clear all touch calibrator views used in any previous calibration.
+  touch_calibrator_views_.clear();
+
+  // Reset the calibration data.
+  touch_point_quad_.fill(std::make_pair(gfx::Point(0, 0), gfx::Point(0, 0)));
+
+  std::vector<display::Display> displays =
+      display::Screen::GetScreen()->GetAllDisplays();
+
+  for (const display::Display& display : displays) {
+    bool is_primary_view = display.id() == target_display_.id();
+    touch_calibrator_views_[display.id()] =
+        base::MakeUnique<TouchCalibratorView>(display, is_primary_view);
+  }
+
+  // TODO(malaykeshav): Call TouchTransformerController::SetForCalibration()
+
+  // Add self as an event handler target.
+  ash::Shell::GetInstance()->AddPreTargetHandler(this);
+}
+
+void TouchCalibratorController::StopCalibration() {
+  if (!is_calibrating_)
+    return;
+  is_calibrating_ = false;
+
+  // TODO(malaykeshav): Call TouchTransformerController::SetForCalibration()
+
+  // Remove self as the event handler.
+  ash::Shell::GetInstance()->RemovePreTargetHandler(this);
+
+  // Transition all touch calibrator views to their final state for a graceful
+  // exit.
+  for (const auto& it : touch_calibrator_views_)
+    it.second->SkipToFinalState();
+}
+
+// ui::EventHandler:
+void TouchCalibratorController::OnKeyEvent(ui::KeyEvent* key) {
+  if (!is_calibrating_)
+    return;
+  // Detect ESC key press.
+  if (key->type() == ui::ET_KEY_PRESSED && key->key_code() == ui::VKEY_ESCAPE)
+    StopCalibration();
+  key->StopPropagation();
+}
+
+void TouchCalibratorController::OnTouchEvent(ui::TouchEvent* touch) {
+  if (!is_calibrating_)
+    return;
+  if (touch->type() != ui::ET_TOUCH_RELEASED)
+    return;
+  if (base::Time::Now() - last_touch_timestamp_ < kTouchIntervalThreshold)
+    return;
+
+  last_touch_timestamp_ = base::Time::Now();
+
+  TouchCalibratorView* target_screen_calibration_view =
+      touch_calibrator_views_[target_display_.id()].get();
+
+  int state_index;
+  // Maps the state to an integer value. Assigns a non negative integral value
+  // for a state in which the user can interact with the the interface.
+  switch (target_screen_calibration_view->state()) {
+    case TouchCalibratorView::DISPLAY_POINT_1:
+      state_index = 0;
+      break;
+    case TouchCalibratorView::DISPLAY_POINT_2:
+      state_index = 1;
+      break;
+    case TouchCalibratorView::DISPLAY_POINT_3:
+      state_index = 2;
+      break;
+    case TouchCalibratorView::DISPLAY_POINT_4:
+      state_index = 3;
+      break;
+    default:
+      // Return early if the interface is in a state that does not allow user
+      // interaction.
+      return;
+  }
+
+  // Store touch point corresponding to its display point.
+  gfx::Point display_point;
+  if (target_screen_calibration_view->GetDisplayPointLocation(&display_point)) {
+    touch_point_quad_[state_index] =
+        std::make_pair(display_point, touch->location());
+  }
+
+  // If this is the final state, then store all calibration data and stop
+  // calibration.
+  if (target_screen_calibration_view->state() ==
+      TouchCalibratorView::CALIBRATION_COMPLETE) {
+    ash::Shell::GetInstance()->display_manager()->SetTouchCalibrationData(
+        target_display_.id(), touch_point_quad_,
+        target_screen_calibration_view->size());
+    StopCalibration();
+    return;
+  }
+
+  target_screen_calibration_view->AdvanceToNextState();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h
new file mode 100644
index 0000000..4f51cd4
--- /dev/null
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
+#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
+
+#include <map>
+
+#include "ash/display/window_tree_host_manager.h"
+#include "base/time/time.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/managed_display_info.h"
+#include "ui/events/event_handler.h"
+
+namespace ui {
+class KeyEvent;
+class TouchEvent;
+}
+
+namespace chromeos {
+
+class TouchCalibratorView;
+
+// TouchCalibratorController is responsible for collecting the touch calibration
+// associated data from the user. It instantiates TouchCalibratorView classes to
+// present an interface the user can interact with for calibration.
+// Touch calibration is restricted to calibrate only one display at a time.
+class TouchCalibratorController : public ui::EventHandler,
+                                  public ash::WindowTreeHostManager::Observer {
+ public:
+  using CalibrationPointPairQuad =
+      display::TouchCalibrationData::CalibrationPointPairQuad;
+
+  static const base::TimeDelta kTouchIntervalThreshold;
+
+  TouchCalibratorController();
+  ~TouchCalibratorController() override;
+
+  // ui::EventHandler
+  void OnKeyEvent(ui::KeyEvent* event) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
+
+  // WindowTreeHostManager::Observer
+  void OnDisplayConfigurationChanged() override;
+
+  // Starts the calibration process for the given |target_display|.
+  void StartCalibration(const display::Display& target_display);
+
+  // Stops any ongoing calibration process.
+  void StopCalibration();
+
+  bool is_calibrating() { return is_calibrating_; }
+
+ private:
+  friend class TouchCalibratorControllerTest;
+  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, StartCalibration);
+  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, KeyEventIntercept);
+  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, TouchThreshold);
+
+  // A map for TouchCalibrator view with the key as display id of the display
+  // it is present in.
+  std::map<int64_t, std::unique_ptr<TouchCalibratorView>>
+      touch_calibrator_views_;
+
+  // The display which is being calibrated by the touch calibrator controller.
+  // This is valid only if |is_calibrating| is set to true.
+  display::Display target_display_;
+
+  // During calibration this stores the timestamp when the previous touch event
+  // was received.
+  base::Time last_touch_timestamp_;
+
+  // Is true if a touch calibration is already underprocess.
+  bool is_calibrating_ = false;
+
+  // An array of Calibration point pairs. This stores all the 4 display and
+  // touch input point pairs that will be used for calibration.
+  CalibrationPointPairQuad touch_point_quad_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchCalibratorController);
+};
+
+}  // namespace chromeos
+#endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
new file mode 100644
index 0000000..d5627a5
--- /dev/null
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event_handler.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/events/test/events_test_utils.h"
+
+using namespace display;
+
+namespace chromeos {
+
+class TouchCalibratorControllerTest : public ash::test::AshTestBase {
+ public:
+  TouchCalibratorControllerTest() {}
+
+  const Display& InitDisplays() {
+    // Initialize 2 displays each with resolution 500x500.
+    UpdateDisplay("500x500,500x500");
+    // Assuming index 0 points to the native display, we will calibrate the
+    // touch display at index 1.
+    const int kTargetDisplayIndex = 1;
+    DisplayIdList display_id_list =
+        display_manager()->GetCurrentDisplayIdList();
+    int64_t target_display_id = display_id_list[kTargetDisplayIndex];
+    const Display& touch_display =
+        display_manager()->GetDisplayForId(target_display_id);
+    return touch_display;
+  }
+
+  void StartCalibrationChecks(TouchCalibratorController* ctrl,
+                              const Display& target_display) {
+    EXPECT_FALSE(ctrl->is_calibrating());
+    EXPECT_FALSE(!!ctrl->touch_calibrator_views_.size());
+
+    ctrl->StartCalibration(target_display);
+    EXPECT_TRUE(ctrl->is_calibrating());
+
+    // There should be a touch calibrator view associated with each of the
+    // active displays.
+    EXPECT_EQ(ctrl->touch_calibrator_views_.size(),
+              display_manager()->GetCurrentDisplayIdList().size());
+
+    TouchCalibratorView* target_calibrator_view =
+        ctrl->touch_calibrator_views_[target_display.id()].get();
+
+    EXPECT_EQ(target_calibrator_view->state(), TouchCalibratorView::UNKNOWN);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TouchCalibratorControllerTest);
+};
+
+TEST_F(TouchCalibratorControllerTest, StartCalibration) {
+  const Display& touch_display = InitDisplays();
+  TouchCalibratorController touch_calibrator_controller;
+  StartCalibrationChecks(&touch_calibrator_controller, touch_display);
+
+  ui::EventTargetTestApi test_api(ash::Shell::GetInstance());
+  const ui::EventHandlerList& handlers = test_api.pre_target_handlers();
+  ui::EventHandlerList::const_iterator event_target =
+      std::find(handlers.begin(), handlers.end(), &touch_calibrator_controller);
+  EXPECT_NE(event_target, handlers.end());
+}
+
+TEST_F(TouchCalibratorControllerTest, KeyEventIntercept) {
+  const Display& touch_display = InitDisplays();
+  TouchCalibratorController touch_calibrator_controller;
+  StartCalibrationChecks(&touch_calibrator_controller, touch_display);
+
+  ui::test::EventGenerator& eg = GetEventGenerator();
+  EXPECT_TRUE(touch_calibrator_controller.is_calibrating());
+  eg.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
+  EXPECT_FALSE(touch_calibrator_controller.is_calibrating());
+}
+
+TEST_F(TouchCalibratorControllerTest, TouchThreshold) {
+  const Display& touch_display = InitDisplays();
+  TouchCalibratorController touch_calibrator_controller;
+  StartCalibrationChecks(&touch_calibrator_controller, touch_display);
+
+  ui::test::EventGenerator& eg = GetEventGenerator();
+
+  base::Time current_timestamp = base::Time::Now();
+  touch_calibrator_controller.last_touch_timestamp_ = current_timestamp;
+
+  eg.set_current_location(gfx::Point(0, 0));
+  eg.PressTouch();
+  eg.ReleaseTouch();
+
+  EXPECT_EQ(touch_calibrator_controller.last_touch_timestamp_,
+            current_timestamp);
+
+  current_timestamp = base::Time::Now();
+  touch_calibrator_controller.last_touch_timestamp_ =
+      current_timestamp - (TouchCalibratorController::kTouchIntervalThreshold);
+
+  eg.PressTouch();
+  eg.ReleaseTouch();
+
+  EXPECT_LT(current_timestamp,
+            touch_calibrator_controller.last_touch_timestamp_);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
new file mode 100644
index 0000000..c0484a6
--- /dev/null
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
+
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/widget/widget.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr char kWidgetName[] = "TouchCalibratorOverlay";
+
+// Returns the initialization params for the widget that contains the touch
+// calibrator view.
+views::Widget::InitParams GetWidgetParams(aura::Window* root_window) {
+  views::Widget::InitParams params;
+  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
+  params.name = kWidgetName;
+  params.keep_on_top = true;
+  params.accept_events = true;
+  params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+  params.parent = ash::Shell::GetContainer(
+      root_window, ash::kShellWindowId_OverlayContainer);
+  return params;
+}
+
+}  // namespace
+
+TouchCalibratorView::TouchCalibratorView(const display::Display& target_display,
+                                         bool is_primary_view)
+    : display_(target_display), is_primary_view_(is_primary_view) {
+  aura::Window* root = ash::Shell::GetInstance()
+                           ->window_tree_host_manager()
+                           ->GetRootWindowForDisplayId(display_.id());
+  widget_.reset(new views::Widget);
+  widget_->Init(GetWidgetParams(root));
+  widget_->SetContentsView(this);
+  widget_->SetBounds(display_.bounds());
+  widget_->Show();
+  set_owned_by_client();
+}
+
+TouchCalibratorView::~TouchCalibratorView() {
+  state_ = UNKNOWN;
+  widget_->Hide();
+}
+
+void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) {
+  OnPaintBackground(canvas);
+}
+
+void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) {}
+
+void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) {
+}
+
+void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) {
+  AnimationEnded(animation);
+}
+
+void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) {}
+
+void TouchCalibratorView::AdvanceToNextState() {}
+
+bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) {
+  if (!is_primary_view_)
+    return false;
+  return false;
+}
+
+void TouchCalibratorView::SkipToFinalState() {}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
new file mode 100644
index 0000000..aeaf20f
--- /dev/null
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
+#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
+
+#include "base/macros.h"
+#include "ui/display/display.h"
+#include "ui/gfx/animation/animation_delegate.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Widget;
+}
+
+namespace chromeos {
+
+// An overlay view used during touch calibration. This view is responsible for
+// all animations and UX during touch calibration on all displays currently
+// active on the device. The view on the display being calibrated is the primary
+// touch calibration view.
+// |TouchCalibratorView| acts as a state machine and has an API to toggle its
+// state or get the current state.
+class TouchCalibratorView : public views::View, public gfx::AnimationDelegate {
+ public:
+  // Different states of |TouchCalibratorView| in order.
+  enum State {
+    UNKNOWN = 0,
+    BACKGROUND_FADING_IN,  // Transition state where the background is fading
+                           // in.
+    DISPLAY_POINT_1,       // Static state where the touch point is at its first
+                           // location.
+    ANIMATING_1_TO_2,  // Transition state when the touch point is being moved
+                       // from one location to another.
+    DISPLAY_POINT_2,   // Static state where the touch point is at its second
+                       // location.
+    ANIMATING_2_TO_3,
+    DISPLAY_POINT_3,  // Static state where the touch point is at its third
+                      // location.
+    ANIMATING_3_TO_4,
+    DISPLAY_POINT_4,       // Static state where the touch point is at its final
+                           // location.
+    CALIBRATION_COMPLETE,  // Static state when the calibration complete message
+                           // is displayed to the user.
+    BACKGROUND_FADING_OUT  // Transition state where the background is fading
+                           // out
+  };
+
+  TouchCalibratorView(const display::Display& target_display,
+                      bool is_primary_view);
+  ~TouchCalibratorView() override;
+
+  // views::View overrides:
+  void OnPaint(gfx::Canvas* canvas) override;
+  void OnPaintBackground(gfx::Canvas* canvas) override;
+
+  // gfx::AnimationDelegate overrides:
+  void AnimationEnded(const gfx::Animation* animation) override;
+  void AnimationProgressed(const gfx::Animation* animation) override;
+  void AnimationCanceled(const gfx::Animation* animation) override;
+
+  // Moves the touch calibrator view to its next state.
+  void AdvanceToNextState();
+
+  // Skips to the final state. Should be used to cancel calibration and hide all
+  // views from the screen with a smooth transition out animation.
+  void SkipToFinalState();
+
+  // Returns true if |location| is set by the end of this function call. If set,
+  // |location| will point to the center of the circle that the user sees during
+  // the touch calibration UX.
+  bool GetDisplayPointLocation(gfx::Point* location);
+
+  // Returns the current state of the view.
+  State state() { return state_; }
+
+ private:
+  // The target display on which this view is rendered on.
+  const display::Display display_;
+
+  // True if this view is on the display that is being calibrated.
+  bool is_primary_view_ = false;
+
+  std::unique_ptr<views::Widget> widget_;
+
+  State state_ = UNKNOWN;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchCalibratorView);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc
index b5c8f1a..d7d9c09 100644
--- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc
@@ -11,6 +11,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/signin/core/account_id/account_id.h"
 #include "components/user_manager/known_user.h"
 
 namespace chromeos {
@@ -66,7 +67,8 @@
     if (users->GetString(i, &current_user_email)) {
       delegate_->RemovePendingBootstrapUser(
           user_manager::known_user::GetAccountId(current_user_email,
-                                                 std::string() /* gaia_id */));
+                                                 std::string() /* id */,
+                                                 AccountType::UNKNOWN));
     }
   }
 
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc
index 0cee775..d2434ec 100644
--- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc
@@ -178,7 +178,7 @@
   }
 
   user_context_.SetAccountId(user_manager::known_user::GetAccountId(
-      user_manager::CanonicalizeUserID(email), gaia_id));
+      user_manager::CanonicalizeUserID(email), gaia_id, AccountType::GOOGLE));
   StartCheckExistingKeys();
 }
 
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
index cdce1cf1..04a96fb5 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
@@ -40,8 +40,9 @@
 EasyUnlockTpmKeyManager* EasyUnlockTpmKeyManagerFactory::GetForUser(
     const std::string& user_id) {
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-  const user_manager::User* user = user_manager->FindUser(
-      user_manager::known_user::GetAccountId(user_id, std::string()));
+  const user_manager::User* user =
+      user_manager->FindUser(user_manager::known_user::GetAccountId(
+          user_id, std::string() /* id */, AccountType::UNKNOWN));
   if (!user)
     return NULL;
   Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
diff --git a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
index 9362462..affc58b 100644
--- a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
@@ -85,8 +85,8 @@
 
 void ChromeUserSelectionScreen::CheckForPublicSessionDisplayNameChange(
     policy::DeviceLocalAccountPolicyBroker* broker) {
-  const AccountId& account_id =
-      user_manager::known_user::GetAccountId(broker->user_id(), std::string());
+  const AccountId& account_id = user_manager::known_user::GetAccountId(
+      broker->user_id(), std::string() /* id */, AccountType::UNKNOWN);
   DCHECK(account_id.is_valid());
   const std::string& display_name = broker->GetDisplayName();
   if (display_name == public_session_display_names_[account_id])
@@ -116,8 +116,8 @@
 
 void ChromeUserSelectionScreen::CheckForPublicSessionLocalePolicyChange(
     policy::DeviceLocalAccountPolicyBroker* broker) {
-  const AccountId& account_id =
-      user_manager::known_user::GetAccountId(broker->user_id(), std::string());
+  const AccountId& account_id = user_manager::known_user::GetAccountId(
+      broker->user_id(), std::string() /* id */, AccountType::UNKNOWN);
   DCHECK(account_id.is_valid());
   const policy::PolicyMap::Entry* entry =
       broker->core()->store()->policy_map().Get(policy::key::kSessionLocales);
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index dedfd7d..a3e89b2c 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -372,8 +372,8 @@
   std::string owner_email;
   chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner,
                                            &owner_email);
-  const AccountId owner =
-      user_manager::known_user::GetAccountId(owner_email, std::string());
+  const AccountId owner = user_manager::known_user::GetAccountId(
+      owner_email, std::string() /* id */, AccountType::UNKNOWN);
 
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 581a1c1..94406b4 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -568,7 +568,7 @@
                      .WithShutdownBehavior(
                          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
                      .WithPriority(base::TaskPriority::BACKGROUND)
-                     .WithFileIO(),
+                     .MayBlock(),
       base::Bind(&base::PathExists, GetRlzDisabledFlagPath()),
       base::Bind(&UserSessionManager::InitRlzImpl, AsWeakPtr(), profile));
 #endif
@@ -898,11 +898,7 @@
 }
 
 void UserSessionManager::StoreUserContextDataBeforeProfileIsCreated() {
-  // Store obfuscated GAIA ID.
-  if (!user_context_.GetGaiaID().empty()) {
-    user_manager::known_user::UpdateGaiaID(user_context_.GetAccountId(),
-                                           user_context_.GetGaiaID());
-  }
+  user_manager::known_user::UpdateId(user_context_.GetAccountId());
 }
 
 void UserSessionManager::StartCrosSession() {
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
index 7cbad7c..bca16cf0 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
@@ -70,7 +70,7 @@
   Key key(attempt->password);
   key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
   const AccountId account_id = user_manager::known_user::GetAccountId(
-      attempt->username, std::string() /* gaia_id */);
+      attempt->username, std::string() /* id */, AccountType::UNKNOWN);
   const cryptohome::Identification cryptohome_id(account_id);
   cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount(
       cryptohome_id, key.GetSecret(), flags,
@@ -95,7 +95,7 @@
   Key master_key(plain_text_master_key);
   master_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
   const AccountId account_id = user_manager::known_user::GetAccountId(
-      attempt->username, std::string() /* gaia_id */);
+      attempt->username, std::string() /* id */, AccountType::UNKNOWN);
   cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey(
       cryptohome::Identification(account_id), user_key.GetSecret(),
       master_key.GetSecret(),
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index c7c0dba2..c72843a6 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -500,8 +500,8 @@
 
 void ChromeUserManagerImpl::OnExternalDataSet(const std::string& policy,
                                               const std::string& user_id) {
-  const AccountId account_id =
-      user_manager::known_user::GetAccountId(user_id, std::string());
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      user_id, std::string() /* id */, AccountType::UNKNOWN);
   if (policy == policy::key::kUserAvatarImage)
     GetUserImageManager(account_id)->OnExternalDataSet(policy);
   else if (policy == policy::key::kWallpaperImage)
@@ -512,8 +512,8 @@
 
 void ChromeUserManagerImpl::OnExternalDataCleared(const std::string& policy,
                                                   const std::string& user_id) {
-  const AccountId account_id =
-      user_manager::known_user::GetAccountId(user_id, std::string());
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      user_id, std::string() /* id */, AccountType::UNKNOWN);
   if (policy == policy::key::kUserAvatarImage)
     GetUserImageManager(account_id)->OnExternalDataCleared(policy);
   else if (policy == policy::key::kWallpaperImage)
@@ -526,8 +526,8 @@
     const std::string& policy,
     const std::string& user_id,
     std::unique_ptr<std::string> data) {
-  const AccountId account_id =
-      user_manager::known_user::GetAccountId(user_id, std::string());
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      user_id, std::string() /* id */, AccountType::UNKNOWN);
   if (policy == policy::key::kUserAvatarImage)
     GetUserImageManager(account_id)
         ->OnExternalDataFetched(policy, std::move(data));
@@ -539,8 +539,8 @@
 }
 
 void ChromeUserManagerImpl::OnPolicyUpdated(const std::string& user_id) {
-  const AccountId account_id =
-      user_manager::known_user::GetAccountId(user_id, std::string());
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      user_id, std::string() /* id */, AccountType::UNKNOWN);
   const user_manager::User* user = FindUser(account_id);
   if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
     return;
@@ -1216,8 +1216,8 @@
 void ChromeUserManagerImpl::SetUserAffiliation(
     const std::string& user_email,
     const AffiliationIDSet& user_affiliation_ids) {
-  const AccountId& account_id =
-      user_manager::known_user::GetAccountId(user_email, std::string());
+  const AccountId& account_id = user_manager::known_user::GetAccountId(
+      user_email, std::string() /* id */, AccountType::UNKNOWN);
   user_manager::User* user = FindUserAndModify(account_id);
 
   if (user) {
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager.cc b/chrome/browser/chromeos/printing/printer_pref_manager.cc
index e92824e..3e52bf1e 100644
--- a/chrome/browser/chromeos/printing/printer_pref_manager.cc
+++ b/chrome/browser/chromeos/printing/printer_pref_manager.cc
@@ -10,6 +10,8 @@
 #include <vector>
 
 #include "base/guid.h"
+#include "base/json/json_reader.h"
+#include "base/md5.h"
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
@@ -65,7 +67,16 @@
 
 }  // anonymous namespace
 
-PrinterPrefManager::PrinterPrefManager(Profile* profile) : profile_(profile) {}
+PrinterPrefManager::PrinterPrefManager(Profile* profile) : profile_(profile) {
+  pref_change_registrar_.Init(profile->GetPrefs());
+  pref_change_registrar_.Add(
+      prefs::kRecommendedNativePrinters,
+      base::Bind(&PrinterPrefManager::UpdateRecommendedPrinters,
+                 base::Unretained(this)));
+  UpdateRecommendedPrinters();
+}
+
+PrinterPrefManager::~PrinterPrefManager() {}
 
 // static
 void PrinterPrefManager::RegisterProfilePrefs(
@@ -96,8 +107,32 @@
   return printers;
 }
 
+std::vector<std::unique_ptr<Printer>>
+PrinterPrefManager::GetRecommendedPrinters() const {
+  std::vector<std::unique_ptr<Printer>> printers;
+
+  for (const std::string& key : recommended_printer_ids_) {
+    const base::DictionaryValue& printer_dictionary =
+        *(recommended_printers_.at(key));
+    std::unique_ptr<Printer> printer =
+        printing::RecommendedPrinterToPrinter(printer_dictionary);
+    if (printer) {
+      printer->set_source(Printer::SRC_POLICY);
+      printers.push_back(std::move(printer));
+    }
+  }
+
+  return printers;
+}
+
 std::unique_ptr<Printer> PrinterPrefManager::GetPrinter(
     const std::string& printer_id) const {
+  // check for a policy printer first
+  const auto& policy_printers = recommended_printers_;
+  auto found = policy_printers.find(printer_id);
+  if (found != policy_printers.end())
+    return printing::RecommendedPrinterToPrinter(*(found->second));
+
   const base::ListValue* values = GetPrinterList(profile_);
   const base::DictionaryValue* printer = FindPrinterPref(values, printer_id);
 
@@ -108,6 +143,7 @@
   if (printer->id().empty())
     printer->set_id(base::GenerateGUID());
 
+  DCHECK_EQ(Printer::SRC_USER_PREFS, printer->source());
   std::unique_ptr<base::DictionaryValue> updated_printer =
       printing::PrinterToPref(*printer);
   UpdatePrinterPref(profile_, printer->id(), std::move(updated_printer));
@@ -123,4 +159,41 @@
   return printer && printer_list->Remove(*printer, nullptr);
 }
 
+// This method is not thread safe and could interact poorly with readers of
+// |recommended_printers_|.
+void PrinterPrefManager::UpdateRecommendedPrinters() {
+  const PrefService* prefs = profile_->GetPrefs();
+
+  const base::ListValue* values =
+      prefs->GetList(prefs::kRecommendedNativePrinters);
+
+  recommended_printer_ids_.clear();
+  for (const auto& value : *values) {
+    std::string printer_json;
+    if (!value->GetAsString(&printer_json)) {
+      NOTREACHED();
+      continue;
+    }
+
+    std::unique_ptr<base::DictionaryValue> printer_dictionary =
+        base::DictionaryValue::From(base::JSONReader::Read(
+            printer_json, base::JSON_ALLOW_TRAILING_COMMAS));
+
+    if (!printer_dictionary) {
+      LOG(WARNING) << "Ignoring invalid printer.  Invalid JSON object: "
+                   << printer_json;
+      continue;
+    }
+
+    // Policy printers don't have id's but the ids only need to be locally
+    // unique so we'll hash the record.  This will not collide with the UUIDs
+    // generated for user entries.
+    std::string id = base::MD5String(printer_json);
+    printer_dictionary->SetString(printing::kPrinterId, id);
+
+    recommended_printer_ids_.push_back(id);
+    recommended_printers_[id] = std::move(printer_dictionary);
+  }
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager.h b/chrome/browser/chromeos/printing/printer_pref_manager.h
index 69cb923..222de0d 100644
--- a/chrome/browser/chromeos/printing/printer_pref_manager.h
+++ b/chrome/browser/chromeos/printing/printer_pref_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_PREF_MANAGER_H_
 #define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_PREF_MANAGER_H_
 
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -12,6 +13,7 @@
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/printing/printer_translator.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_change_registrar.h"
 
 class Profile;
 
@@ -24,6 +26,7 @@
 class PrinterPrefManager : public KeyedService {
  public:
   explicit PrinterPrefManager(Profile* profile);
+  ~PrinterPrefManager() override;
 
   // Register the printing preferences with the |registry|.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -31,6 +34,9 @@
   // Returns the printers that are saved in preferences.
   std::vector<std::unique_ptr<Printer>> GetPrinters() const;
 
+  // Returns printers from enterprise policy.
+  std::vector<std::unique_ptr<Printer>> GetRecommendedPrinters() const;
+
   // Returns the printer with id |printer_id|.
   std::unique_ptr<Printer> GetPrinter(const std::string& printer_id) const;
 
@@ -43,7 +49,17 @@
   bool RemovePrinter(const std::string& printer_id);
 
  private:
+  // Updates the in-memory recommended printer list.
+  void UpdateRecommendedPrinters();
+
   Profile* profile_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  // Contains the keys for all recommended printers in order so we can return
+  // the list of recommended printers in the order they were received.
+  std::vector<std::string> recommended_printer_ids_;
+  std::map<std::string, std::unique_ptr<base::DictionaryValue>>
+      recommended_printers_;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc b/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc
index 58dc1c4..4b02a745 100644
--- a/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc
@@ -9,7 +9,9 @@
 
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/printing/printer_pref_manager_factory.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,11 +22,23 @@
 const char kPrinterId[] = "UUID-UUID-UUID-PRINTER";
 const char kUri[] = "ipps://printer.chromium.org/ipp/print";
 
+const char kLexJson[] = R"json({
+        "display_name": "LexaPrint",
+        "description": "Laser on the test shelf",
+        "manufacturer": "LexaPrint, Inc.",
+        "model": "MS610de",
+        "uri": "ipp://192.168.1.5",
+        "ppd_resource": {
+          "effective_manufacturer": "LexaPrint",
+          "effective_model": "MS610de",
+        },
+      } )json";
+
 }  // namespace
 
 TEST(PrinterPrefManagerTest, AddPrinter) {
   content::TestBrowserThreadBundle thread_bundle;
-  std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>();
+  auto profile = base::MakeUnique<TestingProfile>();
   PrinterPrefManager* manager =
       PrinterPrefManagerFactory::GetForBrowserContext(profile.get());
 
@@ -37,7 +51,7 @@
 
 TEST(PrinterPrefManagerTest, UpdatePrinterAssignsId) {
   content::TestBrowserThreadBundle thread_bundle;
-  std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>();
+  auto profile = base::MakeUnique<TestingProfile>();
   PrinterPrefManager* manager =
       PrinterPrefManagerFactory::GetForBrowserContext(profile.get());
 
@@ -50,13 +64,12 @@
 
 TEST(PrinterPrefManagerTest, UpdatePrinter) {
   content::TestBrowserThreadBundle thread_bundle;
-  std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>();
+  auto profile = base::MakeUnique<TestingProfile>();
   PrinterPrefManager* manager =
       PrinterPrefManagerFactory::GetForBrowserContext(profile.get());
 
   manager->RegisterPrinter(base::MakeUnique<Printer>(kPrinterId));
-  std::unique_ptr<Printer> updated_printer =
-      base::MakeUnique<Printer>(kPrinterId);
+  auto updated_printer = base::MakeUnique<Printer>(kPrinterId);
   updated_printer->set_uri(kUri);
   manager->RegisterPrinter(std::move(updated_printer));
 
@@ -67,7 +80,7 @@
 
 TEST(PrinterPrefManagerTest, RemovePrinter) {
   content::TestBrowserThreadBundle thread_bundle;
-  std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>();
+  auto profile = base::MakeUnique<TestingProfile>();
   PrinterPrefManager* manager =
       PrinterPrefManagerFactory::GetForBrowserContext(profile.get());
 
@@ -83,4 +96,68 @@
   EXPECT_NE(kPrinterId, printers.at(1)->id());
 }
 
+TEST(PrinterPrefManagerTest, RecommendedPrinters) {
+  content::TestBrowserThreadBundle thread_bundle;
+
+  std::string first_printer =
+      R"json({
+      "display_name": "Color Laser",
+      "description": "The printer next to the water cooler.",
+      "manufacturer": "Printer Manufacturer",
+      "model":"Color Laser 2004",
+      "uri":"ipps://print-server.intranet.example.com:443/ipp/cl2k4",
+      "uuid":"1c395fdb-5d93-4904-b246-b2c046e79d12",
+      "ppd_resource":{
+          "effective_manufacturer": "MakesPrinters",
+          "effective_model":"ColorLaser2k4"
+       }
+      })json";
+
+  std::string second_printer = kLexJson;
+
+  auto value = base::MakeUnique<base::ListValue>();
+  value->AppendString(first_printer);
+  value->AppendString(second_printer);
+
+  TestingProfile profile;
+  sync_preferences::TestingPrefServiceSyncable* prefs =
+      profile.GetTestingPrefService();
+  // TestingPrefSyncableService assumes ownership of |value|.
+  prefs->SetManagedPref(prefs::kRecommendedNativePrinters, value.release());
+
+  PrinterPrefManager* manager =
+      PrinterPrefManagerFactory::GetForBrowserContext(&profile);
+
+  auto printers = manager->GetRecommendedPrinters();
+  ASSERT_EQ(2U, printers.size());
+  EXPECT_EQ("Color Laser", printers[0]->display_name());
+  EXPECT_EQ("ipp://192.168.1.5", printers[1]->uri());
+}
+
+TEST(PrinterPrefManagerTest, GetRecommendedPrinter) {
+  content::TestBrowserThreadBundle thread_bundle;
+
+  std::string printer = kLexJson;
+  auto value = base::MakeUnique<base::ListValue>();
+  value->AppendString(printer);
+
+  TestingProfile profile;
+  sync_preferences::TestingPrefServiceSyncable* prefs =
+      profile.GetTestingPrefService();
+  // TestingPrefSyncableService assumes ownership of |value|.
+  prefs->SetManagedPref(prefs::kRecommendedNativePrinters, value.release());
+
+  PrinterPrefManager* manager =
+      PrinterPrefManagerFactory::GetForBrowserContext(&profile);
+
+  auto printers = manager->GetRecommendedPrinters();
+
+  const Printer& from_list = *(printers.front());
+  std::unique_ptr<Printer> retrieved = manager->GetPrinter(from_list.id());
+
+  EXPECT_EQ(from_list.id(), retrieved->id());
+  EXPECT_EQ("LexaPrint", from_list.display_name());
+  EXPECT_EQ(Printer::Source::SRC_POLICY, from_list.source());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
index 7571460..1404616 100644
--- a/chrome/browser/mac/keystone_glue.mm
+++ b/chrome/browser/mac/keystone_glue.mm
@@ -73,7 +73,7 @@
     scoped_refptr<PerformBridge> op = new PerformBridge(target, sel, arg);
     base::PostTaskWithTraits(
         FROM_HERE, base::TaskTraits()
-                       .WithFileIO()
+                       .MayBlock()
                        .WithPriority(base::TaskPriority::BACKGROUND)
                        .WithShutdownBehavior(
                            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 8cfc600..2674edc 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/google/google_brand.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
+#include "chrome/browser/metrics/https_engagement_metrics_provider.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/metrics/sampling_metrics_provider.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
@@ -691,6 +692,10 @@
       std::unique_ptr<metrics::MetricsProvider>(
           new syncer::DeviceCountMetricsProvider(base::Bind(
               &browser_sync::ChromeSyncClient::GetDeviceInfoTrackers))));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new HttpsEngagementMetricsProvider()));
 }
 
 bool ChromeMetricsServiceClient::ShouldIncludeProfilerDataInLog() {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 666f0714..c028993 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -50,6 +50,7 @@
 #include "components/signin/core/common/signin_pref_names.h"
 #include "components/spellcheck/spellcheck_build_features.h"
 #include "components/ssl_config/ssl_config_prefs.h"
+#include "components/sync/base/pref_names.h"
 #include "components/sync/driver/sync_policy_handler.h"
 #include "components/translate/core/common/translate_pref_names.h"
 #include "components/variations/pref_names.h"
@@ -569,10 +570,11 @@
   { key::kBrowserAddPersonEnabled,
     prefs::kBrowserAddPersonEnabled,
     base::Value::Type::BOOLEAN },
+#endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+
   { key::kForceBrowserSignin,
     prefs::kForceBrowserSignin,
     base::Value::Type::BOOLEAN },
-#endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 
 #if defined(OS_WIN)
   { key::kWelcomePageOnOSUpgradeEnabled,
@@ -601,20 +603,33 @@
     prefs::kNetworkThrottlingEnabled,
     base::Value::Type::DICTIONARY },
 
-  { key::kAllowScreenLock, prefs::kAllowScreenLock,
+  { key::kAllowScreenLock,
+    prefs::kAllowScreenLock,
     base::Value::Type::BOOLEAN },
 
-  { key::kQuickUnlockModeWhitelist, prefs::kQuickUnlockModeWhitelist,
+  { key::kQuickUnlockModeWhitelist,
+    prefs::kQuickUnlockModeWhitelist,
     base::Value::Type::LIST },
-  { key::kQuickUnlockTimeout, prefs::kQuickUnlockTimeout,
+  { key::kQuickUnlockTimeout,
+    prefs::kQuickUnlockTimeout,
     base::Value::Type::INTEGER },
-  { key::kPinUnlockMinimumLength, prefs::kPinUnlockMinimumLength,
+  { key::kPinUnlockMinimumLength,
+    prefs::kPinUnlockMinimumLength,
     base::Value::Type::INTEGER },
-  { key::kPinUnlockMaximumLength, prefs::kPinUnlockMaximumLength,
+  { key::kPinUnlockMaximumLength,
+    prefs::kPinUnlockMaximumLength,
     base::Value::Type::INTEGER },
-  { key::kPinUnlockWeakPinsAllowed, prefs::kPinUnlockWeakPinsAllowed,
+  { key::kPinUnlockWeakPinsAllowed,
+    prefs::kPinUnlockWeakPinsAllowed,
     base::Value::Type::BOOLEAN },
 #endif
+
+  { key::kRoamingProfileSupportEnabled,
+    syncer::prefs::kEnableLocalSyncBackend,
+    base::Value::Type::BOOLEAN },
+  { key::kRoamingProfileLocation,
+    syncer::prefs::kLocalSyncBackendDir,
+    base::Value::Type::STRING },
 };
 
 class ForceSafeSearchPolicyHandler : public TypeCheckingPolicyHandler {
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
index 70c3131..7b292b3 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_remover.h"
+#include "chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_test_util.h"
 #include "chrome/browser/profiles/profile.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/test/base/in_process_browser_test.h"
@@ -92,6 +94,25 @@
   DISALLOW_COPY_AND_ASSIGN(InitializationObserver);
 };
 
+class BrowsingDataRemoverObserver : public BrowsingDataRemover::Observer {
+ public:
+  explicit BrowsingDataRemoverObserver(BrowsingDataRemover* remover)
+      : remover_(remover) {
+    remover_->AddObserver(this);
+  }
+  ~BrowsingDataRemoverObserver() override { remover_->RemoveObserver(this); }
+
+  void OnBrowsingDataRemoverDone() override { run_loop_.Quit(); }
+
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  BrowsingDataRemover* remover_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingDataRemoverObserver);
+};
+
 using PageRequestSummary = ResourcePrefetchPredictor::PageRequestSummary;
 using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary;
 
@@ -222,7 +243,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(
         switches::kSpeculativeResourcePrefetching,
-        switches::kSpeculativeResourcePrefetchingEnabled);
+        switches::kSpeculativeResourcePrefetchingEnabledExternal);
   }
 
   void SetUpOnMainThread() override {
@@ -332,7 +353,14 @@
   }
 
   void ClearCache() {
-    chrome::ClearCache(browser());
+    BrowsingDataRemover* remover =
+        BrowsingDataRemoverFactory::GetForBrowserContext(browser()->profile());
+    BrowsingDataRemoverObserver observer(remover);
+    remover->RemoveAndReply(BrowsingDataRemover::Unbounded(),
+                            BrowsingDataRemover::REMOVE_CACHE,
+                            BrowsingDataHelper::UNPROTECTED_WEB, &observer);
+    observer.Wait();
+
     for (auto& kv : resources_)
       kv.second.request.was_cached = false;
   }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a87a1a4..faf7b3ef 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -166,6 +166,7 @@
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/preferences/browser_prefs_android.h"
 #include "chrome/browser/ntp_snippets/download_suggestions_provider.h"
+#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
 #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h"
 #include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h"
 #else
@@ -590,6 +591,7 @@
 
 #if defined(OS_ANDROID)
   DownloadSuggestionsProvider::RegisterProfilePrefs(registry);
+  ntp_snippets::ClickBasedCategoryRanker::RegisterProfilePrefs(registry);
   ntp_snippets::PhysicalWebPageSuggestionsProvider::RegisterProfilePrefs(
       registry);
   ntp_snippets::RecentTabSuggestionsProvider::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 9956202f..97a48ef5 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -250,7 +250,6 @@
     case FINAL_STATUS_DEVTOOLS_ATTACHED:
     case FINAL_STATUS_PAGE_BEING_CAPTURED:
     case FINAL_STATUS_NAVIGATION_UNCOMMITTED:
-    case FINAL_STATUS_WOULD_HAVE_BEEN_USED:
     case FINAL_STATUS_NON_EMPTY_BROWSING_INSTANCE:
       return false;
     default:
@@ -1000,9 +999,6 @@
   PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
   EXPECT_EQ(1, GetPrerenderDomContentLoadedEventCountForLinkNumber(0));
   histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedPLTMatchedComplete", 0);
   histogram_tester().ExpectTotalCount(
       "Prerender.websame_PrerenderNotSwappedInPLT", 1);
 
@@ -1013,10 +1009,6 @@
   channel_close_watcher.WaitForChannelClose();
 
   histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLTMatched",
-                                      1);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PerceivedPLTMatchedComplete", 1);
 
   ASSERT_TRUE(IsEmptyPrerenderLinkManager());
 }
@@ -1026,18 +1018,11 @@
   PrerenderTestURL(GetCrossDomainTestUrl("prerender/prerender_page.html"),
                    FINAL_STATUS_USED, 1);
   histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedPLTMatchedComplete", 0);
   histogram_tester().ExpectTotalCount(
       "Prerender.webcross_PrerenderNotSwappedInPLT", 1);
 
   NavigateToDestURL();
   histogram_tester().ExpectTotalCount("Prerender.webcross_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.webcross_PerceivedPLTMatched",
-                                      1);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.webcross_PerceivedPLTMatchedComplete", 1);
 }
 
 // Checks that pending prerenders launch and receive proper event treatment.
@@ -1441,9 +1426,6 @@
   EXPECT_EQ(1, prerender->number_of_loads());
 
   histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedPLTMatchedComplete", 0);
   // Although there is a client redirect, it is dropped from histograms because
   // it is a Google URL. The target page itself does not load until after the
   // swap.
@@ -1468,9 +1450,6 @@
   histogram_tester().ExpectTotalCount("Prerender.gws_PrerenderNotSwappedInPLT",
                                       0);
   histogram_tester().ExpectTotalCount("Prerender.gws_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.gws_PerceivedPLTMatched", 1);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.gws_PerceivedPLTMatchedComplete", 1);
 
   // The client redirect does /not/ count as a miss because it's a Google URL.
   histogram_tester().ExpectTotalCount("Prerender.PerceivedPLTFirstAfterMiss",
@@ -2910,9 +2889,6 @@
   EXPECT_TRUE(DidPrerenderPass(prerender->contents()->prerender_contents()));
   EXPECT_EQ(0, prerender->number_of_loads());
   histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedPLTMatchedComplete", 0);
   histogram_tester().ExpectTotalCount(
       "Prerender.websame_PrerenderNotSwappedInPLT", 0);
 
@@ -2933,10 +2909,6 @@
   histogram_tester().ExpectTotalCount(
       "Prerender.websame_PrerenderNotSwappedInPLT", 0);
   histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLTMatched",
-                                      1);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PerceivedPLTMatchedComplete", 1);
 }
 
 // Checks that a deferred redirect to an image is not loaded until the
@@ -3085,9 +3057,6 @@
   GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
   ui_test_utils::NavigateToURL(current_browser(), url);
   histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1);
-  histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedPLTMatchedComplete", 0);
 }
 
 // Checks that a prerender which calls window.close() on itself is aborted.
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index f7c11cf..b1e40f90 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -367,8 +367,7 @@
 
   prerender_manager_->RecordFinalStatus(origin(), final_status());
 
-  bool used = final_status() == FINAL_STATUS_USED ||
-              final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED;
+  bool used = final_status() == FINAL_STATUS_USED;
   prerender_manager_->RecordNetworkBytes(origin(), used, network_bytes_);
 
   // Broadcast the removal of aliases.
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h
index 9de710b..718eeac 100644
--- a/chrome/browser/prerender/prerender_final_status.h
+++ b/chrome/browser/prerender/prerender_final_status.h
@@ -53,7 +53,7 @@
   // Obsolete: FINAL_STATUS_MATCH_COMPLETE_DUMMY = 38,
   FINAL_STATUS_DUPLICATE = 39,
   FINAL_STATUS_OPEN_URL = 40,
-  FINAL_STATUS_WOULD_HAVE_BEEN_USED = 41,
+  // Obsolete: FINAL_STATUS_WOULD_HAVE_BEEN_USED = 41,
   FINAL_STATUS_REGISTER_PROTOCOL_HANDLER = 42,
   FINAL_STATUS_CREATING_AUDIO_STREAM = 43,
   FINAL_STATUS_PAGE_BEING_CAPTURED = 44,
diff --git a/chrome/browser/prerender/prerender_histograms.cc b/chrome/browser/prerender/prerender_histograms.cc
index 4e97663..1e892bfd 100644
--- a/chrome/browser/prerender/prerender_histograms.cc
+++ b/chrome/browser/prerender/prerender_histograms.cc
@@ -241,10 +241,6 @@
 // (all prefixed PerceivedPLT)
 // PerceivedPLT -- Perceived Pageloadtimes (PPLT) for all pages in the group.
 // ...Windowed -- PPLT for pages in the 30s after a prerender is created.
-// ...Matched -- A prerendered page that was swapped in.  In the NoUse
-// and Control group cases, while nothing ever gets swapped in, we do keep
-// track of what would be prerendered and would be swapped in -- and those
-// cases are what is classified as Match for these groups.
 // ...FirstAfterMiss -- First page to finish loading after a prerender, which
 // is different from the page that was prerendered.
 // ...FirstAfterMissNonOverlapping -- Same as FirstAfterMiss, but only
@@ -270,15 +266,10 @@
   if (within_window)
     RECORD_PLT("PerceivedPLTWindowed", perceived_page_load_time);
   if (navigation_type != NAVIGATION_TYPE_NORMAL) {
-    DCHECK(navigation_type == NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED ||
-           navigation_type == NAVIGATION_TYPE_PRERENDERED);
-    RECORD_PLT("PerceivedPLTMatchedComplete", perceived_page_load_time);
-    if (navigation_type == NAVIGATION_TYPE_PRERENDERED)
-      RECORD_PLT("PerceivedPLTMatched", perceived_page_load_time);
+    DCHECK(navigation_type == NAVIGATION_TYPE_PRERENDERED);
     seen_any_pageload_ = true;
     seen_pageload_started_after_prerender_ = true;
   } else if (within_window) {
-    RECORD_PLT("PerceivedPLTWindowNotMatched", perceived_page_load_time);
     if (!is_google_url) {
       bool recorded_any = false;
       bool recorded_non_overlapping = false;
diff --git a/chrome/browser/prerender/prerender_histograms.h b/chrome/browser/prerender/prerender_histograms.h
index 7fdc1875..ad2a9a8 100644
--- a/chrome/browser/prerender/prerender_histograms.h
+++ b/chrome/browser/prerender/prerender_histograms.h
@@ -26,10 +26,6 @@
   NAVIGATION_TYPE_NORMAL,
   // A completed navigation or swap that began as a prerender.
   NAVIGATION_TYPE_PRERENDERED,
-  // A normal completed navigation in the control group or with a control
-  // prerender that would have been prerendered.
-  NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED,
-  NAVIGATION_TYPE_MAX,
 };
 
 // PrerenderHistograms is responsible for recording all prerender specific
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index f908885f..7102bb8 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -412,15 +412,7 @@
     return nullptr;
   }
 
-  // If we are just in the control group (which can be detected by noticing
-  // that prerendering hasn't even started yet), record that |web_contents| now
-  // would be showing a prerendered contents, but otherwise, don't do anything.
-  if (!prerender_data->contents()->prerendering_has_started()) {
-    target_tab_helper->WouldHavePrerenderedNextLoad(
-        prerender_data->contents()->origin());
-    prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED);
-    return nullptr;
-  }
+  DCHECK(prerender_data->contents()->prerendering_has_started());
 
   // Don't use prerendered pages if debugger is attached to the tab.
   // See http://crbug.com/98541
diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc
index a7450cac..52a669b 100644
--- a/chrome/browser/prerender/prerender_tab_helper.cc
+++ b/chrome/browser/prerender/prerender_tab_helper.cc
@@ -24,8 +24,6 @@
 PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       origin_(ORIGIN_NONE),
-      next_load_is_control_prerender_(false),
-      next_load_origin_(ORIGIN_NONE),
       weak_factory_(this) {
   // Determine if this is a prerender.
   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
@@ -112,14 +110,6 @@
   pplt_load_start_ = GetTimeTicksFromPrerenderManager();
   actual_load_start_ = base::TimeTicks();
 
-  if (next_load_is_control_prerender_) {
-    DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_);
-    navigation_type_ = NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED;
-    origin_ = next_load_origin_;
-    next_load_is_control_prerender_ = false;
-    next_load_origin_ = ORIGIN_NONE;
-  }
-
   MainFrameUrlDidChange(validated_url);
 }
 
@@ -167,11 +157,6 @@
   }
 }
 
-void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) {
-  next_load_is_control_prerender_ = true;
-  next_load_origin_ = origin;
-}
-
 void PrerenderTabHelper::RecordPerceivedPageLoadTime(
     base::TimeDelta perceived_page_load_time,
     double fraction_plt_elapsed_at_swap_in) {
@@ -180,10 +165,6 @@
   if (!prerender_manager)
     return;
 
-  // Note: it is possible for |next_load_is_control_prerender_| to be true at
-  // this point. This does not affect the classification of the current load,
-  // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
-  // navigation interrupts and aborts another navigation.)
   prerender_manager->RecordPerceivedPageLoadTime(
       origin_, navigation_type_, perceived_page_load_time,
       fraction_plt_elapsed_at_swap_in, url_);
diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h
index 19fb16e3..bc5db07 100644
--- a/chrome/browser/prerender/prerender_tab_helper.h
+++ b/chrome/browser/prerender/prerender_tab_helper.h
@@ -48,9 +48,6 @@
   // Called when this prerendered WebContents has just been swapped in.
   void PrerenderSwappedIn();
 
-  // Called when a control prerender is resolved. Applies to the next load.
-  void WouldHavePrerenderedNextLoad(Origin origin);
-
  private:
   explicit PrerenderTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<PrerenderTabHelper>;
@@ -79,13 +76,6 @@
   // relevant prerender. Otherwise, ORIGIN_NONE.
   Origin origin_;
 
-  // True if the next load will be associated with a control prerender. This
-  // extra state is needed because control prerenders are resolved before the
-  // actual load begins. |next_load_origin_| gives the origin of the control
-  // prerender.
-  bool next_load_is_control_prerender_;
-  Origin next_load_origin_;
-
   // System time at which the current load was started for the purpose of
   // the perceived page load time (PPLT). If null, there is no current
   // load.
diff --git a/chrome/browser/resources/.clang-format b/chrome/browser/resources/.clang-format
new file mode 100644
index 0000000..4d25b73
--- /dev/null
+++ b/chrome/browser/resources/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Chromium
+
+# Renaming quotes in <include> and <if> break things.
+# For normal JS code, please prefer ' to ".
+JavaScriptQuotes: Leave
+
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+SpacesInContainerLiterals: false
+SpacesInSquareBrackets: false
diff --git a/chrome/browser/resources/bluetooth_internals/adapter_broker.js b/chrome/browser/resources/bluetooth_internals/adapter_broker.js
index c1cf8fa..13a8eee 100644
--- a/chrome/browser/resources/bluetooth_internals/adapter_broker.js
+++ b/chrome/browser/resources/bluetooth_internals/adapter_broker.js
@@ -15,7 +15,7 @@
    * handles and back when necessary.
    * @constructor
    * @extends {cr.EventTarget}
-   * @param {!interfaces.BluetoothAdapter.Adapter.proxyClass} adapter
+   * @param {!interfaces.BluetoothAdapter.AdapterPtr} adapter
    */
   var AdapterBroker = function(adapter) {
     this.adapter_ = adapter;
@@ -29,7 +29,7 @@
     /**
      * Creates a GATT connection to the device with |address|.
      * @param {string} address
-     * @return {!Promise<!interfaces.BluetoothDevice.Device.proxyClass>}
+     * @return {!Promise<!interfaces.BluetoothDevice.DevicePtr>}
      */
     connectToDevice: function(address) {
       return this.adapter_.connectToDevice(address).then(function(response) {
@@ -45,9 +45,7 @@
           throw new Error(errorString);
         }
 
-        return interfaces.Connection.bindHandleToProxy(
-            response.device.ptr.passInterface().handle,
-            interfaces.BluetoothDevice.Device);
+        return response.device;
       });
     },
 
@@ -145,14 +143,9 @@
     if (adapterBroker) return Promise.resolve(adapterBroker);
 
     return interfaces.setupInterfaces().then(function(adapter) {
-      // Hook up the instance properties.
-      AdapterClient.prototype.__proto__ =
-          interfaces.BluetoothAdapter.AdapterClient.stubClass.prototype;
-
-      var adapterFactory = interfaces.Connection.bindHandleToProxy(
+      var adapterFactory = new interfaces.BluetoothAdapter.AdapterFactoryPtr(
           interfaces.FrameInterfaces.getInterface(
-              interfaces.BluetoothAdapter.AdapterFactory.name),
-          interfaces.BluetoothAdapter.AdapterFactory);
+              interfaces.BluetoothAdapter.AdapterFactory.name));
 
       // Get an Adapter service.
       return adapterFactory.getAdapter();
@@ -161,11 +154,7 @@
         throw new Error('Bluetooth Not Supported on this platform.');
       }
 
-      var adapter = interfaces.Connection.bindHandleToProxy(
-          response.adapter.ptr.passInterface().handle,
-          interfaces.BluetoothAdapter.Adapter);
-
-      adapterBroker = new AdapterBroker(adapter);
+      adapterBroker = new AdapterBroker(response.adapter);
       return adapterBroker;
     });
   }
diff --git a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
index a87c537..fe2167d 100644
--- a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
+++ b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
@@ -41,7 +41,7 @@
     },
   };
 
-  /** @type {!Map<string, !interfaces.BluetoothDevice.Device.proxyClass>} */
+  /** @type {!Map<string, !interfaces.BluetoothDevice.DevicePtr>} */
   var deviceAddressToProxy = new Map();
 
   /** @type {!device_collection.DeviceCollection} */
diff --git a/chrome/browser/resources/bluetooth_internals/interfaces.js b/chrome/browser/resources/bluetooth_internals/interfaces.js
index 0bbe9cb..20eb2b8b 100644
--- a/chrome/browser/resources/bluetooth_internals/interfaces.js
+++ b/chrome/browser/resources/bluetooth_internals/interfaces.js
@@ -26,13 +26,11 @@
         'device/bluetooth/public/interfaces/adapter.mojom',
         'device/bluetooth/public/interfaces/device.mojom',
         'mojo/public/js/bindings',
-        'mojo/public/js/connection',
       ]).then(function([frameInterfaces, bluetoothAdapter, bluetoothDevice,
-          bindings, connection]) {
+          bindings]) {
         interfaces.BluetoothAdapter = bluetoothAdapter;
         interfaces.BluetoothDevice = bluetoothDevice;
         interfaces.Bindings = bindings;
-        interfaces.Connection = connection;
         interfaces.FrameInterfaces = frameInterfaces;
       });
     });
diff --git a/chrome/browser/resources/engagement/site_engagement.js b/chrome/browser/resources/engagement/site_engagement.js
index bd19f11..3557fd0 100644
--- a/chrome/browser/resources/engagement/site_engagement.js
+++ b/chrome/browser/resources/engagement/site_engagement.js
@@ -5,15 +5,13 @@
 'use strict';
 
 define('main', [
-    'mojo/public/js/connection',
     'chrome/browser/ui/webui/engagement/site_engagement.mojom',
     'content/public/renderer/frame_interfaces',
-], function(connection, siteEngagementMojom, frameInterfaces) {
+], function(siteEngagementMojom, frameInterfaces) {
   return function() {
-    var uiHandler = connection.bindHandleToProxy(
+    var uiHandler = new siteEngagementMojom.SiteEngagementUIHandlerPtr(
         frameInterfaces.getInterface(
-            siteEngagementMojom.SiteEngagementUIHandler.name),
-        siteEngagementMojom.SiteEngagementUIHandler);
+            siteEngagementMojom.SiteEngagementUIHandler.name));
 
     var engagementTableBody = $('engagement-table-body');
     var updateInterval = null;
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index 11c4a993..2570deb 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -416,18 +416,15 @@
   function initializeProxies() {
     return importModules([
       'mojo/public/js/bindings',
-      'mojo/public/js/connection',
       'chrome/browser/ui/webui/omnibox/omnibox.mojom',
       'content/public/renderer/frame_interfaces',
     ]).then(function(modules) {
       var bindings = modules[0];
-      var connection = modules[1];
-      var mojom = modules[2];
-      var frameInterfaces = modules[3];
+      var mojom = modules[1];
+      var frameInterfaces = modules[2];
 
-      browserProxy = connection.bindHandleToProxy(
-          frameInterfaces.getInterface(mojom.OmniboxPageHandler.name),
-          mojom.OmniboxPageHandler);
+      browserProxy = new mojom.OmniboxPageHandlerPtr(
+          frameInterfaces.getInterface(mojom.OmniboxPageHandler.name));
 
       /** @constructor */
       var OmniboxPageImpl = function() {
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index 835f8d5..73d37ff 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -1226,6 +1226,9 @@
       $('sync-action-link').onclick = function(event) {
         switch (syncData.statusAction) {
           case 'reauthenticate':
+            SyncSetupOverlay.startSignIn(false /* creatingSupervisedUser */);
+            break;
+          case 'signOutAndSignIn':
 <if expr="chromeos">
             // On Chrome OS, sign out the user and sign in again to get fresh
             // credentials on auth errors.
diff --git a/chrome/browser/resources/options/chromeos/stylus_overlay.html b/chrome/browser/resources/options/chromeos/stylus_overlay.html
index dff83f31..9e0cc025 100644
--- a/chrome/browser/resources/options/chromeos/stylus_overlay.html
+++ b/chrome/browser/resources/options/chromeos/stylus_overlay.html
@@ -19,6 +19,13 @@
             <span i18n-content="stylusAutoOpenStylusTools"></span>
           </label>
         </div>
+        <div class="controlled-setting-with-label">
+          <label>
+            <span class="option-name" i18n-content="stylusNoteTakingApp"></span>
+            <select id="stylus-note-taking-app-select" class="option-value">
+            </select>
+          </label>
+        </div>
       </div>
     </div>
 
diff --git a/chrome/browser/resources/options/chromeos/stylus_overlay.js b/chrome/browser/resources/options/chromeos/stylus_overlay.js
index 36b4160..9462dc2 100644
--- a/chrome/browser/resources/options/chromeos/stylus_overlay.js
+++ b/chrome/browser/resources/options/chromeos/stylus_overlay.js
@@ -21,18 +21,129 @@
   StylusOverlay.prototype = {
     __proto__: options.SettingsDialog.prototype,
 
+    /**
+     * True if the "enable stylus tools" pref is set to true.
+     * @type {boolean}
+     */
+    stylusToolsEnabled_: true,
+
+    /**
+     * True if the last call to updateNoteTakingApps_() passed at least one
+     * available note-taking app and indicated that we are not waiting for
+     * Android to start.
+     * @type {boolean}
+     */
+    noteTakingAppsAvailable_: false,
+
+    /**
+     * Note-taking app ID selected by the user, or null if the user didn't
+     * change the selection.
+     * @type {?string}
+     */
+    selectedNoteTakingAppId_: null,
+
     /** @override */
     initializePage: function() {
       options.SettingsDialog.prototype.initializePage.call(this);
 
-      // Disable auto-launch pref when enable stylus tools pref is false.
+      // Disable some elements when enable stylus tools pref is false.
       Preferences.getInstance().addEventListener('settings.enable_stylus_tools',
           function(e) {
+            this.stylusToolsEnabled_ = e.value.value;
             $('launch-palette-on-eject-input').disabled = !e.value.value;
-          });
+            this.updateNoteTakingAppsSelectDisabled_();
+          }.bind(this));
+
+      $('stylus-note-taking-app-select')
+          .addEventListener(
+              'change', this.handleNoteTakingAppSelected_.bind(this));
+    },
+
+    /** @override */
+    handleConfirm: function() {
+      options.SettingsDialog.prototype.handleConfirm.call(this);
+
+      if (this.selectedNoteTakingAppId_) {
+        chrome.send(
+            'setPreferredNoteTakingApp', [this.selectedNoteTakingAppId_]);
+        this.selectedNoteTakingAppId_ = null;
+      }
+    },
+
+    /** @override */
+    handleCancel: function() {
+      options.SettingsDialog.prototype.handleCancel.call(this);
+
+      var selectOptions = $('stylus-note-taking-app-select').children;
+      for (var i = 0; i < selectOptions.length; i++) {
+        selectOptions[i].selected = selectOptions[i].defaultSelected;
+      }
+    },
+
+    /**
+     * Updates the list of available note-taking apps. Called from C++.
+     * @param {Array<{name: string, id: string, preferred: boolean}>}
+     *     apps Array of app info structs containing:
+     *       name       App name to display to user.
+     *       id         Opaque app ID.
+     *       preferred  Whether this is the preferred app.
+     * @param {boolean} waitingForAndroid True if Android is starting.
+     * @private
+     */
+    updateNoteTakingApps_: function(apps, waitingForAndroid) {
+      var element = $('stylus-note-taking-app-select');
+
+      // Clear any existing options and make sure we don't save an old
+      // selection when "OK" is clicked.
+      element.textContent = '';
+      this.noteTakingAppsAvailable_ = false;
+      this.selectedNoteTakingAppId_ = null;
+
+      // Disable the menu and use it to display an informative message if
+      // Android is starting or no note-taking apps are installed.
+      if (waitingForAndroid) {
+        element.appendChild(new Option(
+            loadTimeData.getString('stylusNoteTakingAppWaitingForAndroid'), '',
+            false, false));
+      } else if (apps.length == 0) {
+        element.appendChild(new Option(
+            loadTimeData.getString('stylusNoteTakingAppNoneAvailable'), '',
+            false, false));
+      } else {
+        for (var i = 0; i < apps.length; i++) {
+          var app = apps[i];
+          element.appendChild(
+              new Option(app.name, app.id, app.preferred, app.preferred));
+        }
+        this.noteTakingAppsAvailable_ = true;
+      }
+
+      this.updateNoteTakingAppsSelectDisabled_();
+    },
+
+    /**
+     * Updates the disabled state of the note-taking app select element.
+     * @private
+     */
+    updateNoteTakingAppsSelectDisabled_: function() {
+      $('stylus-note-taking-app-select').disabled =
+          !this.stylusToolsEnabled_ || !this.noteTakingAppsAvailable_;
+    },
+
+    /**
+     * Records the user's selection of a note-taking app so it can be saved when
+     * the "OK" button is clicked.
+     * @private
+     */
+    handleNoteTakingAppSelected_: function() {
+      this.selectedNoteTakingAppId_ = $('stylus-note-taking-app-select').value;
     },
   };
 
+  cr.makePublic(StylusOverlay, [
+    'updateNoteTakingApps',
+  ]);
+
   // Export
   return {
     StylusOverlay: StylusOverlay
diff --git a/chrome/browser/resources/pdf/gesture_detector.js b/chrome/browser/resources/pdf/gesture_detector.js
index a64729e..fde4650d 100644
--- a/chrome/browser/resources/pdf/gesture_detector.js
+++ b/chrome/browser/resources/pdf/gesture_detector.js
@@ -16,10 +16,14 @@
   constructor(element) {
     this.element_ = element;
 
-    this.element_.addEventListener('touchstart', this.onTouchStart_.bind(this));
-    this.element_.addEventListener('touchmove', this.onTouch_.bind(this));
-    this.element_.addEventListener('touchend', this.onTouch_.bind(this));
-    this.element_.addEventListener('touchcancel', this.onTouch_.bind(this));
+    this.element_.addEventListener(
+        'touchstart', this.onTouchStart_.bind(this), { passive: false });
+    this.element_.addEventListener(
+        'touchmove', this.onTouch_.bind(this), { passive: true });
+    this.element_.addEventListener(
+        'touchend', this.onTouch_.bind(this), { passive: true });
+    this.element_.addEventListener(
+        'touchcancel', this.onTouch_.bind(this), { passive: true });
 
     this.pinchStartEvent_ = null;
     this.lastEvent_ = null;
diff --git a/chrome/browser/resources/plugins.js b/chrome/browser/resources/plugins.js
index bffd6bb..f57f133 100644
--- a/chrome/browser/resources/plugins.js
+++ b/chrome/browser/resources/plugins.js
@@ -228,18 +228,15 @@
 function initializeProxies() {
   return importModules([
     'mojo/public/js/bindings',
-    'mojo/public/js/connection',
     'chrome/browser/ui/webui/plugins/plugins.mojom',
     'content/public/renderer/frame_interfaces',
   ]).then(function(modules) {
     var bindings = modules[0];
-    var connection = modules[1];
-    var pluginsMojom = modules[2];
-    var frameInterfaces = modules[3];
+    var pluginsMojom = modules[1];
+    var frameInterfaces = modules[2];
 
-    browserProxy = connection.bindHandleToProxy(
-        frameInterfaces.getInterface(pluginsMojom.PluginsPageHandler.name),
-        pluginsMojom.PluginsPageHandler);
+    browserProxy = new pluginsMojom.PluginsPageHandlerPtr(
+        frameInterfaces.getInterface(pluginsMojom.PluginsPageHandler.name));
 
     /** @constructor */
     var PluginsPageImpl = function() {
diff --git a/chrome/browser/resources/settings/people_page/import_data_dialog.js b/chrome/browser/resources/settings/people_page/import_data_dialog.js
index e035c28..058a27f 100644
--- a/chrome/browser/resources/settings/people_page/import_data_dialog.js
+++ b/chrome/browser/resources/settings/people_page/import_data_dialog.js
@@ -58,6 +58,10 @@
         function(data) {
           this.browserProfiles_ = data;
           this.selected_ = this.browserProfiles_[0];
+
+          // Show the dialog only after the browser profiles data is populated
+          // to avoid UI flicker.
+          this.$.dialog.showModal();
         }.bind(this));
 
     this.addWebUIListener(
@@ -68,8 +72,6 @@
           if (this.hasImportStatus_(settings.ImportDataStatus.FAILED))
             this.closeDialog_();
         }.bind(this));
-
-    this.$.dialog.showModal();
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index 1a18e028..5698401 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -338,6 +338,9 @@
 
     switch (this.syncStatus.statusAction) {
       case settings.StatusAction.REAUTHENTICATE:
+        this.syncBrowserProxy_.startSignIn();
+        break;
+      case settings.StatusAction.SIGNOUT_AND_SIGNIN:
 <if expr="chromeos">
         this.syncBrowserProxy_.attemptUserExit();
 </if>
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index f71f2ad0..be5c2b6 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -35,10 +35,11 @@
  * @enum {string}
  */
 settings.StatusAction = {
-  NO_ACTION: 'noAction',                 // No action to take.
-  REAUTHENTICATE: 'reauthenticate',      // User needs to reauthenticate.
-  UPGRADE_CLIENT: 'upgradeClient',       // User needs to upgrade the client.
-  ENTER_PASSPHRASE: 'enterPassphrase',   // User needs to enter passphrase.
+  NO_ACTION: 'noAction',                  // No action to take.
+  REAUTHENTICATE: 'reauthenticate',       // User needs to reauthenticate.
+  SIGNOUT_AND_SIGNIN: 'signOutAndSignIn', // User needs to sign out and sign in.
+  UPGRADE_CLIENT: 'upgradeClient',        // User needs to upgrade the client.
+  ENTER_PASSPHRASE: 'enterPassphrase',    // User needs to enter passphrase.
 };
 
 /**
@@ -180,10 +181,7 @@
 <if expr="not chromeos">
     /** @override */
     startSignIn: function() {
-      // TODO(tommycli): Currently this is always false, but this will become
-      // a parameter once supervised users are implemented in MD Settings.
-      var creatingSupervisedUser = false;
-      chrome.send('SyncSetupStartSignIn', [creatingSupervisedUser]);
+      chrome.send('SyncSetupStartSignIn');
     },
 
     /** @override */
diff --git a/chrome/browser/resources/usb_internals/usb_internals.js b/chrome/browser/resources/usb_internals/usb_internals.js
index a1c1a28..bed9e59 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.js
+++ b/chrome/browser/resources/usb_internals/usb_internals.js
@@ -61,17 +61,14 @@
 
   function initializeProxies() {
     return importModules([
-      'mojo/public/js/connection',
       'chrome/browser/ui/webui/usb_internals/usb_internals.mojom',
       'content/public/renderer/frame_interfaces',
     ]).then(function(modules) {
-      let connection = modules[0];
-      let mojom = modules[1];
-      let frameInterfaces = modules[2];
+      let mojom = modules[0];
+      let frameInterfaces = modules[1];
 
-      pageHandler = connection.bindHandleToProxy(
-          frameInterfaces.getInterface(mojom.UsbInternalsPageHandler.name),
-          mojom.UsbInternalsPageHandler);
+      pageHandler = new mojom.UsbInternalsPageHandlerPtr(
+          frameInterfaces.getInterface(mojom.UsbInternalsPageHandler.name));
     });
   }
 
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
index de3332f3..740af35 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -394,11 +394,8 @@
       false /* expect cert status error */);
 }
 
-IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1Broken) {
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1Certificate) {
   ASSERT_TRUE(https_server_.Start());
-  // The test server uses a long-lived cert by default, so a SHA1
-  // signature in it will register as a "broken" condition rather than
-  // "warning".
   SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT,
                                       net::OK);
 
@@ -406,7 +403,7 @@
                                https_server_.GetURL("/ssl/google.html"));
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR,
+      security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR,
       security_state::CONTENT_STATUS_NONE, false,
       false /* expect cert status error */);
 }
@@ -591,12 +588,9 @@
 }
 
 // Same as the test above but with a long-lived SHA1 cert.
-IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentWithBrokenSHA1) {
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentWithSHA1Cert) {
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
-  // The test server uses a long-lived cert by default, so a SHA1
-  // signature in it will register as a "broken" condition rather than
-  // "warning".
   SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT,
                                       net::OK);
 
@@ -615,7 +609,7 @@
                                https_server_.GetURL(replacement_path));
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR,
+      security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR,
       security_state::CONTENT_STATUS_DISPLAYED, false,
       false /* expect cert status error */);
 
@@ -627,7 +621,7 @@
                                https_server_.GetURL(replacement_path));
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR,
+      security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR,
       security_state::CONTENT_STATUS_NONE, false,
       false /* expect cert status error */);
   // Load the insecure image.
@@ -638,7 +632,7 @@
   EXPECT_TRUE(js_result);
   CheckSecurityInfoForSecure(
       browser()->tab_strip_model()->GetActiveWebContents(),
-      security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR,
+      security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR,
       security_state::CONTENT_STATUS_DISPLAYED, false,
       false /* expect cert status error */);
 
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h
index ca78bd9..9f976db 100644
--- a/chrome/browser/sync/sync_ui_util.h
+++ b/chrome/browser/sync/sync_ui_util.h
@@ -29,10 +29,11 @@
 
 // The action associated with the sync status.
 enum ActionType {
-  NO_ACTION,         // No action to take.
-  REAUTHENTICATE,    // User needs to reauthenticate.
-  UPGRADE_CLIENT,    // User needs to upgrade the client.
-  ENTER_PASSPHRASE,  // User needs to enter their passphrase.
+  NO_ACTION,           // No action to take.
+  REAUTHENTICATE,      // User needs to reauthenticate.
+  SIGNOUT_AND_SIGNIN,  // User needs to sign out and sign in.
+  UPGRADE_CLIENT,      // User needs to upgrade the client.
+  ENTER_PASSPHRASE,    // User needs to enter their passphrase.
 };
 
 enum StatusLabelStyle {
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
index 2464dfa..844ad12 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
@@ -53,9 +53,12 @@
 
 namespace {
 
-const char kAAccountIdString[] = "{\"email\":\"A\",\"gaia_id\":\"\"}";
-const char kBAccountIdString[] = "{\"email\":\"B\",\"gaia_id\":\"\"}";
-const char kArrowBAccountIdString[] = "->{\"email\":\"B\",\"gaia_id\":\"\"}";
+const char kAAccountIdString[] =
+    "{\"account_type\":\"unknown\",\"email\":\"A\"}";
+const char kBAccountIdString[] =
+    "{\"account_type\":\"unknown\",\"email\":\"B\"}";
+const char kArrowBAccountIdString[] =
+    "->{\"account_type\":\"unknown\",\"email\":\"B\"}";
 
 // TOOD(beng): This implementation seems only superficially different to the
 //             production impl. Evaluate whether or not we can just use that
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm
index 8b72196..19d1190 100644
--- a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm
@@ -38,6 +38,7 @@
   [label setDrawsBackground:NO];
   [label setBezeled:NO];
   [label setStringValue:title];
+  [label setAlignment:NSNaturalTextAlignment];
   [label setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
   [label sizeToFit];
   [view addSubview:label.get()];
diff --git a/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm b/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm
index 9727315c..fe02247 100644
--- a/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm
@@ -13,6 +13,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "skia/ext/skia_utils_mac.h"
+#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -64,6 +65,10 @@
   NSTextField* titleLabel =
       [self addTitleLabel:base::SysUTF16ToNSString(self.model->title())
                    toView:view];
+  // Title should occupy the whole width to that it's aligned properly for RTL.
+  [titleLabel setFrameSize:NSMakeSize(kDesiredBubbleWidth - 2 * kFramePadding,
+                                      0)];
+  [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel];
 
   // Text.
   confirmationText_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
diff --git a/chrome/browser/ui/cocoa/passwords/credential_item_button.mm b/chrome/browser/ui/cocoa/passwords/credential_item_button.mm
index 27c200d..24d401e 100644
--- a/chrome/browser/ui/cocoa/passwords/credential_item_button.mm
+++ b/chrome/browser/ui/cocoa/passwords/credential_item_button.mm
@@ -4,6 +4,7 @@
 
 #import "chrome/browser/ui/cocoa/passwords/credential_item_button.h"
 
+#include "base/i18n/rtl.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/ui/cocoa/passwords/passwords_bubble_utils.h"
@@ -50,7 +51,8 @@
   // in -drawImage, so it must be added when drawing the title.
   NSRect marginRect;
   NSDivideRect(frame, &marginRect, &frame, marginSpacing_, NSMinXEdge);
-  NSDivideRect(frame, &marginRect, &frame, imageTitleSpacing_, NSMinXEdge);
+  NSDivideRect(frame, &marginRect, &frame, imageTitleSpacing_,
+               base::i18n::IsRTL() ? NSMaxXEdge : NSMinXEdge);
   NSDivideRect(frame, &marginRect, &frame, marginSpacing_, NSMaxXEdge);
 
   return [super drawTitle:title withFrame:frame inView:controlView];
@@ -59,7 +61,10 @@
 - (void)drawImage:(NSImage*)image
         withFrame:(NSRect)frame
            inView:(NSView*)controlView {
-  frame.origin.x = marginSpacing_;
+  if (base::i18n::IsRTL())
+    frame.origin.x -= marginSpacing_;
+  else
+    frame.origin.x += marginSpacing_;
   gfx::ScopedNSGraphicsContextSaveGState scopedGState;
   NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame];
   [path addClip];
@@ -116,8 +121,7 @@
                       .GetPrimaryFont()
                       .GetNativeFont()];
     [self setButtonType:NSMomentaryLightButton];
-    [self setImagePosition:NSImageLeft];
-    [self setAlignment:NSLeftTextAlignment];
+    [self setImagePosition:base::i18n::IsRTL() ? NSImageRight : NSImageLeft];
   }
   return self;
 }
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm
index 5e25aac3..6436e43 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm
@@ -84,10 +84,8 @@
   [view addSubview:contentView];
 
   // Wrap the title if necessary to match the width of the content view.
-  if (NSWidth([titleLabel frame]) > NSWidth([contentView frame])) {
-    [titleLabel setFrameSize:NSMakeSize(NSWidth([contentView frame]), 0)];
-    [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel];
-  }
+  [titleLabel setFrameSize:NSMakeSize(NSWidth([contentView frame]), 0)];
+  [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel];
 
   // Done button.
   doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 595fdf7..7879306 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -262,34 +262,36 @@
                   static_cast<int>(output_string.size()));
 }
 
-// Shows the User Manager on startup if the last used profile must sign in or
-// if the last used profile was the guest or system profile.
-// Returns true if the User Manager was shown, false otherwise.
-bool ShowUserManagerOnStartupIfNeeded(
-    Profile* last_used_profile, const base::CommandLine& command_line) {
+// Returns whether |profile| can be opened during Chrome startup without
+// explicit user action.
+bool ProfileCanBeAutoOpened(Profile* profile) {
 #if defined(OS_CHROMEOS)
-  // ChromeOS never shows the User Manager on startup.
-  return false;
+  // On ChromeOS, ther user has alrady chosen and logged into the profile
+  // before Chrome starts up.
+  return true;
 #else
+  // Profiles that require signin are not available.
   ProfileAttributesEntry* entry = nullptr;
-  bool has_entry =
-      g_browser_process->profile_manager()
+  if (g_browser_process->profile_manager()
           ->GetProfileAttributesStorage()
-          .GetProfileAttributesWithPath(last_used_profile->GetPath(), &entry);
-
-  if (!has_entry || !entry->IsSigninRequired()) {
-    // Signin is not required. However, guest, system or locked profiles cannot
-    // be re-opened on startup. The only exception is if there's already a Guest
-    // window open in a separate process (for example, launching a new browser
-    // after clicking on a downloaded file in Guest mode).
-    if ((!last_used_profile->IsGuestSession() &&
-         !last_used_profile->IsSystemProfile()) ||
-        (chrome::GetBrowserCount(last_used_profile->GetOffTheRecordProfile()) >
-         0)) {
-      return false;
-    }
+          .GetProfileAttributesWithPath(profile->GetPath(), &entry) &&
+      entry->IsSigninRequired()) {
+    return false;
   }
 
+  // Guest or system profiles are not available unless a separate process
+  // already has a window open for the profile.
+  return (!profile->IsGuestSession() && !profile->IsSystemProfile()) ||
+         (chrome::GetBrowserCount(profile->GetOffTheRecordProfile()) > 0);
+#endif
+}
+
+// Returns whether the User Manager was shown.
+bool ShowUserManagerOnStartupIfNeeded(Profile* last_used_profile,
+                                      const base::CommandLine& command_line) {
+  if (ProfileCanBeAutoOpened(last_used_profile))
+    return false;
+
   // Show the User Manager.
   profiles::UserManagerAction action =
       command_line.HasSwitch(switches::kShowAppList) ?
@@ -298,7 +300,6 @@
   UserManager::Show(
       base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL, action);
   return true;
-#endif
 }
 
 }  // namespace
@@ -588,11 +589,16 @@
   }
 
   bool silent_launch = false;
+  bool can_use_last_profile =
+      (ProfileCanBeAutoOpened(last_used_profile) &&
+       !IncognitoModePrefs::ShouldLaunchIncognito(
+           command_line, last_used_profile->GetPrefs()));
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   // If we are just displaying a print dialog we shouldn't open browser
   // windows.
   if (command_line.HasSwitch(switches::kCloudPrintFile) &&
+      can_use_last_profile &&
       print_dialog_cloud::CreatePrintDialogFromCommandLine(last_used_profile,
                                                            command_line)) {
     silent_launch = true;
@@ -684,8 +690,7 @@
     return true;
 
   if (command_line.HasSwitch(extensions::switches::kLoadApps) &&
-      !IncognitoModePrefs::ShouldLaunchIncognito(
-          command_line, last_used_profile->GetPrefs())) {
+      can_use_last_profile) {
     if (!ProcessLoadApps(command_line, cur_dir, last_used_profile))
       return false;
 
@@ -699,9 +704,7 @@
   }
 
   // Check for --load-and-launch-app.
-  if (command_line.HasSwitch(apps::kLoadAndLaunchApp) &&
-      !IncognitoModePrefs::ShouldLaunchIncognito(
-          command_line, last_used_profile->GetPrefs())) {
+  if (command_line.HasSwitch(apps::kLoadAndLaunchApp) && can_use_last_profile) {
     base::CommandLine::StringType path =
         command_line.GetSwitchValueNative(apps::kLoadAndLaunchApp);
 
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 4d1ef42..d4d62e3 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -74,7 +74,7 @@
 }
 
 ui::ModalType SigninViewControllerDelegateViews::GetModalType() const {
-  return ui::MODAL_TYPE_CHILD;
+  return ui::MODAL_TYPE_WINDOW;
 }
 
 bool SigninViewControllerDelegateViews::ShouldShowCloseButton() const {
@@ -107,9 +107,12 @@
 }
 
 void SigninViewControllerDelegateViews::DisplayModal() {
-  modal_signin_widget_ = constrained_window::ShowWebModalDialogViews(
-      this, browser_->tab_strip_model()->GetActiveWebContents());
-  content_view_->RequestFocus();
+  gfx::NativeWindow window = browser_->tab_strip_model()
+                                 ->GetActiveWebContents()
+                                 ->GetTopLevelNativeWindow();
+  modal_signin_widget_ =
+      constrained_window::CreateBrowserModalDialogViews(this, window);
+  modal_signin_widget_->Show();
 }
 
 // static
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 5660e1b..460bf6e 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -88,9 +88,9 @@
 
     // If there's a populated email, we must check first that this user is using
     // SAML in order to decide whether to show the interstitial page.
-    const user_manager::User* user =
-        user_manager::UserManager::Get()->FindUser(
-            user_manager::known_user::GetAccountId(email, std::string()));
+    const user_manager::User* user = user_manager::UserManager::Get()->FindUser(
+        user_manager::known_user::GetAccountId(email, std::string() /* id */,
+                                               AccountType::UNKNOWN));
 
     if (user && user->using_saml())
       return GAIA_SCREEN_MODE_SAML_INTERSTITIAL;
@@ -406,7 +406,7 @@
 
 void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) {
   if (!Delegate()->IsUserWhitelisted(user_manager::known_user::GetAccountId(
-          user_email, std::string() /* gaia_id */)))
+          user_email, std::string() /* id */, AccountType::UNKNOWN)))
     ShowWhitelistCheckFailedError();
 }
 
@@ -451,12 +451,13 @@
 
 AccountId GaiaScreenHandler::GetAccountId(
     const std::string& authenticated_email,
-    const std::string& gaia_id) const {
+    const std::string& id,
+    const AccountType& account_type) const {
   const std::string canonicalized_email =
       gaia::CanonicalizeEmail(gaia::SanitizeEmail(authenticated_email));
 
-  const AccountId account_id =
-      user_manager::known_user::GetAccountId(authenticated_email, gaia_id);
+  const AccountId account_id = user_manager::known_user::GetAccountId(
+      authenticated_email, id, account_type);
 
   if (account_id.GetUserEmail() != canonicalized_email) {
     LOG(WARNING) << "Existing user '" << account_id.GetUserEmail()
@@ -481,7 +482,7 @@
   const std::string sanitized_email = gaia::SanitizeEmail(email);
   Delegate()->SetDisplayEmail(sanitized_email);
 
-  UserContext user_context(GetAccountId(email, gaia_id));
+  UserContext user_context(GetAccountId(email, gaia_id, AccountType::GOOGLE));
   user_context.SetKey(Key(password));
   user_context.SetAuthCode(auth_code);
   user_context.SetAuthFlow(using_saml
@@ -566,7 +567,8 @@
   DCHECK(!gaia_id.empty());
   const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
   Delegate()->SetDisplayEmail(sanitized_email);
-  UserContext user_context(GetAccountId(typed_email, gaia_id));
+  UserContext user_context(
+      GetAccountId(typed_email, gaia_id, AccountType::GOOGLE));
   user_context.SetKey(Key(password));
   user_context.SetAuthFlow(using_saml
                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
@@ -705,7 +707,7 @@
     Delegate()->LoadSigninWallpaper();
   } else {
     Delegate()->LoadWallpaper(user_manager::known_user::GetAccountId(
-        populated_email_, std::string()));
+        populated_email_, std::string() /* id */, AccountType::UNKNOWN));
   }
 
   input_method::InputMethodManager* imm =
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 21c5497..45d3fa2 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -179,7 +179,8 @@
   // Returns user canonical e-mail. Finds already used account alias, if
   // user has already signed in.
   AccountId GetAccountId(const std::string& authenticated_email,
-                         const std::string& gaia_id) const;
+                         const std::string& id,
+                         const AccountType& account_type) const;
 
   bool offline_login_is_active() const { return offline_login_is_active_; }
   void set_offline_login_is_active(bool offline_login_is_active) {
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 8627e66..77eb09d 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -184,6 +184,8 @@
   switch (action_type) {
     case sync_ui_util::REAUTHENTICATE:
       return "reauthenticate";
+    case sync_ui_util::SIGNOUT_AND_SIGNIN:
+      return "signOutAndSignIn";
     case sync_ui_util::UPGRADE_CLIENT:
       return "upgradeClient";
     case sync_ui_util::ENTER_PASSPHRASE:
diff --git a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
index 63b9768..d832ba9 100644
--- a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
@@ -5,15 +5,30 @@
 #include "chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h"
 
 #include "ash/common/system/chromeos/palette/palette_utils.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace chromeos {
 namespace options {
 
-OptionsStylusHandler::OptionsStylusHandler() {}
+namespace {
 
-OptionsStylusHandler::~OptionsStylusHandler() {}
+// Keys in objects passed to updateNoteTakingApps_.
+constexpr char kAppNameKey[] = "name";
+constexpr char kAppIdKey[] = "id";
+constexpr char kAppPreferredKey[] = "preferred";
+
+}  // namespace
+
+OptionsStylusHandler::OptionsStylusHandler() : weak_ptr_factory_(this) {
+  NoteTakingHelper::Get()->AddObserver(this);
+}
+
+OptionsStylusHandler::~OptionsStylusHandler() {
+  NoteTakingHelper::Get()->RemoveObserver(this);
+}
 
 void OptionsStylusHandler::GetLocalizedValues(
     base::DictionaryValue* localized_strings) {
@@ -34,14 +49,82 @@
   localized_strings->SetString(
       "stylusFindMoreApps",
       l10n_util::GetStringUTF16(IDS_SETTINGS_STYLUS_FIND_MORE_APPS));
+  localized_strings->SetString(
+      "stylusNoteTakingApp",
+      l10n_util::GetStringUTF16(IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_LABEL));
+  localized_strings->SetString(
+      "stylusNoteTakingAppNoneAvailable",
+      l10n_util::GetStringUTF16(
+          IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE));
+  localized_strings->SetString(
+      "stylusNoteTakingAppWaitingForAndroid",
+      l10n_util::GetStringUTF16(
+          IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID));
 
   localized_strings->SetBoolean("showStylusSettings",
                                 ash::IsPaletteFeatureEnabled());
 }
 
-void OptionsStylusHandler::InitializePage() {}
+void OptionsStylusHandler::InitializePage() {
+  UpdateNoteTakingApps();
+}
 
-void OptionsStylusHandler::RegisterMessages() {}
+void OptionsStylusHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "setPreferredNoteTakingApp",
+      base::Bind(&OptionsStylusHandler::SetPreferredNoteTakingApp,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OptionsStylusHandler::OnAvailableNoteTakingAppsUpdated() {
+  UpdateNoteTakingApps();
+}
+
+void OptionsStylusHandler::UpdateNoteTakingApps() {
+  bool waiting_for_android = false;
+  note_taking_app_ids_.clear();
+  base::ListValue apps_list;
+
+  NoteTakingHelper* helper = NoteTakingHelper::Get();
+  if (helper->android_enabled() && !helper->android_apps_received()) {
+    // If Android is enabled but not ready yet, let the JS know so it can
+    // disable the menu and display an explanatory message.
+    waiting_for_android = true;
+  } else {
+    for (const NoteTakingAppInfo& info :
+         NoteTakingHelper::Get()->GetAvailableApps(
+             Profile::FromWebUI(web_ui()))) {
+      std::unique_ptr<base::DictionaryValue> dict(
+          new base::DictionaryValue());
+      dict->SetString(kAppNameKey, info.name);
+      dict->SetString(kAppIdKey, info.app_id);
+      dict->SetBoolean(kAppPreferredKey, info.preferred);
+      apps_list.Append(std::move(dict));
+
+      note_taking_app_ids_.insert(info.app_id);
+    }
+  }
+
+  web_ui()->CallJavascriptFunctionUnsafe(
+      "StylusOverlay.updateNoteTakingApps", apps_list,
+      base::FundamentalValue(waiting_for_android));
+}
+
+void OptionsStylusHandler::SetPreferredNoteTakingApp(
+    const base::ListValue* args) {
+  std::string app_id;
+  CHECK(args->GetString(0, &app_id));
+
+  // Sanity check: make sure that the ID we got back from WebUI is in the
+  // currently-available set.
+  if (!note_taking_app_ids_.count(app_id)) {
+    LOG(ERROR) << "Got unknown note-taking-app ID \"" << app_id << "\"";
+    return;
+  }
+
+  NoteTakingHelper::Get()->SetPreferredApp(Profile::FromWebUI(web_ui()),
+                                           app_id);
+}
 
 }  // namespace options
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
index 1e5f3c6..a35595a 100644
--- a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
+++ b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
@@ -5,14 +5,24 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_
 
+#include <set>
+#include <string>
+
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/ui/webui/options/options_ui.h"
 
+namespace base {
+class ListValue;
+}  // namespace base
+
 namespace chromeos {
 namespace options {
 
 // Stylus-specific options C++ code.
-class OptionsStylusHandler : public ::options::OptionsPageUIHandler {
+class OptionsStylusHandler : public ::options::OptionsPageUIHandler,
+                             public NoteTakingHelper::Observer {
  public:
   OptionsStylusHandler();
   ~OptionsStylusHandler() override;
@@ -22,7 +32,22 @@
   void InitializePage() override;
   void RegisterMessages() override;
 
+  // NoteTakingHelper::Observer implementation.
+  void OnAvailableNoteTakingAppsUpdated() override;
+
  private:
+  // Updates the note-taking app menu in the stylus overlay to display the
+  // currently-available set of apps.
+  void UpdateNoteTakingApps();
+
+  // Called from WebUI when the user selects a note-taking app.
+  void SetPreferredNoteTakingApp(const base::ListValue* args);
+
+  // IDs of available note-taking apps.
+  std::set<std::string> note_taking_app_ids_;
+
+  base::WeakPtrFactory<OptionsStylusHandler> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(OptionsStylusHandler);
 };
 
diff --git a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
index d302cbf..c5858ea 100644
--- a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
+++ b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
@@ -38,7 +38,7 @@
   if (!status) {
     LOG(WARNING) << "Failed to deserialize account_id.";
     account_id = user_manager::known_user::GetAccountId(
-        serialized_account_id, std::string() /* gaia_id */);
+        serialized_account_id, std::string() /* id */, AccountType::UNKNOWN);
   }
   *email = account_id.GetUserEmail();
 }
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler.cc b/chrome/browser/ui/webui/options/sync_setup_handler.cc
index 8d995e3..d9f8054 100644
--- a/chrome/browser/ui/webui/options/sync_setup_handler.cc
+++ b/chrome/browser/ui/webui/options/sync_setup_handler.cc
@@ -647,8 +647,11 @@
 
 #if !defined(OS_CHROMEOS)
 void SyncSetupHandler::HandleStartSignin(const base::ListValue* args) {
-  // Should only be called if the user is not already signed in.
-  DCHECK(!SigninManagerFactory::GetForProfile(GetProfile())->IsAuthenticated());
+  // Should only be called if the user is not already signed in or has an auth
+  // error.
+  DCHECK(
+      !SigninManagerFactory::GetForProfile(GetProfile())->IsAuthenticated() ||
+      SigninErrorControllerFactory::GetForProfile(GetProfile())->HasError());
   bool creating_supervised_user = false;
   args->GetBoolean(0, &creating_supervised_user);
   OpenSyncSetup(creating_supervised_user);
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
index 2e7c957f..2d068a4 100644
--- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -169,6 +169,7 @@
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kEnableNativeCups)) {
       AddPrintersToList(prefs_->GetPrinters(), &printer_list);
+      AddPrintersToList(prefs_->GetRecommendedPrinters(), &printer_list);
     }
 
     content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 642b7c8..35e1d2d 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -156,6 +156,8 @@
   switch (action_type) {
     case sync_ui_util::REAUTHENTICATE:
       return "reauthenticate";
+    case sync_ui_util::SIGNOUT_AND_SIGNIN:
+      return "signOutAndSignIn";
     case sync_ui_util::UPGRADE_CLIENT:
       return "upgradeClient";
     case sync_ui_util::ENTER_PASSPHRASE:
@@ -505,7 +507,7 @@
     return;
   }
 
-  OpenSyncSetup(false /* creating_supervised_user */);
+  OpenSyncSetup();
 }
 
 #if defined(OS_CHROMEOS)
@@ -521,11 +523,12 @@
 void PeopleHandler::HandleStartSignin(const base::ListValue* args) {
   AllowJavascript();
 
-  // Should only be called if the user is not already signed in.
-  DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated());
-  bool creating_supervised_user = false;
-  args->GetBoolean(0, &creating_supervised_user);
-  OpenSyncSetup(creating_supervised_user);
+  // Should only be called if the user is not already signed in or has an auth
+  // error.
+  DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
+         SigninErrorControllerFactory::GetForProfile(profile_)->HasError());
+
+  OpenSyncSetup();
 }
 
 void PeopleHandler::HandleStopSyncing(const base::ListValue* args) {
@@ -623,7 +626,7 @@
   configuring_sync_ = false;
 }
 
-void PeopleHandler::OpenSyncSetup(bool creating_supervised_user) {
+void PeopleHandler::OpenSyncSetup() {
   // Notify services that login UI is now active.
   GetLoginUIService()->SetLoginUI(this);
 
@@ -643,18 +646,14 @@
   //    sync configure UI, not login UI).
   // 7) User re-enables sync after disabling it via advanced settings.
 #if !defined(OS_CHROMEOS)
-  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_);
-  if (!signin->IsAuthenticated() ||
+  if (!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
       SigninErrorControllerFactory::GetForProfile(profile_)->HasError()) {
     // User is not logged in (cases 1-2), or login has been specially requested
     // because previously working credentials have expired (case 3). Close sync
     // setup including any visible overlays, and display the gaia auth page.
     // Control will be returned to the sync settings page once auth is complete.
     CloseUI();
-    DisplayGaiaLogin(
-        creating_supervised_user ?
-            signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER :
-            signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
+    DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
     return;
   }
 #endif
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index da243e0..9b367b7 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -60,7 +60,7 @@
   ~PeopleHandler() override;
 
   // Initializes the sync setup flow and shows the setup UI.
-  void OpenSyncSetup(bool creating_supervised_user);
+  void OpenSyncSetup();
 
   // Terminates the sync setup flow.
   void CloseSyncSetup();
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 273f55bf..487eff2 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -396,7 +396,7 @@
   EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   SetDefaultExpectationsForConfigPage();
 
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   EXPECT_EQ(1U, web_ui_.call_data().size());
   ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
@@ -432,7 +432,7 @@
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
   SetDefaultExpectationsForConfigPage();
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   // It's important to tell sync the user cancelled the setup flow before we
   // tell it we're through with the setup progress.
@@ -452,7 +452,7 @@
   error_ = GoogleServiceAuthError::AuthErrorNone();
   EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
   ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
   Mock::VerifyAndClearExpectations(mock_pss_);
   error_ = GoogleServiceAuthError(
@@ -479,7 +479,7 @@
       .WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   // Open the web UI.
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   ASSERT_FALSE(handler_->is_configuring_sync());
 }
@@ -489,7 +489,7 @@
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   // Open the web UI.
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   ASSERT_FALSE(handler_->is_configuring_sync());
 }
@@ -498,7 +498,7 @@
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   // Open the web UI.
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   ASSERT_FALSE(handler_->is_configuring_sync());
 }
@@ -698,7 +698,7 @@
   SetupInitializedProfileSyncService();
   // This should display the sync setup dialog (not login).
   SetDefaultExpectationsForConfigPage();
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   ExpectSyncPrefsChanged();
 }
@@ -733,7 +733,7 @@
 
   // On ChromeOS, this should display the spinner while we try to startup the
   // sync engine, and on desktop this displays the login dialog.
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   // Sync setup is closed when re-auth is in progress.
   EXPECT_EQ(NULL,
@@ -752,7 +752,7 @@
   SetupInitializedProfileSyncService();
   SetDefaultExpectationsForConfigPage();
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "syncAllDataTypes", true);
@@ -782,7 +782,7 @@
   sync_prefs.SetKeepEverythingSynced(false);
   SetDefaultExpectationsForConfigPage();
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes());
@@ -806,7 +806,7 @@
         WillRepeatedly(Return(types));
 
     // This should display the sync setup dialog (not login).
-    handler_->OpenSyncSetup(false /* creating_supervised_user */);
+    handler_->OpenSyncSetup();
 
     // Close the config overlay.
     LoginUIServiceFactory::GetForProfile(profile_)->LoginUIClosed(
@@ -830,7 +830,7 @@
   SetDefaultExpectationsForConfigPage();
 
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "passphraseRequired", true);
@@ -846,7 +846,7 @@
   SetDefaultExpectationsForConfigPage();
 
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "passphraseRequired", true);
@@ -864,7 +864,7 @@
       .WillRepeatedly(Return(true));
 
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "encryptAllData", true);
@@ -881,7 +881,7 @@
       .WillRepeatedly(Return(false));
 
   // This should display the sync setup dialog (not login).
-  handler_->OpenSyncSetup(false /* creating_supervised_user */);
+  handler_->OpenSyncSetup();
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "encryptAllData", false);
diff --git a/chrome/test/data/pdf/gesture_detector_test.js b/chrome/test/data/pdf/gesture_detector_test.js
index b73b85a..b238294 100644
--- a/chrome/test/data/pdf/gesture_detector_test.js
+++ b/chrome/test/data/pdf/gesture_detector_test.js
@@ -7,21 +7,25 @@
 
   class StubElement {
     constructor() {
-      this.listeners_ = new Map([
+      this.listeners = new Map([
         ['touchstart', []],
         ['touchmove', []],
         ['touchend', []],
         ['touchcancel', []]
       ]);
+
+      this.listenerOptions = new Map();
     }
 
-    addEventListener(type, listener) {
-      if (this.listeners_.has(type))
-        this.listeners_.get(type).push(listener);
+    addEventListener(type, listener, options) {
+      if (this.listeners.has(type)) {
+        this.listeners.get(type).push(listener);
+        this.listenerOptions.set(listener, options);
+      }
     }
 
     sendEvent(event) {
-      for (let l of this.listeners_.get(event.type))
+      for (let l of this.listeners.get(event.type))
         l(event);
     }
   }
@@ -178,6 +182,17 @@
       let gestureDetector = new GestureDetector(stubElement);
       let pinchListener = new PinchListener(gestureDetector);
 
+      // Ensure that the touchstart listener is not passive, otherwise the
+      // call to preventDefault will be ignored. Since listeners could default
+      // to being passive, we must set the value explicitly
+      // (see crbug.com/675730).
+      for (let l of stubElement.listeners.get('touchstart')) {
+        let options = stubElement.listenerOptions.get(l);
+        chrome.test.assertTrue(!!options &&
+                               typeof(options.passive) == 'boolean');
+        chrome.test.assertFalse(options.passive);
+      }
+
       let pinchStartEvent = new MockTouchEvent('touchstart', [
         {clientX: 0, clientY: 0},
         {clientX: 0, clientY: 2}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 4c5cf7d..00dd1324 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2044,6 +2044,23 @@
     ]
   },
 
+  "RoamingProfileSupportEnabled": {
+    "os": ["win"],
+    "test_policy": { "RoamingProfileSupportEnabled": true },
+    "pref_mappings": [
+      { "pref": "sync.enable_local_sync_backend" }
+    ]
+  },
+
+  "RoamingProfileLocation": {
+    "os": ["win"],
+    "test_policy": { "RoamingProfileLocation": "${roaming_app_data}" },
+    "pref_mappings": [
+      { "pref": "sync.local_sync_backend_dir" }
+    ]
+  },
+
+
   "----- Chrome OS policies ------------------------------------------------": {},
 
   "ChromeOsLockOnIdleSuspend": {
diff --git a/chrome/test/data/webui/bluetooth_internals_browsertest.js b/chrome/test/data/webui/bluetooth_internals_browsertest.js
index 0e948f1..2bfa1804b 100644
--- a/chrome/test/data/webui/bluetooth_internals_browsertest.js
+++ b/chrome/test/data/webui/bluetooth_internals_browsertest.js
@@ -51,9 +51,7 @@
         'device/bluetooth/public/interfaces/adapter.mojom',
         'device/bluetooth/public/interfaces/device.mojom',
         'mojo/public/js/bindings',
-        'mojo/public/js/connection',
-      ]).then(function([frameInterfaces, adapter, device, bindings,
-          connection]) {
+      ]).then(function([frameInterfaces, adapter, device, bindings]) {
         /**
           * A test adapter factory proxy for the chrome://bluetooth-internals
           * page.
@@ -66,6 +64,7 @@
             'getAdapter',
           ]);
 
+          this.binding = new bindings.Binding(adapter.AdapterFactory, this);
           this.adapter = new TestAdapter();
           this.adapterBinding_ = new bindings.Binding(adapter.Adapter,
                                                       this.adapter);
@@ -88,14 +87,12 @@
           * Must be used to create message pipe handle from test code.
           *
           * @constructor
-          * @extends {adapter.Adapter.stubClass}
           */
         var TestAdapter = function() {
           this.proxy = new TestAdapterProxy();
         };
 
         TestAdapter.prototype = {
-          __proto__: adapter.Adapter.stubClass.prototype,
           getInfo: function() { return this.proxy.getInfo(); },
           getDevices: function() { return this.proxy.getDevices(); },
           setClient: function(client) { return this.proxy.setClient(client); }
@@ -146,10 +143,8 @@
 
         frameInterfaces.addInterfaceOverrideForTesting(
             adapter.AdapterFactory.name, function(handle) {
-              var stub = connection.bindHandleToStub(
-                  handle, adapter.AdapterFactory);
-
               this.adapterFactory = new TestAdapterFactoryProxy();
+              this.adapterFactory.binding.bind(handle);
 
               this.adapterFactory.adapter.proxy.setTestDevices([
                 this.fakeDeviceInfo1(),
@@ -158,8 +153,6 @@
               this.adapterFactory.adapter.proxy.setTestAdapter(
                   this.fakeAdapterInfo());
 
-              bindings.StubBindings(stub).delegate = this.adapterFactory;
-
               this.setupResolver.resolve();
             }.bind(this));
 
diff --git a/chrome/test/data/webui/plugins_browsertest.js b/chrome/test/data/webui/plugins_browsertest.js
index 6f613ea..1fc8ac7 100644
--- a/chrome/test/data/webui/plugins_browsertest.js
+++ b/chrome/test/data/webui/plugins_browsertest.js
@@ -55,6 +55,8 @@
         'saveShowDetailsToPrefs',
       ]);
 
+      this.bindingSet = null;
+
       /**
        * The data to be returned by |getPluginsData_|.
        * @private
@@ -96,20 +98,19 @@
     window.setupFn = function() {
       return importModules([
         'mojo/public/js/bindings',
-        'mojo/public/js/connection',
         'chrome/browser/ui/webui/plugins/plugins.mojom',
         'content/public/renderer/frame_interfaces',
       ]).then(function(modules) {
         var bindings = modules[0];
-        var connection = modules[1];
-        var pluginsMojom = modules[2];
-        var frameInterfaces = modules[3];
+        var pluginsMojom = modules[1];
+        var frameInterfaces = modules[2];
 
+        this.browserProxy.bindingSet = new bindings.BindingSet(
+            pluginsMojom.PluginsPageHandler);
         frameInterfaces.addInterfaceOverrideForTesting(
             pluginsMojom.PluginsPageHandler.name, function(handle) {
-              var stub = connection.bindHandleToStub(
-                  handle, pluginsMojom.PluginsPageHandler);
-              bindings.StubBindings(stub).delegate = this.browserProxy;
+              this.browserProxy.bindingSet.addBinding(this.browserProxy,
+                                                      handle);
             }.bind(this));
         return this.setupFnResolver.promise;
       }.bind(this));
diff --git a/chrome/test/data/webui/settings/category_default_setting_tests.js b/chrome/test/data/webui/settings/category_default_setting_tests.js
index 306e535..58a8b071 100644
--- a/chrome/test/data/webui/settings/category_default_setting_tests.js
+++ b/chrome/test/data/webui/settings/category_default_setting_tests.js
@@ -193,7 +193,6 @@
 
             assertEquals(category, contentType);
             assertTrue(testElement.categoryEnabled);
-            assertTrue(testElement.categoryEnabled);
             assertFalse(secondaryToggle.disabled);
             assertTrue(secondaryToggle.checked);
 
diff --git a/chrome/test/data/webui/settings/import_data_dialog_test.js b/chrome/test/data/webui/settings/import_data_dialog_test.js
index 703a00f..d053879 100644
--- a/chrome/test/data/webui/settings/import_data_dialog_test.js
+++ b/chrome/test/data/webui/settings/import_data_dialog_test.js
@@ -95,8 +95,8 @@
     dialog = document.createElement('settings-import-data-dialog');
     dialog.set('prefs', prefs);
     document.body.appendChild(dialog);
-    assertTrue(dialog.$.dialog.open);
     return browserProxy.whenCalled('initializeImportDialog').then(function() {
+      assertTrue(dialog.$.dialog.open);
       Polymer.dom.flush();
     });
   });
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index f58d024..cf283c4 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -15,6 +15,8 @@
     "internal/",
     "telemetry/",
     "../../../third_party/catapult/",
+    "../../../tools/json_comment_eater/",
+    "../../../tools/json_to_struct/",
     "../../../tools/perf/",
     "../../../tools/variations/",
   ]
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index a4da7ad4..58a351c 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -444,6 +444,7 @@
     'filename': 'gcp_portmon.dll.pdb',
     'buildtype': ['official'],
     'archive': 'cloud_print.zip',
+    'optional': ['official'],
   },
   {
     'filename': 'gcp_portmon64.dll',
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index d5d122c..d929769 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9105.0.0
\ No newline at end of file
+9110.0.0
\ No newline at end of file
diff --git a/chromeos/cryptohome/cryptohome_parameters.cc b/chromeos/cryptohome/cryptohome_parameters.cc
index 9ce9c91..4f6d557 100644
--- a/chromeos/cryptohome/cryptohome_parameters.cc
+++ b/chromeos/cryptohome/cryptohome_parameters.cc
@@ -19,7 +19,7 @@
 
 const std::string GetCryptohomeId(const AccountId& account_id) {
   // Guest/kiosk/managed/public accounts have empty GaiaId. Default to email.
-  if (account_id.GetGaiaId().empty())
+  if (account_id.GetAccountType() == AccountType::UNKNOWN)
     return account_id.GetUserEmail();  // Migrated
 
   if (GetGaiaIdMigrationStatus(account_id))
@@ -56,7 +56,7 @@
   // A LOT of tests start with --login_user <user>, and not registing this user
   // before. So we might have "known_user" entry without gaia_id.
   for (const AccountId& known_id : known_account_ids) {
-    if (!known_id.GetGaiaId().empty() && known_id.GetAccountIdKey() == id_) {
+    if (known_id.HasAccountIdKey() && known_id.GetAccountIdKey() == id_) {
       return known_id;
     }
   }
@@ -67,8 +67,8 @@
     }
   }
 
-  return user_manager::known_user::GetAccountId(id_,
-                                                std::string() /* gaia_id */);
+  return user_manager::known_user::GetAccountId(id_, std::string() /* id */,
+                                                AccountType::UNKNOWN);
 }
 
 KeyDefinition::AuthorizationData::Secret::Secret() : encrypt(false),
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index 9fc700a..389dd6c 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -220,12 +220,12 @@
   }
   const bool already_migrated = cryptohome::GetGaiaIdMigrationStatus(
       attempt->user_context.GetAccountId());
-  const bool has_gaia_id =
-      !attempt->user_context.GetAccountId().GetGaiaId().empty();
+  const bool has_account_key =
+      attempt->user_context.GetAccountId().HasAccountIdKey();
 
   bool need_migration = false;
   if (!create_if_nonexistent && !already_migrated) {
-    if (has_gaia_id) {
+    if (has_account_key) {
       need_migration = true;
     } else {
       LOG(WARNING) << "Account '"
@@ -248,7 +248,7 @@
                    create_if_nonexistent));
     return;
   }
-  if (!already_migrated && has_gaia_id) {
+  if (!already_migrated && has_account_key) {
     // Mark new users migrated.
     cryptohome::SetGaiaIdMigrationStatusDone(
         attempt->user_context.GetAccountId());
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index f05b53f0..133f771 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -283,11 +283,13 @@
                                   uint64_t* last_updated_time) {
     std::string contents;
     if (!ValidateAndGetResponseAsString(*source, &contents)) {
+      LOG(WARNING) << "Response not validated";
       return false;
     }
 
     auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents));
     if (dict == nullptr) {
+      LOG(WARNING) << "Response not a dictionary";
       return false;
     }
     std::string last_updated_time_string;
@@ -299,6 +301,7 @@
     }
 
     if (ppd_contents->size() > options_.max_ppd_contents_size_) {
+      LOG(WARNING) << "PPD too large";
       // PPD is too big.
       //
       // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
@@ -414,9 +417,9 @@
   // return true, otherwise return false.
   bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher,
                                       std::string* contents) {
-    return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
-            (fetcher.GetResponseCode() == net::HTTP_OK) &&
-            fetcher.GetResponseAsString(contents));
+    return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
+           (fetcher.GetResponseCode() == net::HTTP_OK) &&
+           fetcher.GetResponseAsString(contents);
   }
 
   // API key for accessing quirks server.
diff --git a/chromeos/printing/printer_configuration.cc b/chromeos/printing/printer_configuration.cc
index b5c8c3f..9b6f663 100644
--- a/chromeos/printing/printer_configuration.cc
+++ b/chromeos/printing/printer_configuration.cc
@@ -10,11 +10,11 @@
 
 namespace chromeos {
 
-Printer::Printer() {
+Printer::Printer() : source_(SRC_USER_PREFS) {
   id_ = base::GenerateGUID();
 }
 
-Printer::Printer(const std::string& id) : id_(id) {
+Printer::Printer(const std::string& id) : id_(id), source_(SRC_USER_PREFS) {
   if (id_.empty())
     id_ = base::GenerateGUID();
 }
diff --git a/chromeos/printing/printer_configuration.h b/chromeos/printing/printer_configuration.h
index 233a09f0..5223753 100644
--- a/chromeos/printing/printer_configuration.h
+++ b/chromeos/printing/printer_configuration.h
@@ -43,6 +43,12 @@
     std::string effective_model;
   };
 
+  // The location where the printer is stored.
+  enum Source {
+    SRC_USER_PREFS,
+    SRC_POLICY,
+  };
+
   // Constructs a printer object that is completely empty.
   Printer();
 
@@ -90,6 +96,9 @@
   // |uri_|.
   bool IsIppEverywhere() const;
 
+  Source source() const { return source_; }
+  void set_source(const Source source) { source_ = source; }
+
  private:
   // Globally unique identifier. Empty indicates a new printer.
   std::string id_;
@@ -115,6 +124,9 @@
 
   // The UUID from an autoconf protocol for deduplication. Could be empty.
   std::string uuid_;
+
+  // The datastore which holds this printer.
+  Source source_;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/printing/printer_translator.cc b/chromeos/printing/printer_translator.cc
index 9f64668a..4574bf6 100644
--- a/chromeos/printing/printer_translator.cc
+++ b/chromeos/printing/printer_translator.cc
@@ -33,6 +33,9 @@
 const char kPpdReference[] = "ppd_reference";
 const char kUUID[] = "uuid";
 
+// The name of the PpdResource for policy printers.
+const char kPpdResource[] = "ppd_resource";
+
 Printer::PpdReference DictionaryToPpdReference(
     const base::DictionaryValue* value) {
   Printer::PpdReference ppd;
@@ -58,15 +61,11 @@
   return dictionary;
 }
 
-}  // namespace
-
-namespace printing {
-
-const char kPrinterId[] = "id";
-
-std::unique_ptr<Printer> PrefToPrinter(const DictionaryValue& value) {
+// Converts |value| into a Printer object for the fields that are shared
+// between pref printers and policy printers.
+std::unique_ptr<Printer> DictionaryToPrinter(const DictionaryValue& value) {
   std::string id;
-  if (!value.GetString(kPrinterId, &id)) {
+  if (!value.GetString(printing::kPrinterId, &id)) {
     LOG(WARNING) << "Record id required";
     return nullptr;
   }
@@ -97,6 +96,23 @@
   if (value.GetString(kUUID, &uuid))
     printer->set_uuid(uuid);
 
+  return printer;
+}
+
+}  // namespace
+
+namespace printing {
+
+const char kPrinterId[] = "id";
+
+std::unique_ptr<Printer> PrefToPrinter(const DictionaryValue& value) {
+  if (!value.HasKey(kPrinterId)) {
+    LOG(WARNING) << "Record id required";
+    return nullptr;
+  }
+
+  std::unique_ptr<Printer> printer = DictionaryToPrinter(value);
+
   const DictionaryValue* ppd;
   if (value.GetDictionary(kPpdReference, &ppd)) {
     *printer->mutable_ppd_reference() = DictionaryToPpdReference(ppd);
@@ -122,6 +138,17 @@
   return dictionary;
 }
 
-}  // namespace printing
+std::unique_ptr<Printer> RecommendedPrinterToPrinter(
+    const base::DictionaryValue& pref) {
+  std::unique_ptr<Printer> printer = DictionaryToPrinter(pref);
 
+  const DictionaryValue* ppd;
+  if (pref.GetDictionary(kPpdResource, &ppd)) {
+    *printer->mutable_ppd_reference() = DictionaryToPpdReference(ppd);
+  }
+
+  return printer;
+}
+
+}  // namespace printing
 }  // namespace chromeos
diff --git a/chromeos/printing/printer_translator.h b/chromeos/printing/printer_translator.h
index 14283ab..1efa84e 100644
--- a/chromeos/printing/printer_translator.h
+++ b/chromeos/printing/printer_translator.h
@@ -28,6 +28,11 @@
 CHROMEOS_EXPORT std::unique_ptr<base::DictionaryValue> PrinterToPref(
     const Printer& printer);
 
+// Returns a new printer populated with the fields from |pref|.  Processes
+// dictionaries from policy i.e. cPanel.
+CHROMEOS_EXPORT std::unique_ptr<Printer> RecommendedPrinterToPrinter(
+    const base::DictionaryValue& pref);
+
 }  // namespace printing
 }  // namespace chromeos
 
diff --git a/chromeos/tpm/tpm_token_info_getter_unittest.cc b/chromeos/tpm/tpm_token_info_getter_unittest.cc
index cff50eaf..6e6a9d9 100644
--- a/chromeos/tpm/tpm_token_info_getter_unittest.cc
+++ b/chromeos/tpm/tpm_token_info_getter_unittest.cc
@@ -259,7 +259,7 @@
 class UserTPMTokenInfoGetterTest : public testing::Test {
  public:
   UserTPMTokenInfoGetterTest()
-      : account_id_(AccountId::FromUserEmail("user")) {}
+      : account_id_(AccountId::FromUserEmail("user@gmail.com")) {}
   ~UserTPMTokenInfoGetterTest() override {}
 
   void SetUp() override {
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 09de1ca8..b4868426 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -886,7 +886,7 @@
         base::TaskTraits()
             .WithPriority(base::TaskPriority::USER_BLOCKING)
             .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN)
-            .WithFileIO());
+            .MayBlock().WithSyncPrimitives());
   }
 
   // Create the history backend.
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index c3477b30..370e2a4 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -21,6 +21,8 @@
     "category_info.cc",
     "category_info.h",
     "category_rankers/category_ranker.h",
+    "category_rankers/click_based_category_ranker.cc",
+    "category_rankers/click_based_category_ranker.h",
     "category_rankers/constant_category_ranker.cc",
     "category_rankers/constant_category_ranker.h",
     "category_status.cc",
@@ -126,6 +128,7 @@
   testonly = true
   sources = [
     "bookmarks/bookmark_last_visit_utils_unittest.cc",
+    "category_rankers/click_based_category_ranker_unittest.cc",
     "category_rankers/constant_category_ranker_unittest.cc",
     "category_unittest.cc",
     "content_suggestions_service_unittest.cc",
diff --git a/components/ntp_snippets/OWNERS b/components/ntp_snippets/OWNERS
index 5facc94e..d4be5146 100644
--- a/components/ntp_snippets/OWNERS
+++ b/components/ntp_snippets/OWNERS
@@ -8,3 +8,4 @@
 
 # Fallback, in case all of the above are OOO:
 markusheintz@chromium.org
+vitaliii@chromium.org
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
new file mode 100644
index 0000000..45da64b
--- /dev/null
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -0,0 +1,236 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
+
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+// In order to increase stability and predictability of the order, an extra
+// level of "confidence" is required before moving a category upwards. In other
+// words, the category is moved not when it reaches the previous one, but rather
+// when it leads by some amount. We refer to this required extra "confidence" as
+// a passing margin. Each position has its own passing margin. The category is
+// moved upwards (i.e. passes another category) when it has at least passing
+// margin of the previous category position more clicks.
+const int kPassingMargin = 5;
+
+// The first categories get more attention and, therefore, here more stability
+// is needed. The passing margin of such categories is increased and they are
+// referred to as top categories (with extra margin). Only category position
+// defines whether a category is top, but not its content.
+const int kNumTopCategoriesWithExtraMargin = 3;
+
+// The increase of passing margin for each top category compared to the next
+// category (e.g. the first top category has passing margin larger by this value
+// than the second top category, the last top category has it larger by this
+// value than the first non-top category).
+const int kExtraPassingMargin = 2;
+
+const char kCategoryIdKey[] = "category";
+const char kClicksKey[] = "clicks";
+
+}  // namespace
+
+ClickBasedCategoryRanker::ClickBasedCategoryRanker(PrefService* pref_service)
+    : pref_service_(pref_service) {
+  if (!ReadOrderFromPrefs(&ordered_categories_)) {
+    // TODO(crbug.com/676273): Handle adding new hardcoded KnownCategories to
+    // existing order from prefs. Currently such new category is completely
+    // ignored and may be never shown.
+    RestoreDefaultOrder();
+  }
+}
+
+ClickBasedCategoryRanker::~ClickBasedCategoryRanker() = default;
+
+bool ClickBasedCategoryRanker::Compare(Category left, Category right) const {
+  if (!ContainsCategory(left)) {
+    LOG(DFATAL) << "The category with ID " << left.id()
+                << " has not been added using AppendCategoryIfNecessary.";
+  }
+  if (!ContainsCategory(right)) {
+    LOG(DFATAL) << "The category with ID " << right.id()
+                << " has not been added using AppendCategoryIfNecessary.";
+  }
+  if (left == right) {
+    return false;
+  }
+  for (const RankedCategory& ranked_category : ordered_categories_) {
+    if (ranked_category.category == left) {
+      return true;
+    }
+    if (ranked_category.category == right) {
+      return false;
+    }
+  }
+  // This fallback is provided only to satisfy "Compare" contract if by mistake
+  // categories are not added using AppendCategoryIfNecessary. One should not
+  // rely on this, instead the order must be defined explicitly using
+  // AppendCategoryIfNecessary.
+  return left.id() < right.id();
+}
+
+void ClickBasedCategoryRanker::ClearHistory(base::Time begin, base::Time end) {
+  // TODO(crbug.com/675953): Preserve remote categories with 0 counts instead of
+  // removing them.
+  RestoreDefaultOrder();
+}
+
+void ClickBasedCategoryRanker::AppendCategoryIfNecessary(Category category) {
+  if (!ContainsCategory(category)) {
+    ordered_categories_.push_back(RankedCategory(category, /*clicks=*/0));
+  }
+}
+
+// TODO(vitaliii): Listen to dismissed sections and penalise them.
+void ClickBasedCategoryRanker::OnSuggestionOpened(Category category) {
+  if (!ContainsCategory(category)) {
+    LOG(DFATAL) << "The category with ID " << category.id()
+                << " has not been added using AppendCategoryIfNecessary.";
+    return;
+  }
+
+  std::vector<RankedCategory>::iterator current = FindCategory(category);
+  current->clicks++;
+
+  // TODO(crbug.com/676279): Prevent overflow.
+  if (current != ordered_categories_.begin()) {
+    std::vector<RankedCategory>::iterator previous = current - 1;
+    const int passing_margin = GetPositionPassingMargin(previous);
+    if (current->clicks >= previous->clicks + passing_margin) {
+      // It is intended to move only by one position at a time in order to avoid
+      // dramatic changes, which could confuse the user.
+      std::swap(*current, *previous);
+    }
+  }
+
+  StoreOrderToPrefs(ordered_categories_);
+}
+
+// static
+void ClickBasedCategoryRanker::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterListPref(prefs::kClickBasedCategoryRankerOrderWithClicks);
+}
+
+// static
+int ClickBasedCategoryRanker::GetPassingMargin() {
+  return kPassingMargin;
+}
+
+// static
+int ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin() {
+  return kNumTopCategoriesWithExtraMargin;
+}
+
+ClickBasedCategoryRanker::RankedCategory::RankedCategory(Category category,
+                                                         int clicks)
+    : category(category), clicks(clicks) {}
+
+// Returns passing margin for a given position taking into account whether it is
+// a top category.
+int ClickBasedCategoryRanker::GetPositionPassingMargin(
+    std::vector<RankedCategory>::const_iterator category_position) const {
+  int index = category_position - ordered_categories_.cbegin();
+  int passing_margin_increase = 0;
+  if (index < kNumTopCategoriesWithExtraMargin) {
+    passing_margin_increase =
+        kExtraPassingMargin * (kNumTopCategoriesWithExtraMargin - index);
+  }
+  return kPassingMargin + passing_margin_increase;
+}
+
+void ClickBasedCategoryRanker::RestoreDefaultOrder() {
+  ordered_categories_.clear();
+
+  std::vector<KnownCategories> ordered_known_categories =
+      ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+
+  for (KnownCategories known_category : ordered_known_categories) {
+    AppendKnownCategory(known_category);
+  }
+
+  StoreOrderToPrefs(ordered_categories_);
+}
+
+void ClickBasedCategoryRanker::AppendKnownCategory(
+    KnownCategories known_category) {
+  Category category = Category::FromKnownCategory(known_category);
+  DCHECK(!ContainsCategory(category));
+  ordered_categories_.push_back(RankedCategory(category, /*clicks=*/0));
+}
+
+bool ClickBasedCategoryRanker::ReadOrderFromPrefs(
+    std::vector<RankedCategory>* result_categories) {
+  result_categories->clear();
+  const base::ListValue* list =
+      pref_service_->GetList(prefs::kClickBasedCategoryRankerOrderWithClicks);
+  if (!list || list->GetSize() == 0) {
+    return false;
+  }
+
+  for (const std::unique_ptr<base::Value>& value : *list) {
+    base::DictionaryValue* dictionary;
+    if (!value->GetAsDictionary(&dictionary)) {
+      LOG(DFATAL) << "Failed to parse category data from prefs param "
+                  << prefs::kClickBasedCategoryRankerOrderWithClicks
+                  << " into dictionary.";
+      return false;
+    }
+    int category_id, clicks;
+    if (!dictionary->GetInteger(kCategoryIdKey, &category_id)) {
+      LOG(DFATAL) << "Dictionary does not have '" << kCategoryIdKey << "' key.";
+      return false;
+    }
+    if (!dictionary->GetInteger(kClicksKey, &clicks)) {
+      LOG(DFATAL) << "Dictionary does not have '" << kClicksKey << "' key.";
+      return false;
+    }
+    Category category = Category::FromIDValue(category_id);
+    result_categories->push_back(RankedCategory(category, clicks));
+  }
+  return true;
+}
+
+void ClickBasedCategoryRanker::StoreOrderToPrefs(
+    const std::vector<RankedCategory>& ordered_categories) {
+  base::ListValue list;
+  for (const RankedCategory& category : ordered_categories) {
+    auto dictionary = base::MakeUnique<base::DictionaryValue>();
+    dictionary->SetInteger(kCategoryIdKey, category.category.id());
+    dictionary->SetInteger(kClicksKey, category.clicks);
+    list.Append(std::move(dictionary));
+  }
+  pref_service_->Set(prefs::kClickBasedCategoryRankerOrderWithClicks, list);
+}
+
+std::vector<ClickBasedCategoryRanker::RankedCategory>::iterator
+ClickBasedCategoryRanker::FindCategory(Category category) {
+  return std::find_if(ordered_categories_.begin(), ordered_categories_.end(),
+                      [category](const RankedCategory& ranked_category) {
+                        return category == ranked_category.category;
+                      });
+}
+
+bool ClickBasedCategoryRanker::ContainsCategory(Category category) const {
+  for (const auto& ranked_category : ordered_categories_) {
+    if (category == ranked_category.category) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace ntp_snippets
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.h b/components/ntp_snippets/category_rankers/click_based_category_ranker.h
new file mode 100644
index 0000000..4b3a8a2
--- /dev/null
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.h
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ntp_snippets {
+
+// An implementation of a CategoryRanker based on a number of clicks per
+// category. Initial order is hardcoded, but sections with more clicks are moved
+// to the top. The new remote categories must be registered using
+// AppendCategoryIfNecessary. All other categories must be hardcoded in the
+// initial order. The order and category usage data are persisted in prefs and
+// reloaded on startup. TODO(crbug.com/675929): Remove unused categories from
+// prefs.
+class ClickBasedCategoryRanker : public CategoryRanker {
+ public:
+  explicit ClickBasedCategoryRanker(PrefService* pref_service);
+  ~ClickBasedCategoryRanker() override;
+
+  // CategoryRanker implementation.
+  bool Compare(Category left, Category right) const override;
+  void ClearHistory(base::Time begin, base::Time end) override;
+  void AppendCategoryIfNecessary(Category category) override;
+  void OnSuggestionOpened(Category category) override;
+
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  // Returns passing margin, i.e. a number of extra clicks required to move a
+  // category upwards. For testing only.
+  static int GetPassingMargin();
+
+  // Returns number of top categories with extra margin (i.e. with increased
+  // passing margin). For testing only.
+  static int GetNumTopCategoriesWithExtraMargin();
+
+ private:
+  struct RankedCategory {
+    Category category;
+    int clicks;
+
+    RankedCategory(Category category, int clicks);
+  };
+
+  int GetPositionPassingMargin(
+      std::vector<RankedCategory>::const_iterator category_position) const;
+  void RestoreDefaultOrder();
+  void AppendKnownCategory(KnownCategories known_category);
+  bool ReadOrderFromPrefs(std::vector<RankedCategory>* result_categories);
+  void StoreOrderToPrefs(const std::vector<RankedCategory>& ordered_categories);
+  std::vector<RankedCategory>::iterator FindCategory(Category category);
+  bool ContainsCategory(Category category) const;
+
+  std::vector<RankedCategory> ordered_categories_;
+  PrefService* pref_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRanker);
+};
+
+}  // namespace ntp_snippets
+
+#endif  // COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
new file mode 100644
index 0000000..fc1cb55
--- /dev/null
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+class ClickBasedCategoryRankerTest : public testing::Test {
+ public:
+  ClickBasedCategoryRankerTest()
+      : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()),
+        unused_remote_category_id_(
+            static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) {
+    ClickBasedCategoryRanker::RegisterProfilePrefs(pref_service_->registry());
+
+    ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(pref_service_.get());
+  }
+
+  int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; }
+
+  Category GetUnusedRemoteCategory() {
+    return Category::FromIDValue(GetUnusedRemoteCategoryID());
+  }
+
+  bool CompareCategories(const Category& left, const Category& right) {
+    return ranker()->Compare(left, right);
+  }
+
+  Category AddUnusedRemoteCategory() {
+    Category category = GetUnusedRemoteCategory();
+    ranker()->AppendCategoryIfNecessary(category);
+    return category;
+  }
+
+  void AddUnusedRemoteCategories(int quantity) {
+    for (int i = 0; i < quantity; ++i) {
+      AddUnusedRemoteCategory();
+    }
+  }
+
+  void ResetRanker() {
+    ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(pref_service_.get());
+  }
+
+  void NotifyOnSuggestionOpened(int times, Category category) {
+    for (int i = 0; i < times; ++i) {
+      ranker()->OnSuggestionOpened(category);
+    }
+  }
+
+  ClickBasedCategoryRanker* ranker() { return ranker_.get(); }
+
+ private:
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+  int unused_remote_category_id_;
+  std::unique_ptr<ClickBasedCategoryRanker> ranker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest);
+};
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) {
+  const Category first = GetUnusedRemoteCategory();
+  const Category second = GetUnusedRemoteCategory();
+  // Categories are added in decreasing id order to test that they are not
+  // compared by id.
+  ranker()->AppendCategoryIfNecessary(second);
+  ranker()->AppendCategoryIfNecessary(first);
+  EXPECT_TRUE(CompareCategories(second, first));
+  EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldSortLocalCategoriesBeforeRemote) {
+  const Category remote_category = AddUnusedRemoteCategory();
+  const Category local_category =
+      Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+  EXPECT_TRUE(CompareCategories(local_category, remote_category));
+  EXPECT_FALSE(CompareCategories(remote_category, local_category));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       CompareShouldReturnFalseForSameCategories) {
+  const Category remote_category = AddUnusedRemoteCategory();
+  EXPECT_FALSE(CompareCategories(remote_category, remote_category));
+
+  const Category local_category =
+      Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+  EXPECT_FALSE(CompareCategories(local_category, local_category));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       AddingMoreRemoteCategoriesShouldNotChangePreviousOrder) {
+  AddUnusedRemoteCategories(3);
+
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+
+  ASSERT_TRUE(CompareCategories(first, second));
+  ASSERT_FALSE(CompareCategories(second, first));
+
+  AddUnusedRemoteCategories(3);
+
+  EXPECT_TRUE(CompareCategories(first, second));
+  EXPECT_FALSE(CompareCategories(second, first));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldChangeOrderOfNonTopCategories) {
+  // Add dummy remote categories to ensure that the following categories are not
+  // in the top anymore.
+  AddUnusedRemoteCategories(
+      ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+
+  ASSERT_TRUE(CompareCategories(first, second));
+  ASSERT_FALSE(CompareCategories(second, first));
+
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+
+  EXPECT_TRUE(CompareCategories(second, first));
+  EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       ShouldNotChangeOrderRightAfterOrderChange) {
+  // Add dummy remote categories to ensure that the following categories are not
+  // in the top anymore.
+  AddUnusedRemoteCategories(
+      ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+  // Two non-top categories are added.
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+  ASSERT_TRUE(CompareCategories(first, second));
+  ASSERT_FALSE(CompareCategories(second, first));
+  // Their order is changed.
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+  ASSERT_TRUE(CompareCategories(second, first));
+  ASSERT_FALSE(CompareCategories(first, second));
+
+  // Click on the lower category.
+  NotifyOnSuggestionOpened(/*times=*/1, first);
+
+  // Order should not change.
+  EXPECT_TRUE(CompareCategories(second, first));
+  EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       ShouldNotMoveCategoryMoreThanOncePerClick) {
+  // Add dummy remote categories to ensure that the following categories are not
+  // in the top anymore.
+  AddUnusedRemoteCategories(
+      ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+  // Non-top categories are added.
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+  Category third = AddUnusedRemoteCategory();
+
+  // Move the third category up.
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
+  EXPECT_TRUE(CompareCategories(third, second));
+  // But only on one position even though the first category has low counts.
+  EXPECT_TRUE(CompareCategories(first, third));
+  // However, another click must move it further.
+  NotifyOnSuggestionOpened(/*times=*/1, third);
+  EXPECT_TRUE(CompareCategories(third, first));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       ShouldNotMoveTopCategoryRightAfterThreshold) {
+  ASSERT_GE(ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin(), 1);
+
+  // At least one top category is added from the default order.
+  std::vector<KnownCategories> default_order =
+      ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+  Category first = Category::FromKnownCategory(default_order[0]);
+  Category second = Category::FromKnownCategory(default_order[1]);
+
+  // Try to move the second category up as if the first category was non-top.
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+
+  // Nothing should change, because the first category is top.
+  EXPECT_TRUE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldPersistOrderAndClicksWhenRestarted) {
+  // Add dummy remote categories to ensure that the following categories are not
+  // in the top anymore.
+  AddUnusedRemoteCategories(
+      ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+  // Non-top categories are added.
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+  Category third = AddUnusedRemoteCategory();
+
+  // Change the order.
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
+  ASSERT_TRUE(CompareCategories(third, second));
+  ASSERT_TRUE(CompareCategories(first, third));
+
+  // Simulate Chrome restart.
+  ResetRanker();
+
+  // The old order must be preserved.
+  EXPECT_TRUE(CompareCategories(third, second));
+
+  // Clicks must be preserved as well.
+  NotifyOnSuggestionOpened(
+      /*times=*/1, third);
+  EXPECT_TRUE(CompareCategories(third, first));
+}
+
+}  // namespace ntp_snippets
diff --git a/components/ntp_snippets/category_rankers/constant_category_ranker.cc b/components/ntp_snippets/category_rankers/constant_category_ranker.cc
index 9fb648a7..859cf81 100644
--- a/components/ntp_snippets/category_rankers/constant_category_ranker.cc
+++ b/components/ntp_snippets/category_rankers/constant_category_ranker.cc
@@ -9,22 +9,11 @@
 namespace ntp_snippets {
 
 ConstantCategoryRanker::ConstantCategoryRanker() {
-  // Add all local categories in a fixed order.
-  AppendKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES);
-  AppendKnownCategory(KnownCategories::DOWNLOADS);
-  AppendKnownCategory(KnownCategories::RECENT_TABS);
-  AppendKnownCategory(KnownCategories::FOREIGN_TABS);
-  AppendKnownCategory(KnownCategories::BOOKMARKS);
-
-  DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT),
-            ordered_categories_.size());
-
-  // Known remote categories come after. Other remote categories will be ordered
-  // after these depending on when providers notify us about them using
-  // AppendCategoryIfNecessary.
-  // TODO(treib): Consider not adding ARTICLES here, so that providers can
-  // define the order themselves.
-  AppendKnownCategory(KnownCategories::ARTICLES);
+  std::vector<KnownCategories> ordered_known_categories =
+      GetKnownCategoriesDefaultOrder();
+  for (KnownCategories known_category : ordered_known_categories) {
+    AppendKnownCategory(known_category);
+  }
 }
 
 ConstantCategoryRanker::~ConstantCategoryRanker() = default;
@@ -71,6 +60,31 @@
   // Ignored. The order is constant.
 }
 
+// static
+std::vector<KnownCategories>
+ConstantCategoryRanker::GetKnownCategoriesDefaultOrder() {
+  std::vector<KnownCategories> categories;
+
+  // Add all local categories in a fixed order.
+  categories.push_back(KnownCategories::PHYSICAL_WEB_PAGES);
+  categories.push_back(KnownCategories::DOWNLOADS);
+  categories.push_back(KnownCategories::RECENT_TABS);
+  categories.push_back(KnownCategories::FOREIGN_TABS);
+  categories.push_back(KnownCategories::BOOKMARKS);
+
+  DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT),
+            categories.size());
+
+  // Known remote categories come after. Other remote categories will be ordered
+  // after these depending on when providers notify us about them using
+  // AppendCategoryIfNecessary.
+  // TODO(treib): Consider not adding ARTICLES here, so that providers can
+  // define the order themselves.
+  categories.push_back(KnownCategories::ARTICLES);
+
+  return categories;
+}
+
 void ConstantCategoryRanker::AppendKnownCategory(
     KnownCategories known_category) {
   Category category = Category::FromKnownCategory(known_category);
diff --git a/components/ntp_snippets/category_rankers/constant_category_ranker.h b/components/ntp_snippets/category_rankers/constant_category_ranker.h
index c01f0b9..6d2a143 100644
--- a/components/ntp_snippets/category_rankers/constant_category_ranker.h
+++ b/components/ntp_snippets/category_rankers/constant_category_ranker.h
@@ -29,6 +29,8 @@
   void AppendCategoryIfNecessary(Category category) override;
   void OnSuggestionOpened(Category category) override;
 
+  static std::vector<KnownCategories> GetKnownCategoriesDefaultOrder();
+
  private:
   void AppendKnownCategory(KnownCategories known_category);
 
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
index 15f1d02..2d054885 100644
--- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
+++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -242,10 +242,7 @@
 ContentSuggestion PhysicalWebPageSuggestionsProvider::ConvertPhysicalWebPage(
     const DictionaryValue& page) const {
   std::string scanned_url, raw_resolved_url, title, description;
-  int scan_timestamp;
   bool success = page.GetString(physical_web::kScannedUrlKey, &scanned_url);
-  success = page.GetInteger(physical_web::kScanTimestampKey, &scan_timestamp) &&
-            success;
   success = page.GetString(physical_web::kResolvedUrlKey, &raw_resolved_url) &&
             success;
   success = page.GetString(physical_web::kTitleKey, &title) && success;
@@ -260,10 +257,6 @@
                                resolved_url);
   DCHECK(base::IsStringUTF8(title));
   suggestion.set_title(base::UTF8ToUTF16(title));
-  // TODO(vitaliii): Set the time properly once the proper value is provided
-  // (see crbug.com/667722).
-  suggestion.set_publish_date(
-      base::Time::FromTimeT(static_cast<time_t>(scan_timestamp)));
   suggestion.set_publisher_name(base::UTF8ToUTF16(resolved_url.host()));
   DCHECK(base::IsStringUTF8(description));
   suggestion.set_snippet_text(base::UTF8ToUTF16(description));
diff --git a/components/ntp_snippets/pref_names.cc b/components/ntp_snippets/pref_names.cc
index fbf5505..dced63df 100644
--- a/components/ntp_snippets/pref_names.cc
+++ b/components/ntp_snippets/pref_names.cc
@@ -61,5 +61,8 @@
 const char kUserClassifierLastTimeToUseSuggestions[] =
     "ntp_suggestions.user_classifier.last_time_to_use_suggestions";
 
+const char kClickBasedCategoryRankerOrderWithClicks[] =
+    "ntp_suggestions.click_based_category_ranker.category_order_with_clicks";
+
 }  // namespace prefs
 }  // namespace ntp_snippets
diff --git a/components/ntp_snippets/pref_names.h b/components/ntp_snippets/pref_names.h
index c0ddda0..5ff759f 100644
--- a/components/ntp_snippets/pref_names.h
+++ b/components/ntp_snippets/pref_names.h
@@ -69,6 +69,9 @@
 // The pref name for the last time content suggestions were used by the user.
 extern const char kUserClassifierLastTimeToUseSuggestions[];
 
+// The pref name for the current order of categories and their clicks.
+extern const char kClickBasedCategoryRankerOrderWithClicks[];
+
 }  // namespace prefs
 }  // namespace ntp_snippets
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 7ca12a6..7938dd0 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -139,7 +139,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 357
+#   For your editing convenience: highest ID currently used: 359
 #
 # Placeholders:
 #   The following placeholder strings are automatically substituted:
@@ -1474,6 +1474,52 @@
       'arc_support': 'Disabling Google Sync will cause Android Backup and Restore to not function properly.',
     },
     {
+      'name': 'RoamingProfileSupportEnabled',
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.win:57-'],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': False,
+      },
+      'example_value': True,
+      'id': 358,
+      'caption': '''Enable the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data.''',
+      'tags': ['local-data-access'],
+      'desc': '''Enables the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data.
+
+      If you enable this setting, the settings stored in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profiles like bookmarks, autofill data, passwords, etc. will also be written to a file stored in the Roaming user profile folder or a location specified by the Administrator through the <ph name="ROAMINGPROFILELOCATION_POLICY_NAME">$1<ex>RoamingProfileLocation</ex></ph> policy.
+
+      Google Sync is automatically disabled when this policy is enabled.
+
+      To prevent accidental data leaks it is advisable to also set the <ph name="SIGNINALLOWED_POLICY_NAME">$1<ex>SigninAllowed</ex></ph> policy to False.
+
+      If this policy is disabled or left not set only the regular local profiles will be used.''',
+      'label': '''Enable the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data.''',
+    },
+    {
+      'name': 'RoamingProfileLocation',
+      'type': 'string',
+      'schema': { 'type': 'string' },
+      'supported_on': ['chrome.win:57-'],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': False,
+      },
+      'example_value': '${roaming_app_data}\\chrome-profile',
+      'id': 359,
+      'caption': '''Set the roaming profile directory''',
+      'tags': ['local-data-access'],
+      'desc': '''Configures the directory that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use for storing the roaming copy of the profiles.
+
+      If you set this policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use the provided directory to store the roaming copy of the profiles if the <ph name="ROAMINGPROFILESUPPORTENABLED_POLICY_NAME">$1<ex>RoamingProfileSupportEnabled</ex></ph> policy has been enabled. If the <ph name="ROAMINGPROFILESUPPORTENABLED_POLICY_NAME">$1<ex>RoamingProfileSupportEnabled</ex></ph> policy is disabled or left unset the value stored in this policy is not used.
+
+      See https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables for a list of variables that can be used.
+
+      If this policy is left not set the default roaming profile path will be used.''',
+      'label': '''Set the roaming profile directory''',
+    },
+    {
       'name': 'SigninAllowed',
       'type': 'main',
       'schema': { 'type': 'boolean' },
@@ -8060,7 +8106,7 @@
       'type': 'main',
       'future': True,
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.*:55-'],
+      'supported_on': ['chrome.*:55-', 'android:57-'],
       'features': {
         'dynamic_refresh': False,
         'per_profile': False,
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 328871f5..4d525b1 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -171,9 +171,11 @@
   if (used_policy_installed_certificate)
     return SECURE_WITH_POLICY_INSTALLED_CERT;
 
-  if (sha1_status == DEPRECATED_SHA1_MAJOR)
-    return DANGEROUS;
-  if (sha1_status == DEPRECATED_SHA1_MINOR)
+  // In most cases, SHA1 use is treated as a certificate error, in which case
+  // DANGEROUS will have been returned above. If SHA1 is permitted, we downgrade
+  // the security level.
+  if (sha1_status == DEPRECATED_SHA1_MAJOR ||
+      sha1_status == DEPRECATED_SHA1_MINOR)
     return NONE;
 
   // Active mixed content is handled above.
diff --git a/components/signin/core/account_id/account_id.cc b/components/signin/core/account_id/account_id.cc
index 00ceb2e2..1ffac80d 100644
--- a/components/signin/core/account_id/account_id.cc
+++ b/components/signin/core/account_id/account_id.cc
@@ -16,25 +16,20 @@
 
 namespace {
 
-// Known account types.
-const char kGoogle[] = "google";
-
 // Serialization keys
 const char kGaiaIdKey[] = "gaia_id";
 const char kEmailKey[] = "email";
+const char kObjGuid[] = "obj_guid";
+const char kAccountTypeKey[] = "account_type";
+
+// Serialization values for account type.
+const char kGoogle[] = "google";
+const char kAd[] = "ad";
+const char kUnknown[] = "unknown";
 
 // Prefix for GetAccountIdKey().
 const char kKeyGaiaIdPrefix[] = "g-";
-
-struct GoogleStringSingleton {
-  GoogleStringSingleton() : google(kGoogle) {}
-
-  static GoogleStringSingleton* GetInstance() {
-    return base::Singleton<GoogleStringSingleton>::get();
-  }
-
-  const std::string google;
-};
+const char kKeyAdIdPrefix[] = "a-";
 
 }  // anonymous namespace
 
@@ -49,26 +44,46 @@
 
 AccountId::AccountId() {}
 
-AccountId::AccountId(const std::string& gaia_id, const std::string& user_email)
-    : gaia_id_(gaia_id), user_email_(user_email) {
+AccountId::AccountId(const std::string& id,
+                     const std::string& user_email,
+                     const AccountType& account_type)
+    : id_(id), user_email_(user_email), account_type_(account_type) {
+  DCHECK(account_type != AccountType::UNKNOWN || id.empty());
+  DCHECK(account_type != AccountType::ACTIVE_DIRECTORY || !id.empty());
   // Fail if e-mail looks similar to GaiaIdKey.
   LOG_ASSERT(!base::StartsWith(user_email, kKeyGaiaIdPrefix,
                                base::CompareCase::SENSITIVE) ||
              user_email.find('@') != std::string::npos)
-      << "Bad e-mail: '" << user_email << "' with gaia_id='" << gaia_id << "'";
+      << "Bad e-mail: '" << user_email << "' with gaia_id='" << id << "'";
 
   // TODO(alemate): DCHECK(!email.empty());
   // TODO(alemate): check gaia_id is not empty once it is required.
 }
 
 AccountId::AccountId(const AccountId& other)
-    : gaia_id_(other.gaia_id_), user_email_(other.user_email_) {}
+    : id_(other.id_),
+      user_email_(other.user_email_),
+      account_type_(other.account_type_) {}
 
 bool AccountId::operator==(const AccountId& other) const {
-  return (this == &other) ||
-         (gaia_id_ == other.gaia_id_ && user_email_ == other.user_email_) ||
-         (!gaia_id_.empty() && gaia_id_ == other.gaia_id_) ||
-         (!user_email_.empty() && user_email_ == other.user_email_);
+  if (this == &other)
+    return true;
+  if (account_type_ == AccountType::UNKNOWN ||
+      other.account_type_ == AccountType::UNKNOWN)
+    return user_email_ == other.user_email_;
+  if (account_type_ != other.account_type_)
+    return false;
+  switch (account_type_) {
+    case AccountType::GOOGLE:
+      return (id_ == other.id_ && user_email_ == other.user_email_) ||
+             (!id_.empty() && id_ == other.id_) ||
+             (!user_email_.empty() && user_email_ == other.user_email_);
+    case AccountType::ACTIVE_DIRECTORY:
+      return id_ == other.id_ && user_email_ == other.user_email_;
+    default:
+      NOTREACHED() << "Unknown account type";
+  }
+  return false;
 }
 
 bool AccountId::operator!=(const AccountId& other) const {
@@ -81,45 +96,70 @@
 }
 
 bool AccountId::empty() const {
-  return gaia_id_.empty() && user_email_.empty();
+  return id_.empty() && user_email_.empty() &&
+         account_type_ == AccountType::UNKNOWN;
 }
 
 bool AccountId::is_valid() const {
-  return /* !gaia_id_.empty() && */ !user_email_.empty();
+  switch (account_type_) {
+    case AccountType::GOOGLE:
+      return /* !id_.empty() && */ !user_email_.empty();
+    case AccountType::ACTIVE_DIRECTORY:
+      return !id_.empty() && !user_email_.empty();
+    case AccountType::UNKNOWN:
+      return id_.empty() && !user_email_.empty();
+  }
+  NOTREACHED();
+  return false;
 }
 
 void AccountId::clear() {
-  gaia_id_.clear();
+  id_.clear();
   user_email_.clear();
+  account_type_ = AccountType::UNKNOWN;
 }
 
-const std::string& AccountId::GetAccountType() const {
-  return GoogleStringSingleton::GetInstance()->google;
+AccountType AccountId::GetAccountType() const {
+  return account_type_;
 }
 
 const std::string& AccountId::GetGaiaId() const {
-  return gaia_id_;
+  if (account_type_ != AccountType::GOOGLE)
+    NOTIMPLEMENTED() << "Failed to get gaia_id for non-Google account.";
+  return id_;
+}
+
+const std::string& AccountId::GetObjGuid() const {
+  if (account_type_ != AccountType::ACTIVE_DIRECTORY)
+    NOTIMPLEMENTED()
+        << "Failed to get obj_guid for non-Active Directory account.";
+  return id_;
 }
 
 const std::string& AccountId::GetUserEmail() const {
   return user_email_;
 }
 
-const std::string AccountId::GetAccountIdKey() const {
-#ifdef NDEBUG
-  if (gaia_id_.empty())
-    LOG(FATAL) << "GetAccountIdKey(): no gaia id for " << Serialize();
-
-#else
-  CHECK(!gaia_id_.empty());
-#endif
-
-  return std::string(kKeyGaiaIdPrefix) + gaia_id_;
+bool AccountId::HasAccountIdKey() const {
+  return account_type_ != AccountType::UNKNOWN && !id_.empty();
 }
 
-void AccountId::SetGaiaId(const std::string& gaia_id) {
-  DCHECK(!gaia_id.empty());
-  gaia_id_ = gaia_id;
+const std::string AccountId::GetAccountIdKey() const {
+#ifdef NDEBUG
+  if (id_.empty())
+    LOG(FATAL) << "GetAccountIdKey(): no id for " << Serialize();
+#else
+  CHECK(!id_.empty());
+#endif
+  switch (GetAccountType()) {
+    case AccountType::GOOGLE:
+      return std::string(kKeyGaiaIdPrefix) + id_;
+    case AccountType::ACTIVE_DIRECTORY:
+      return std::string(kKeyAdIdPrefix) + id_;
+    default:
+      NOTREACHED() << "Unknown account type";
+  }
+  return std::string();
 }
 
 void AccountId::SetUserEmail(const std::string& email) {
@@ -130,24 +170,75 @@
 // static
 AccountId AccountId::FromUserEmail(const std::string& email) {
   // TODO(alemate): DCHECK(!email.empty());
-  return AccountId(std::string() /* gaia_id */, email);
+  return AccountId(std::string() /* id */, email, AccountType::UNKNOWN);
 }
 
+// static
 AccountId AccountId::FromGaiaId(const std::string& gaia_id) {
   DCHECK(!gaia_id.empty());
-  return AccountId(gaia_id, std::string() /* email */);
+  return AccountId(gaia_id, std::string() /* email */, AccountType::GOOGLE);
 }
 
 // static
 AccountId AccountId::FromUserEmailGaiaId(const std::string& email,
                                          const std::string& gaia_id) {
   DCHECK(!(email.empty() && gaia_id.empty()));
-  return AccountId(gaia_id, email);
+  return AccountId(gaia_id, email, AccountType::GOOGLE);
+}
+
+// static
+AccountId AccountId::AdFromUserEmailObjGuid(const std::string& email,
+                                            const std::string& obj_guid) {
+  DCHECK(!email.empty() && !obj_guid.empty());
+  return AccountId(obj_guid, email, AccountType::ACTIVE_DIRECTORY);
+}
+
+// static
+AccountId AccountId::AdFromObjGuid(const std::string& obj_guid) {
+  DCHECK(!obj_guid.empty());
+  return AccountId(obj_guid, std::string() /* email */,
+                   AccountType::ACTIVE_DIRECTORY);
+}
+
+// static
+AccountType AccountId::StringToAccountType(
+    const std::string& account_type_string) {
+  if (account_type_string == kGoogle)
+    return AccountType::GOOGLE;
+  if (account_type_string == kAd)
+    return AccountType::ACTIVE_DIRECTORY;
+  if (account_type_string == kUnknown)
+    return AccountType::UNKNOWN;
+  NOTREACHED() << "Unknown account type " << account_type_string;
+  return AccountType::UNKNOWN;
+}
+
+// static
+std::string AccountId::AccountTypeToString(const AccountType& account_type) {
+  switch (account_type) {
+    case AccountType::GOOGLE:
+      return kGoogle;
+    case AccountType::ACTIVE_DIRECTORY:
+      return kAd;
+    case AccountType::UNKNOWN:
+      return kUnknown;
+  }
+  return std::string();
 }
 
 std::string AccountId::Serialize() const {
   base::DictionaryValue value;
-  value.SetString(kGaiaIdKey, gaia_id_);
+  switch (GetAccountType()) {
+    case AccountType::GOOGLE:
+      value.SetString(kGaiaIdKey, id_);
+      break;
+    case AccountType::ACTIVE_DIRECTORY:
+      value.SetString(kObjGuid, id_);
+      break;
+    case AccountType::UNKNOWN:
+      break;
+  }
+  value.SetString(kAccountTypeKey, AccountTypeToString(GetAccountType()));
   value.SetString(kEmailKey, user_email_);
 
   std::string serialized;
@@ -167,23 +258,65 @@
 
   std::string gaia_id;
   std::string user_email;
+  std::string obj_guid;
+  std::string account_type_string;
+  AccountType account_type = AccountType::GOOGLE;
 
   const bool found_gaia_id = dictionary_value->GetString(kGaiaIdKey, &gaia_id);
   const bool found_user_email =
       dictionary_value->GetString(kEmailKey, &user_email);
+  const bool found_obj_guid = dictionary_value->GetString(kObjGuid, &obj_guid);
+  const bool found_account_type =
+      dictionary_value->GetString(kAccountTypeKey, &account_type_string);
+  if (found_account_type)
+    account_type = StringToAccountType(account_type_string);
 
-  if (!found_gaia_id)
-    LOG(ERROR) << "gaia_id is not found in '" << serialized << "'";
+  switch (account_type) {
+    case AccountType::GOOGLE:
+      if (found_obj_guid)
+        DLOG(ERROR) << "AccountType is 'google' but obj_guid is found in '"
+                    << serialized << "'";
 
-  if (!found_user_email)
-    LOG(ERROR) << "user_email is not found in '" << serialized << "'";
+      if (!found_gaia_id)
+        DLOG(ERROR) << "gaia_id is not found in '" << serialized << "'";
 
-  if (!found_gaia_id && !found_user_email)
-    return false;
+      if (!found_user_email)
+        DLOG(ERROR) << "user_email is not found in '" << serialized << "'";
 
-  *account_id = FromUserEmailGaiaId(user_email, gaia_id);
+      if (!found_gaia_id && !found_user_email)
+        return false;
 
-  return true;
+      *account_id = FromUserEmailGaiaId(user_email, gaia_id);
+      return true;
+
+    case AccountType::ACTIVE_DIRECTORY:
+      if (found_gaia_id)
+        DLOG(ERROR)
+            << "AccountType is 'active directory' but gaia_id is found in '"
+            << serialized << "'";
+
+      if (!found_obj_guid) {
+        DLOG(ERROR) << "obj_guid is not found in '" << serialized << "'";
+        return false;
+      }
+
+      if (!found_user_email) {
+        DLOG(ERROR) << "user_email is not found in '" << serialized << "'";
+      }
+
+      if (!found_obj_guid || !found_user_email)
+        return false;
+
+      *account_id = AdFromUserEmailObjGuid(user_email, obj_guid);
+      return true;
+
+    case AccountType::UNKNOWN:
+      if (!found_user_email)
+        return false;
+      *account_id = FromUserEmail(user_email);
+      return true;
+  }
+  return false;
 }
 
 const AccountId& EmptyAccountId() {
diff --git a/components/signin/core/account_id/account_id.h b/components/signin/core/account_id/account_id.h
index 7cba697..4bc0205 100644
--- a/components/signin/core/account_id/account_id.h
+++ b/components/signin/core/account_id/account_id.h
@@ -10,18 +10,28 @@
 #include <string>
 #include "base/containers/hash_tables.h"
 
+enum class AccountType { UNKNOWN, GOOGLE, ACTIVE_DIRECTORY };
+
 // Type that contains enough information to identify user.
 //
 // TODO(alemate): we are in the process of moving away from std::string as a
 // type for storing user identifier to AccountId. At this time GaiaId is mostly
 // empty, so this type is used as a replacement for e-mail string.
 // But in near future AccountId will become full feature user identifier.
+// TODO(alemate): Rename functions and fields to reflect different types of
+// accounts. (see crbug.com/672253)
 class AccountId {
  public:
   struct EmptyAccountId;
 
   AccountId(const AccountId& other);
 
+  // If any of the comparable AccountIds has AccountType == UNKNOWN then it
+  // compares emails.
+  // If both are not UNKNOWN and not equal then it returns false.
+  // If AccountType == GOOGLE then it checks if either ids or emails are equal.
+  // If AccountType == ACTIVE_DIRECTORY then it checks if ids and emails are
+  // equal.
   bool operator==(const AccountId& other) const;
   bool operator!=(const AccountId& other) const;
   bool operator<(const AccountId& right) const;
@@ -31,28 +41,45 @@
   bool is_valid() const;
   void clear();
 
-  const std::string& GetAccountType() const;
+  AccountType GetAccountType() const;
   const std::string& GetGaiaId() const;
+  const std::string& GetObjGuid() const;
   // Users of AccountId should make no assumptions on the format of email.
   // I.e. it cannot be used as account identifier, because it is (in general)
   // non-comparable.
   const std::string& GetUserEmail() const;
 
+  // Returns true if |GetAccountIdKey| would return valid key.
+  bool HasAccountIdKey() const;
   // This returns prefixed some string that can be used as a storage key.
   // You should make no assumptions on the format of this string.
   const std::string GetAccountIdKey() const;
 
-  void SetGaiaId(const std::string& gaia_id);
   void SetUserEmail(const std::string& email);
 
   // This method is to be used during transition period only.
+  // AccountId with UNKNOWN AccountType;
   static AccountId FromUserEmail(const std::string& user_email);
+  // AccountId with GOOGLE AccountType;
   // This method is to be used during transition period only.
   static AccountId FromGaiaId(const std::string& gaia_id);
   // This method is the preferred way to construct AccountId if you have
   // full account information.
+  // AccountId with GOOGLE AccountType;
   static AccountId FromUserEmailGaiaId(const std::string& user_email,
                                        const std::string& gaia_id);
+  // These methods are used to construct Active Directory AccountIds.
+  // AccountId with ACTIVE_DIRECTORY AccountType;
+  static AccountId AdFromUserEmailObjGuid(const std::string& email,
+                                          const std::string& obj_guid);
+  // AccountId with ACTIVE_DIRECTORY AccountType;
+  static AccountId AdFromObjGuid(const std::string& obj_guid);
+
+  // Translation functions between AccountType and std::string. Used for
+  // serialization.
+  static AccountType StringToAccountType(
+      const std::string& account_type_string);
+  static std::string AccountTypeToString(const AccountType& account_type);
 
   // These are (for now) unstable and cannot be used to store serialized data to
   // persistent storage. Only in-memory storage is safe.
@@ -64,10 +91,13 @@
 
  private:
   AccountId();
-  AccountId(const std::string& gaia_id, const std::string& user_email);
+  AccountId(const std::string& id,
+            const std::string& user_email,
+            const AccountType& account_type);
 
-  std::string gaia_id_;
+  std::string id_;
   std::string user_email_;
+  AccountType account_type_ = AccountType::UNKNOWN;
 };
 
 // Returns a reference to a singleton.
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index 8ad90614..815d105e 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -32,6 +32,12 @@
 // Key of obfuscated GAIA id value.
 const char kGAIAIdKey[] = "gaia_id";
 
+// Key of obfuscated object guid value for Active Directory accounts.
+const char kObjGuidKey[] = "obj_guid";
+
+// Key of account type.
+const char kAccountTypeKey[] = "account_type";
+
 // Key of whether this user ID refers to a SAML user.
 const char kUsingSAMLKey[] = "using_saml";
 
@@ -58,11 +64,29 @@
 bool UserMatches(const AccountId& account_id,
                  const base::DictionaryValue& dict) {
   std::string value;
+  if (account_id.GetAccountType() != AccountType::UNKNOWN &&
+      dict.GetString(kAccountTypeKey, &value) &&
+      account_id.GetAccountType() != AccountId::StringToAccountType(value)) {
+    return false;
+  }
 
   // TODO(alemate): update code once user id is really a struct.
-  bool has_gaia_id = dict.GetString(kGAIAIdKey, &value);
-  if (has_gaia_id && account_id.GetGaiaId() == value)
-    return true;
+  switch (account_id.GetAccountType()) {
+    case AccountType::GOOGLE: {
+      bool has_gaia_id = dict.GetString(kGAIAIdKey, &value);
+      if (has_gaia_id && account_id.GetGaiaId() == value)
+        return true;
+      break;
+    }
+    case AccountType::ACTIVE_DIRECTORY: {
+      bool has_obj_guid = dict.GetString(kObjGuidKey, &value);
+      if (has_obj_guid && account_id.GetObjGuid() == value)
+        return true;
+      break;
+    }
+    case AccountType::UNKNOWN: {
+    }
+  }
 
   bool has_email = dict.GetString(kCanonicalEmail, &value);
   if (has_email && account_id.GetUserEmail() == value)
@@ -76,8 +100,20 @@
   if (!account_id.GetUserEmail().empty())
     dict.SetString(kCanonicalEmail, account_id.GetUserEmail());
 
-  if (!account_id.GetGaiaId().empty())
-    dict.SetString(kGAIAIdKey, account_id.GetGaiaId());
+  switch (account_id.GetAccountType()) {
+    case AccountType::GOOGLE:
+      if (!account_id.GetGaiaId().empty())
+        dict.SetString(kGAIAIdKey, account_id.GetGaiaId());
+      break;
+    case AccountType::ACTIVE_DIRECTORY:
+      if (!account_id.GetObjGuid().empty())
+        dict.SetString(kObjGuidKey, account_id.GetObjGuid());
+      break;
+    case AccountType::UNKNOWN:
+      return;
+  }
+  dict.SetString(kAccountTypeKey,
+                 AccountId::AccountTypeToString(account_id.GetAccountType()));
 }
 
 }  // namespace
@@ -216,48 +252,76 @@
 }
 
 AccountId GetAccountId(const std::string& user_email,
-                       const std::string& gaia_id) {
+                       const std::string& id,
+                       const AccountType& account_type) {
+  DCHECK((id.empty() && account_type == AccountType::UNKNOWN) ||
+         (!id.empty() && account_type != AccountType::UNKNOWN));
   // In tests empty accounts are possible.
-  if (user_email.empty() && gaia_id.empty())
+  if (user_email.empty() && id.empty() &&
+      account_type == AccountType::UNKNOWN) {
     return EmptyAccountId();
+  }
 
   AccountId result(EmptyAccountId());
   // UserManager is usually NULL in unit tests.
-  if (UserManager::IsInitialized() &&
-      UserManager::Get()->GetPlatformKnownUserId(user_email, gaia_id,
-                                                 &result)) {
+  if (account_type == AccountType::UNKNOWN && UserManager::IsInitialized() &&
+      UserManager::Get()->GetPlatformKnownUserId(user_email, id, &result)) {
     return result;
   }
 
-  // We can have several users with the same gaia_id but different e-mails.
-  // The opposite case is not possible.
   std::string stored_gaia_id;
+  std::string stored_obj_guid;
   const std::string sanitized_email =
       user_email.empty()
           ? std::string()
           : gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email));
 
-  if (!sanitized_email.empty() &&
-      GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey,
-                    &stored_gaia_id)) {
-    if (!gaia_id.empty() && gaia_id != stored_gaia_id)
-      LOG(ERROR) << "User gaia id has changed. Sync will not work.";
+  if (!sanitized_email.empty()) {
+    if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey,
+                      &stored_gaia_id)) {
+      if (!id.empty()) {
+        DCHECK(account_type == AccountType::GOOGLE);
+        if (id != stored_gaia_id)
+          LOG(ERROR) << "User gaia id has changed. Sync will not work.";
+      }
 
-    // gaia_id is associated with cryptohome.
-    return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id);
+      // gaia_id is associated with cryptohome.
+      return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id);
+    }
+
+    if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kObjGuidKey,
+                      &stored_obj_guid)) {
+      if (!id.empty()) {
+        DCHECK(account_type == AccountType::ACTIVE_DIRECTORY);
+        if (id != stored_obj_guid)
+          LOG(ERROR) << "User object guid has changed. Sync will not work.";
+      }
+
+      // obj_guid is associated with cryptohome.
+      return AccountId::AdFromUserEmailObjGuid(sanitized_email,
+                                               stored_obj_guid);
+    }
   }
 
   std::string stored_email;
-  // GetStringPref() returns the first user record that matches
-  // given ID. So we will get the first one if there are multiples.
-  if (!gaia_id.empty() && GetStringPref(AccountId::FromGaiaId(gaia_id),
-                                        kCanonicalEmail, &stored_email)) {
-    return AccountId::FromUserEmailGaiaId(stored_email, gaia_id);
+  switch (account_type) {
+    case AccountType::GOOGLE:
+      if (GetStringPref(AccountId::FromGaiaId(id), kCanonicalEmail,
+                        &stored_email)) {
+        return AccountId::FromUserEmailGaiaId(stored_email, id);
+      }
+      return AccountId::FromUserEmailGaiaId(sanitized_email, id);
+    case AccountType::ACTIVE_DIRECTORY:
+      if (GetStringPref(AccountId::AdFromObjGuid(id), kCanonicalEmail,
+                        &stored_email)) {
+        return AccountId::AdFromUserEmailObjGuid(stored_email, id);
+      }
+      return AccountId::AdFromUserEmailObjGuid(sanitized_email, id);
+    case AccountType::UNKNOWN:
+      return AccountId::FromUserEmail(sanitized_email);
   }
-
-  return (gaia_id.empty()
-              ? AccountId::FromUserEmail(user_email)
-              : AccountId::FromUserEmailGaiaId(user_email, gaia_id));
+  NOTREACHED();
+  return EmptyAccountId();
 }
 
 std::vector<AccountId> GetKnownAccountIds() {
@@ -274,10 +338,30 @@
     if (known_users->GetDictionary(i, &element)) {
       std::string email;
       std::string gaia_id;
+      std::string obj_guid;
       const bool has_email = element->GetString(kCanonicalEmail, &email);
       const bool has_gaia_id = element->GetString(kGAIAIdKey, &gaia_id);
-      if (has_email || has_gaia_id)
-        result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id));
+      const bool has_obj_guid = element->GetString(kObjGuidKey, &obj_guid);
+      AccountType account_type = AccountType::GOOGLE;
+      std::string account_type_string;
+      if (element->GetString(kAccountTypeKey, &account_type_string)) {
+        account_type = AccountId::StringToAccountType(account_type_string);
+      }
+      switch (account_type) {
+        case AccountType::GOOGLE:
+          if (has_email || has_gaia_id) {
+            result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id));
+          }
+          break;
+        case AccountType::ACTIVE_DIRECTORY:
+          if (has_email && has_obj_guid) {
+            result.push_back(
+                AccountId::AdFromUserEmailObjGuid(email, obj_guid));
+          }
+          break;
+        default:
+          NOTREACHED() << "Unknown account type";
+      }
     }
   }
   return result;
@@ -304,6 +388,23 @@
 
 void UpdateGaiaID(const AccountId& account_id, const std::string& gaia_id) {
   SetStringPref(account_id, kGAIAIdKey, gaia_id);
+  SetStringPref(account_id, kAccountTypeKey,
+                AccountId::AccountTypeToString(AccountType::GOOGLE));
+}
+
+void UpdateId(const AccountId& account_id) {
+  switch (account_id.GetAccountType()) {
+    case AccountType::GOOGLE:
+      SetStringPref(account_id, kGAIAIdKey, account_id.GetGaiaId());
+      break;
+    case AccountType::ACTIVE_DIRECTORY:
+      SetStringPref(account_id, kObjGuidKey, account_id.GetObjGuid());
+      break;
+    case AccountType::UNKNOWN:
+      return;
+  }
+  SetStringPref(account_id, kAccountTypeKey,
+                AccountId::AccountTypeToString(account_id.GetAccountType()));
 }
 
 bool FindGaiaID(const AccountId& account_id, std::string* out_value) {
diff --git a/components/user_manager/known_user.h b/components/user_manager/known_user.h
index c0dbd9cd..245ed57 100644
--- a/components/user_manager/known_user.h
+++ b/components/user_manager/known_user.h
@@ -11,6 +11,7 @@
 #include "components/user_manager/user_manager_export.h"
 
 class AccountId;
+enum class AccountType;
 class PrefRegistrySimple;
 
 namespace base {
@@ -74,7 +75,8 @@
 // gaia_id.
 // This is a temporary call while migrating to AccountId.
 AccountId USER_MANAGER_EXPORT GetAccountId(const std::string& user_email,
-                                           const std::string& gaia_id);
+                                           const std::string& id,
+                                           const AccountType& account_type);
 
 // Returns true if |subsystem| data was migrated to GaiaId for the |account_id|.
 bool USER_MANAGER_EXPORT GetGaiaIdMigrationStatus(const AccountId& account_id,
@@ -91,6 +93,10 @@
 void USER_MANAGER_EXPORT UpdateGaiaID(const AccountId& account_id,
                                       const std::string& gaia_id);
 
+// Updates |account_id.account_type_| and |account_id.GetGaiaId()| or
+// |account_id.GetObjGuid()| for user with |account_id|.
+void USER_MANAGER_EXPORT UpdateId(const AccountId& account_id);
+
 // Find GAIA ID for user with |account_id|, fill in |out_value| and return
 // true
 // if GAIA ID was found or false otherwise.
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 6710f25d..24772d6b 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -494,7 +494,8 @@
       continue;
     }
 
-    const AccountId account_id = known_user::GetAccountId(email, std::string());
+    const AccountId account_id = known_user::GetAccountId(
+        email, std::string() /* id */, AccountType::UNKNOWN);
 
     if (existing_users.find(account_id) != existing_users.end() ||
         !users_set->insert(account_id).second) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3e1a862b..f619693 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -77,8 +77,6 @@
     "//device/power_save_blocker",
     "//device/screen_orientation/public/interfaces",
     "//device/sensors/public/cpp",
-    "//device/time_zone_monitor",
-    "//device/time_zone_monitor/public/interfaces",
     "//device/vibration",
     "//device/wake_lock",
     "//gin",
@@ -107,7 +105,7 @@
     "//printing/features",
     "//services/catalog/public/interfaces:constants",
     "//services/device:lib",
-    "//services/device/public/cpp",
+    "//services/device/public/interfaces",
     "//services/file:lib",
     "//services/file/public/interfaces",
     "//services/service_manager",
@@ -393,11 +391,14 @@
     "cache_storage/cache_storage_cache.h",
     "cache_storage/cache_storage_cache_handle.cc",
     "cache_storage/cache_storage_cache_handle.h",
+    "cache_storage/cache_storage_cache_observer.h",
     "cache_storage/cache_storage_context_impl.cc",
     "cache_storage/cache_storage_context_impl.h",
     "cache_storage/cache_storage_dispatcher_host.cc",
     "cache_storage/cache_storage_dispatcher_host.h",
     "cache_storage/cache_storage_histogram_macros.h",
+    "cache_storage/cache_storage_index.cc",
+    "cache_storage/cache_storage_index.h",
     "cache_storage/cache_storage_manager.cc",
     "cache_storage/cache_storage_manager.h",
     "cache_storage/cache_storage_operation.cc",
@@ -527,6 +528,8 @@
     "dom_storage/dom_storage_session.h",
     "dom_storage/dom_storage_task_runner.cc",
     "dom_storage/dom_storage_task_runner.h",
+    "dom_storage/local_storage_context_mojo.cc",
+    "dom_storage/local_storage_context_mojo.h",
     "dom_storage/local_storage_database_adapter.cc",
     "dom_storage/local_storage_database_adapter.h",
     "dom_storage/session_storage_database.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index c4c944f..e544256 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -25,7 +25,6 @@
   "+device/power_save_blocker",
   "+device/screen_orientation/public/interfaces",
   "+device/sensors/public",
-  "+device/time_zone_monitor",  # For TimeZoneMonitor creation.
   "+device/vibration",  # For Vibration API
   "+device/vr",  # For WebVR API
   "+device/wake_lock",
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 30ff8d4..1e7f2c3 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -40,7 +40,7 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "services/device/device_service.h"
-#include "services/device/public/cpp/constants.h"
+#include "services/device/public/interfaces/constants.mojom.h"
 #include "services/file/file_service.h"
 #include "services/file/public/interfaces/constants.mojom.h"
 #include "services/file/user_id_map.h"
@@ -464,7 +464,7 @@
     info.factory =
         base::Bind(&device::CreateDeviceService,
                    BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE));
-    connection->AddEmbeddedService(device::kDeviceServiceName, info);
+    connection->AddEmbeddedService(device::mojom::kServiceName, info);
 
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kMojoLocalStorage)) {
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 80a7c51..ef5fea2 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -92,7 +92,6 @@
 #include "content/public/common/result_codes.h"
 #include "device/battery/battery_status_service.h"
 #include "device/gamepad/gamepad_service.h"
-#include "device/time_zone_monitor/time_zone_monitor.h"
 #include "media/base/media.h"
 #include "media/base/user_input_monitor.h"
 #include "media/midi/midi_service.h"
@@ -1540,13 +1539,6 @@
 
   {
     TRACE_EVENT0("startup",
-                 "BrowserMainLoop::BrowserThreadsStarted::TimeZoneMonitor");
-    time_zone_monitor_ =
-        device::TimeZoneMonitor::Create(file_thread_->task_runner());
-  }
-
-  {
-    TRACE_EVENT0("startup",
       "BrowserMainLoop::BrowserThreadsStarted::SaveFileManager");
     save_file_manager_ = new SaveFileManager();
   }
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index ad6c04c..1c4dcbe 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -35,10 +35,6 @@
 }  // namespace trace_event
 }  // namespace base
 
-namespace device {
-class TimeZoneMonitor;
-}
-
 namespace discardable_memory {
 class DiscardableSharedMemoryManager;
 }
@@ -146,9 +142,6 @@
   media::UserInputMonitor* user_input_monitor() const {
     return user_input_monitor_.get();
   }
-  device::TimeZoneMonitor* time_zone_monitor() const {
-    return time_zone_monitor_.get();
-  }
   discardable_memory::DiscardableSharedMemoryManager*
   discardable_shared_memory_manager() const {
     return discardable_shared_memory_manager_.get();
@@ -319,7 +312,6 @@
   std::unique_ptr<ResourceDispatcherHostImpl> resource_dispatcher_host_;
   std::unique_ptr<MediaStreamManager> media_stream_manager_;
   std::unique_ptr<SpeechRecognitionManagerImpl> speech_recognition_manager_;
-  std::unique_ptr<device::TimeZoneMonitor> time_zone_monitor_;
   std::unique_ptr<discardable_memory::DiscardableSharedMemoryManager>
       discardable_shared_memory_manager_;
   scoped_refptr<SaveFileManager> save_file_manager_;
diff --git a/content/browser/cache_storage/README.md b/content/browser/cache_storage/README.md
index 014b9882..7b3758d 100644
--- a/content/browser/cache_storage/README.md
+++ b/content/browser/cache_storage/README.md
@@ -62,6 +62,13 @@
 3. Owned by `CacheStorage` and deleted either when `CacheStorage` deletes or
    when the last `CacheStorageCacheHandle` for the cache is gone.
 
+### CacheStorageIndex
+1. Manages an ordered collection of metadata
+   (CacheStorageIndex::CacheStorageMetadata) for each CacheStorageCache owned
+   by a given CacheStorage instance.
+2. Is serialized by CacheStorage::CacheLoader (WriteIndex/LoadIndex) as a
+   Protobuf file.
+
 ### CacheStorageCacheHandle
 1. Holds a weak reference to a `CacheStorageCache`.
 2. When the last `CacheStorageCacheHandle` to a `CacheStorageCache` is
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc
index 63b03c5..d32fc3d9 100644
--- a/content/browser/cache_storage/cache_storage.cc
+++ b/content/browser/cache_storage/cache_storage.cc
@@ -28,6 +28,7 @@
 #include "content/browser/cache_storage/cache_storage.pb.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
+#include "content/browser/cache_storage/cache_storage_index.h"
 #include "content/browser/cache_storage/cache_storage_scheduler.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/directory_lister.h"
@@ -47,24 +48,18 @@
   return valued_hexed_hash;
 }
 
-void SizeRetrievedFromCache(
-    std::unique_ptr<CacheStorageCacheHandle> cache_handle,
-    const base::Closure& closure,
-    int64_t* accumulator,
-    int64_t size) {
-  *accumulator += size;
-  closure.Run();
-}
-
 void SizeRetrievedFromAllCaches(std::unique_ptr<int64_t> accumulator,
                                 const CacheStorage::SizeCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(callback, *accumulator));
 }
 
+void DoNothingWithBool(bool success) {}
+
 }  // namespace
 
 const char CacheStorage::kIndexFileName[] = "index.txt";
+const int64_t CacheStorage::kSizeUnknown;
 
 struct CacheStorage::CacheMatchResponse {
   CacheMatchResponse() = default;
@@ -81,8 +76,8 @@
   typedef base::Callback<void(std::unique_ptr<CacheStorageCache>)>
       CacheCallback;
   typedef base::Callback<void(bool)> BoolCallback;
-  typedef base::Callback<void(std::unique_ptr<std::vector<std::string>>)>
-      StringVectorCallback;
+  using CacheStorageIndexLoadCallback =
+      base::Callback<void(std::unique_ptr<CacheStorageIndex>)>;
 
   CacheLoader(
       base::SequencedTaskRunner* cache_task_runner,
@@ -105,7 +100,8 @@
   // Creates a CacheStorageCache with the given name. It does not attempt to
   // load the backend, that happens lazily when the cache is used.
   virtual std::unique_ptr<CacheStorageCache> CreateCache(
-      const std::string& cache_name) = 0;
+      const std::string& cache_name,
+      int64_t cache_size) = 0;
 
   // Deletes any pre-existing cache of the same name and then loads it.
   virtual void PrepareNewCacheDestination(const std::string& cache_name,
@@ -115,13 +111,12 @@
   // removing the cache's directory.
   virtual void CleanUpDeletedCache(CacheStorageCache* cache) = 0;
 
-  // Writes the cache names (and sizes) to disk if applicable.
-  virtual void WriteIndex(const StringVector& cache_names,
+  // Writes the cache index to disk if applicable.
+  virtual void WriteIndex(const CacheStorageIndex& index,
                           const BoolCallback& callback) = 0;
 
-  // Loads the cache names from disk if applicable.
-  virtual void LoadIndex(std::unique_ptr<std::vector<std::string>> cache_names,
-                         const StringVectorCallback& callback) = 0;
+  // Loads the cache index from disk if applicable.
+  virtual void LoadIndex(const CacheStorageIndexLoadCallback& callback) = 0;
 
   // Called when CacheStorage has created a cache. Used to hold onto a handle to
   // the cache if necessary.
@@ -168,8 +163,8 @@
                     cache_storage,
                     origin) {}
 
-  std::unique_ptr<CacheStorageCache> CreateCache(
-      const std::string& cache_name) override {
+  std::unique_ptr<CacheStorageCache> CreateCache(const std::string& cache_name,
+                                                 int64_t cache_size) override {
     return CacheStorageCache::CreateMemoryCache(
         origin_, cache_name, cache_storage_, request_context_getter_,
         quota_manager_proxy_, blob_context_);
@@ -177,20 +172,20 @@
 
   void PrepareNewCacheDestination(const std::string& cache_name,
                                   const CacheCallback& callback) override {
-    std::unique_ptr<CacheStorageCache> cache = CreateCache(cache_name);
+    std::unique_ptr<CacheStorageCache> cache =
+        CreateCache(cache_name, 0 /*cache_size*/);
     callback.Run(std::move(cache));
   }
 
   void CleanUpDeletedCache(CacheStorageCache* cache) override {}
 
-  void WriteIndex(const StringVector& cache_names,
+  void WriteIndex(const CacheStorageIndex& index,
                   const BoolCallback& callback) override {
     callback.Run(true);
   }
 
-  void LoadIndex(std::unique_ptr<std::vector<std::string>> cache_names,
-                 const StringVectorCallback& callback) override {
-    callback.Run(std::move(cache_names));
+  void LoadIndex(const CacheStorageIndexLoadCallback& callback) override {
+    callback.Run(base::MakeUnique<CacheStorageIndex>());
   }
 
   void NotifyCacheCreated(
@@ -236,8 +231,8 @@
         origin_path_(origin_path),
         weak_ptr_factory_(this) {}
 
-  std::unique_ptr<CacheStorageCache> CreateCache(
-      const std::string& cache_name) override {
+  std::unique_ptr<CacheStorageCache> CreateCache(const std::string& cache_name,
+                                                 int64_t cache_size) override {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_name));
 
@@ -245,7 +240,8 @@
     base::FilePath cache_path = origin_path_.AppendASCII(cache_dir);
     return CacheStorageCache::CreatePersistentCache(
         origin_, cache_name, cache_storage_, cache_path,
-        request_context_getter_, quota_manager_proxy_, blob_context_);
+        request_context_getter_, quota_manager_proxy_, blob_context_,
+        cache_size);
   }
 
   void PrepareNewCacheDestination(const std::string& cache_name,
@@ -282,7 +278,7 @@
     }
 
     cache_name_to_cache_dir_[cache_name] = cache_dir;
-    callback.Run(CreateCache(cache_name));
+    callback.Run(CreateCache(cache_name, CacheStorage::kSizeUnknown));
   }
 
   void CleanUpDeletedCache(CacheStorageCache* cache) override {
@@ -302,26 +298,30 @@
     base::DeleteFile(cache_path, true /* recursive */);
   }
 
-  void WriteIndex(const StringVector& cache_names,
+  void WriteIndex(const CacheStorageIndex& index,
                   const BoolCallback& callback) override {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     // 1. Create the index file as a string. (WriteIndex)
     // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
 
-    proto::CacheStorageIndex index;
-    index.set_origin(origin_.spec());
+    proto::CacheStorageIndex protobuf_index;
+    protobuf_index.set_origin(origin_.spec());
 
-    for (size_t i = 0u, max = cache_names.size(); i < max; ++i) {
-      DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_names[i]));
+    for (const auto& cache_metadata : index.ordered_cache_metadata()) {
+      DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_metadata.name));
 
-      proto::CacheStorageIndex::Cache* index_cache = index.add_cache();
-      index_cache->set_name(cache_names[i]);
-      index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_names[i]]);
+      proto::CacheStorageIndex::Cache* index_cache = protobuf_index.add_cache();
+      index_cache->set_name(cache_metadata.name);
+      index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_metadata.name]);
+      if (cache_metadata.size == CacheStorage::kSizeUnknown)
+        index_cache->clear_size();
+      else
+        index_cache->set_size(cache_metadata.size);
     }
 
     std::string serialized;
-    bool success = index.SerializeToString(&serialized);
+    bool success = protobuf_index.SerializeToString(&serialized);
     DCHECK(success);
 
     base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
@@ -348,47 +348,38 @@
     return base::ReplaceFile(tmp_path, index_path, NULL);
   }
 
-  void LoadIndex(std::unique_ptr<std::vector<std::string>> names,
-                 const StringVectorCallback& callback) override {
+  void LoadIndex(const CacheStorageIndexLoadCallback& callback) override {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-    // 1. Read the file from disk. (LoadIndexReadFileInPool)
-    // 2. Parse file and return the names of the caches (LoadIndexDidReadFile)
-
-    base::FilePath index_path =
-        origin_path_.AppendASCII(CacheStorage::kIndexFileName);
-
     PostTaskAndReplyWithResult(
         cache_task_runner_.get(), FROM_HERE,
-        base::Bind(&SimpleCacheLoader::ReadAndMigrateIndexInPool, index_path),
-        base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile,
-                   weak_ptr_factory_.GetWeakPtr(), base::Passed(&names),
-                   callback));
+        base::Bind(&SimpleCacheLoader::ReadAndMigrateIndexInPool, origin_path_),
+        base::Bind(&SimpleCacheLoader::LoadIndexDidReadIndex,
+                   weak_ptr_factory_.GetWeakPtr(), callback));
   }
 
-  void LoadIndexDidReadFile(std::unique_ptr<std::vector<std::string>> names,
-                            const StringVectorCallback& callback,
-                            const std::string& serialized) {
+  void LoadIndexDidReadIndex(const CacheStorageIndexLoadCallback& callback,
+                             proto::CacheStorageIndex protobuf_index) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     std::unique_ptr<std::set<std::string>> cache_dirs(
         new std::set<std::string>);
 
-    proto::CacheStorageIndex index;
-    if (index.ParseFromString(serialized)) {
-      for (int i = 0, max = index.cache_size(); i < max; ++i) {
-        const proto::CacheStorageIndex::Cache& cache = index.cache(i);
-        DCHECK(cache.has_cache_dir());
-        names->push_back(cache.name());
-        cache_name_to_cache_dir_[cache.name()] = cache.cache_dir();
-        cache_dirs->insert(cache.cache_dir());
-      }
+    auto index = base::MakeUnique<CacheStorageIndex>();
+    for (int i = 0, max = protobuf_index.cache_size(); i < max; ++i) {
+      const proto::CacheStorageIndex::Cache& cache = protobuf_index.cache(i);
+      DCHECK(cache.has_cache_dir());
+      int64_t cache_size =
+          cache.has_size() ? cache.size() : CacheStorage::kSizeUnknown;
+      index->Insert(CacheStorageIndex::CacheMetadata(cache.name(), cache_size));
+      cache_name_to_cache_dir_[cache.name()] = cache.cache_dir();
+      cache_dirs->insert(cache.cache_dir());
     }
 
     cache_task_runner_->PostTask(
         FROM_HERE, base::Bind(&DeleteUnreferencedCachesInPool, origin_path_,
                               base::Passed(&cache_dirs)));
-    callback.Run(std::move(names));
+    callback.Run(std::move(index));
   }
 
   void NotifyCacheDoomed(
@@ -424,22 +415,41 @@
   }
 
   // Runs on cache_task_runner_
-  static std::string MigrateCachesIfNecessaryInPool(
-      const std::string& body,
-      const base::FilePath& index_path) {
-    proto::CacheStorageIndex index;
-    if (!index.ParseFromString(body))
-      return body;
+  static proto::CacheStorageIndex ReadAndMigrateIndexInPool(
+      const base::FilePath& origin_path) {
+    const base::FilePath index_path =
+        origin_path.AppendASCII(CacheStorage::kIndexFileName);
 
-    base::FilePath origin_path = index_path.DirName();
-    bool index_is_dirty = false;
-    const std::string kBadIndexState("");
+    proto::CacheStorageIndex index;
+    std::string body;
+    if (!base::ReadFileToString(index_path, &body) ||
+        !index.ParseFromString(body))
+      return proto::CacheStorageIndex();
+    body.clear();
+
+    base::File::Info file_info;
+    base::Time index_last_modified;
+    if (GetFileInfo(index_path, &file_info))
+      index_last_modified = file_info.last_modified;
+    bool index_modified = false;
 
     // Look for caches that have no cache_dir. Give any such caches a directory
     // with a random name and move them there. Then, rewrite the index file.
+    // Additionally invalidate the size of any index entries where the cache was
+    // modified after the index (making it out-of-date).
     for (int i = 0, max = index.cache_size(); i < max; ++i) {
       const proto::CacheStorageIndex::Cache& cache = index.cache(i);
-      if (!cache.has_cache_dir()) {
+      if (cache.has_cache_dir()) {
+        if (cache.has_size()) {
+          base::FilePath cache_dir = origin_path.AppendASCII(cache.cache_dir());
+          if (!GetFileInfo(cache_dir, &file_info) ||
+              index_last_modified <= file_info.last_modified) {
+            // Index is older than this cache, so invalidate index entries that
+            // may change as a result of cache operations.
+            index.mutable_cache(i)->clear_size();
+          }
+        }
+      } else {
         // Find a new home for the cache.
         base::FilePath legacy_cache_path =
             origin_path.AppendASCII(HexedHash(cache.name()));
@@ -454,34 +464,24 @@
           // If the move fails then the cache is in a bad state. Return an empty
           // index so that the CacheStorage can start fresh. The unreferenced
           // caches will be discarded later in initialization.
-          return kBadIndexState;
+          return proto::CacheStorageIndex();
         }
 
         index.mutable_cache(i)->set_cache_dir(cache_dir);
-        index_is_dirty = true;
+        index.mutable_cache(i)->clear_size();
+        index_modified = true;
       }
     }
 
-    if (index_is_dirty) {
-      std::string new_body;
-      if (!index.SerializeToString(&new_body))
-        return kBadIndexState;
-      if (base::WriteFile(index_path, new_body.c_str(), new_body.size()) !=
-          base::checked_cast<int>(new_body.size()))
-        return kBadIndexState;
-      return new_body;
+    if (index_modified) {
+      if (!index.SerializeToString(&body))
+        return proto::CacheStorageIndex();
+      if (base::WriteFile(index_path, body.c_str(), body.size()) !=
+          base::checked_cast<int>(body.size()))
+        return proto::CacheStorageIndex();
     }
 
-    return body;
-  }
-
-  // Runs on cache_task_runner_
-  static std::string ReadAndMigrateIndexInPool(
-      const base::FilePath& index_path) {
-    std::string body;
-    base::ReadFileToString(index_path, &body);
-
-    return MigrateCachesIfNecessaryInPool(body, index_path);
+    return index;
   }
 
   const base::FilePath origin_path_;
@@ -570,7 +570,7 @@
                  cache_name, scheduler_->WrapCallbackToRunNext(callback)));
 }
 
-void CacheStorage::EnumerateCaches(const StringsCallback& callback) {
+void CacheStorage::EnumerateCaches(const IndexCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!initialized_)
@@ -646,6 +646,50 @@
                  scheduler_->WrapCallbackToRunNext(callback)));
 }
 
+void CacheStorage::ScheduleWriteIndex() {
+  static const int64_t kWriteIndexDelaySecs = 5;
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  index_write_task_.Reset(base::Bind(&CacheStorage::WriteIndex,
+                                     weak_factory_.GetWeakPtr(),
+                                     base::Bind(&DoNothingWithBool)));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, index_write_task_.callback(),
+      base::TimeDelta::FromSeconds(kWriteIndexDelaySecs));
+}
+
+void CacheStorage::WriteIndex(const base::Callback<void(bool)>& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  scheduler_->ScheduleOperation(
+      base::Bind(&CacheStorage::WriteIndexImpl, weak_factory_.GetWeakPtr(),
+                 scheduler_->WrapCallbackToRunNext(callback)));
+}
+
+void CacheStorage::WriteIndexImpl(const base::Callback<void(bool)>& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  cache_loader_->WriteIndex(*cache_index_, callback);
+}
+
+bool CacheStorage::InitiateScheduledIndexWriteForTest(
+    const base::Callback<void(bool)>& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (index_write_pending()) {
+    index_write_task_.Cancel();
+    WriteIndex(callback);
+    return true;
+  }
+  callback.Run(true /* success */);
+  return false;
+}
+
+void CacheStorage::CacheSizeUpdated(const CacheStorageCache* cache,
+                                    int64_t size) {
+  // Should not be called for doomed caches.
+  DCHECK(!base::ContainsKey(doomed_caches_,
+                            const_cast<CacheStorageCache*>(cache)));
+  cache_index_->SetCacheSize(cache->cache_name(), size);
+  ScheduleWriteIndex();
+}
+
 void CacheStorage::StartAsyncOperationForTesting() {
   scheduler_->ScheduleOperation(base::Bind(&base::DoNothing));
 }
@@ -674,29 +718,28 @@
   DCHECK(!initialized_);
   DCHECK(initializing_);
 
-  // 1. Get the list of cache names (async call)
+  // 1. Get the cache index (async call)
   // 2. For each cache name, load the cache (async call)
   // 3. Once each load is complete, update the map variables.
   // 4. Call the list of waiting callbacks.
 
-  std::unique_ptr<std::vector<std::string>> indexed_cache_names(
-      new std::vector<std::string>());
-
-  cache_loader_->LoadIndex(std::move(indexed_cache_names),
-                           base::Bind(&CacheStorage::LazyInitDidLoadIndex,
+  cache_loader_->LoadIndex(base::Bind(&CacheStorage::LazyInitDidLoadIndex,
                                       weak_factory_.GetWeakPtr()));
 }
 
 void CacheStorage::LazyInitDidLoadIndex(
-    std::unique_ptr<std::vector<std::string>> indexed_cache_names) {
+    std::unique_ptr<CacheStorageIndex> index) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(cache_map_.empty());
 
-  for (size_t i = 0u, max = indexed_cache_names->size(); i < max; ++i) {
-    cache_map_.insert(std::make_pair(indexed_cache_names->at(i),
+  for (const auto& cache_metadata : index->ordered_cache_metadata()) {
+    cache_map_.insert(std::make_pair(cache_metadata.name,
                                      std::unique_ptr<CacheStorageCache>()));
-    ordered_cache_names_.push_back(indexed_cache_names->at(i));
   }
 
+  DCHECK(!cache_index_);
+  cache_index_ = std::move(index);
+
   initializing_ = false;
   initialized_ = true;
 
@@ -735,13 +778,13 @@
   CacheStorageCache* cache_ptr = cache.get();
 
   cache_map_.insert(std::make_pair(cache_name, std::move(cache)));
-  ordered_cache_names_.push_back(cache_name);
+  cache_index_->Insert(
+      CacheStorageIndex::CacheMetadata(cache_name, cache_ptr->cache_size()));
 
   cache_loader_->WriteIndex(
-      ordered_cache_names_,
-      base::Bind(&CacheStorage::CreateCacheDidWriteIndex,
-                 weak_factory_.GetWeakPtr(), callback,
-                 base::Passed(CreateCacheHandle(cache_ptr))));
+      *cache_index_, base::Bind(&CacheStorage::CreateCacheDidWriteIndex,
+                                weak_factory_.GetWeakPtr(), callback,
+                                base::Passed(CreateCacheHandle(cache_ptr))));
 
   cache_loader_->NotifyCacheCreated(cache_name, CreateCacheHandle(cache_ptr));
 }
@@ -766,45 +809,43 @@
 
 void CacheStorage::DeleteCacheImpl(const std::string& cache_name,
                                    const BoolAndErrorCallback& callback) {
-  if (!GetLoadedCache(cache_name)) {
+  std::unique_ptr<CacheStorageCacheHandle> cache_handle =
+      GetLoadedCache(cache_name);
+  if (!cache_handle) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(callback, false, CACHE_STORAGE_ERROR_NOT_FOUND));
     return;
   }
 
-  // Delete the name from ordered_cache_names_.
-  StringVector original_ordered_cache_names = ordered_cache_names_;
-  StringVector::iterator iter = std::find(
-      ordered_cache_names_.begin(), ordered_cache_names_.end(), cache_name);
-  DCHECK(iter != ordered_cache_names_.end());
-  ordered_cache_names_.erase(iter);
-
-  cache_loader_->WriteIndex(ordered_cache_names_,
-                            base::Bind(&CacheStorage::DeleteCacheDidWriteIndex,
-                                       weak_factory_.GetWeakPtr(), cache_name,
-                                       original_ordered_cache_names, callback));
+  cache_handle->value()->SetObserver(nullptr);
+  cache_index_->DoomCache(cache_name);
+  cache_loader_->WriteIndex(
+      *cache_index_,
+      base::Bind(&CacheStorage::DeleteCacheDidWriteIndex,
+                 weak_factory_.GetWeakPtr(),
+                 base::Passed(std::move(cache_handle)), callback));
 }
 
 void CacheStorage::DeleteCacheDidWriteIndex(
-    const std::string& cache_name,
-    const StringVector& original_ordered_cache_names,
+    std::unique_ptr<CacheStorageCacheHandle> cache_handle,
     const BoolAndErrorCallback& callback,
     bool success) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!success) {
-    // Undo any changes if the change couldn't be written to disk.
-    ordered_cache_names_ = original_ordered_cache_names;
+    // Undo any changes if the index couldn't be written to disk.
+    cache_index_->RestoreDoomedCache();
+    cache_handle->value()->SetObserver(this);
     callback.Run(false, CACHE_STORAGE_ERROR_STORAGE);
     return;
   }
 
-  // Make sure that a cache handle exists for the doomed cache to ensure that
-  // DeleteCacheFinalize is called.
-  std::unique_ptr<CacheStorageCacheHandle> cache_handle =
-      GetLoadedCache(cache_name);
+  cache_index_->FinalizeDoomedCache();
 
-  CacheMap::iterator map_iter = cache_map_.find(cache_name);
+  CacheMap::iterator map_iter =
+      cache_map_.find(cache_handle->value()->cache_name());
+  DCHECK(map_iter != cache_map_.end());
+
   doomed_caches_.insert(
       std::make_pair(map_iter->second.get(), std::move(map_iter->second)));
   cache_map_.erase(map_iter);
@@ -834,8 +875,8 @@
   doomed_caches_.erase(doomed_caches_iter);
 }
 
-void CacheStorage::EnumerateCachesImpl(const StringsCallback& callback) {
-  callback.Run(ordered_cache_names_);
+void CacheStorage::EnumerateCachesImpl(const IndexCallback& callback) {
+  callback.Run(*cache_index_);
 }
 
 void CacheStorage::MatchCacheImpl(
@@ -876,17 +917,18 @@
     const CacheStorageCacheQueryParams& match_params,
     const CacheStorageCache::ResponseCallback& callback) {
   std::vector<CacheMatchResponse>* match_responses =
-      new std::vector<CacheMatchResponse>(ordered_cache_names_.size());
+      new std::vector<CacheMatchResponse>(cache_index_->num_entries());
 
   base::Closure barrier_closure = base::BarrierClosure(
-      ordered_cache_names_.size(),
+      cache_index_->num_entries(),
       base::Bind(&CacheStorage::MatchAllCachesDidMatchAll,
                  weak_factory_.GetWeakPtr(),
                  base::Passed(base::WrapUnique(match_responses)), callback));
 
-  for (size_t i = 0, max = ordered_cache_names_.size(); i < max; ++i) {
+  size_t idx = 0;
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
     std::unique_ptr<CacheStorageCacheHandle> cache_handle =
-        GetLoadedCache(ordered_cache_names_[i]);
+        GetLoadedCache(cache_metadata.name);
     DCHECK(cache_handle);
 
     CacheStorageCache* cache_ptr = cache_handle->value();
@@ -895,7 +937,8 @@
                      base::Bind(&CacheStorage::MatchAllCachesDidMatch,
                                 weak_factory_.GetWeakPtr(),
                                 base::Passed(std::move(cache_handle)),
-                                &match_responses->at(i), barrier_closure));
+                                &match_responses->at(idx), barrier_closure));
+    idx++;
   }
 }
 
@@ -982,8 +1025,8 @@
   CacheStorageCache* cache = map_iter->second.get();
 
   if (!cache) {
-    std::unique_ptr<CacheStorageCache> new_cache =
-        cache_loader_->CreateCache(cache_name);
+    std::unique_ptr<CacheStorageCache> new_cache = cache_loader_->CreateCache(
+        cache_name, cache_index_->GetCacheSize(cache_name));
     CacheStorageCache* cache_ptr = new_cache.get();
     map_iter->second = std::move(new_cache);
 
@@ -993,6 +1036,16 @@
   return CreateCacheHandle(cache);
 }
 
+void CacheStorage::SizeRetrievedFromCache(
+    std::unique_ptr<CacheStorageCacheHandle> cache_handle,
+    const base::Closure& closure,
+    int64_t* accumulator,
+    int64_t size) {
+  cache_index_->SetCacheSize(cache_handle->value()->cache_name(), size);
+  *accumulator += size;
+  closure.Run();
+}
+
 void CacheStorage::GetSizeThenCloseAllCachesImpl(const SizeCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(initialized_);
@@ -1001,15 +1054,15 @@
   int64_t* accumulator_ptr = accumulator.get();
 
   base::Closure barrier_closure = base::BarrierClosure(
-      ordered_cache_names_.size(),
+      cache_index_->num_entries(),
       base::Bind(&SizeRetrievedFromAllCaches,
                  base::Passed(std::move(accumulator)), callback));
 
-  for (const std::string& cache_name : ordered_cache_names_) {
-    std::unique_ptr<CacheStorageCacheHandle> cache_handle =
-        GetLoadedCache(cache_name);
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
+    auto cache_handle = GetLoadedCache(cache_metadata.name);
     CacheStorageCache* cache = cache_handle->value();
-    cache->GetSizeThenClose(base::Bind(&SizeRetrievedFromCache,
+    cache->GetSizeThenClose(base::Bind(&CacheStorage::SizeRetrievedFromCache,
+                                       weak_factory_.GetWeakPtr(),
                                        base::Passed(std::move(cache_handle)),
                                        barrier_closure, accumulator_ptr));
   }
@@ -1019,19 +1072,31 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(initialized_);
 
+  if (cache_index_->GetStorageSize() != kSizeUnknown) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, cache_index_->GetStorageSize()));
+    return;
+  }
+
   std::unique_ptr<int64_t> accumulator(new int64_t(0));
   int64_t* accumulator_ptr = accumulator.get();
 
   base::Closure barrier_closure = base::BarrierClosure(
-      ordered_cache_names_.size(),
+      cache_index_->num_entries(),
       base::Bind(&SizeRetrievedFromAllCaches,
                  base::Passed(std::move(accumulator)), callback));
 
-  for (const std::string& cache_name : ordered_cache_names_) {
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
+    if (cache_metadata.size != CacheStorage::kSizeUnknown) {
+      *accumulator_ptr += cache_metadata.size;
+      barrier_closure.Run();
+      continue;
+    }
     std::unique_ptr<CacheStorageCacheHandle> cache_handle =
-        GetLoadedCache(cache_name);
+        GetLoadedCache(cache_metadata.name);
     CacheStorageCache* cache = cache_handle->value();
-    cache->Size(base::Bind(&SizeRetrievedFromCache,
+    cache->Size(base::Bind(&CacheStorage::SizeRetrievedFromCache,
+                           weak_factory_.GetWeakPtr(),
                            base::Passed(std::move(cache_handle)),
                            barrier_closure, accumulator_ptr));
   }
diff --git a/content/browser/cache_storage/cache_storage.h b/content/browser/cache_storage/cache_storage.h
index e40c937..b99886a 100644
--- a/content/browser/cache_storage/cache_storage.h
+++ b/content/browser/cache_storage/cache_storage.h
@@ -18,6 +18,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
+#include "content/browser/cache_storage/cache_storage_cache_observer.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -33,6 +34,7 @@
 
 namespace content {
 class CacheStorageCacheHandle;
+class CacheStorageIndex;
 class CacheStorageScheduler;
 
 // TODO(jkarlin): Constrain the total bytes used per origin.
@@ -40,14 +42,15 @@
 // CacheStorage holds the set of caches for a given origin. It is
 // owned by the CacheStorageManager. This class expects to be run
 // on the IO thread. The asynchronous methods are executed serially.
-class CONTENT_EXPORT CacheStorage {
+class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver {
  public:
-  typedef std::vector<std::string> StringVector;
+  constexpr static int64_t kSizeUnknown = -1;
+
   typedef base::Callback<void(bool, CacheStorageError)> BoolAndErrorCallback;
   typedef base::Callback<void(std::unique_ptr<CacheStorageCacheHandle>,
                               CacheStorageError)>
       CacheAndErrorCallback;
-  using StringsCallback = base::Callback<void(const StringVector&)>;
+  using IndexCallback = base::Callback<void(const CacheStorageIndex&)>;
   using SizeCallback = base::Callback<void(int64_t)>;
 
   static const char kIndexFileName[];
@@ -86,8 +89,8 @@
   void DeleteCache(const std::string& cache_name,
                    const BoolAndErrorCallback& callback);
 
-  // Calls the callback with a vector of cache names (keys) available.
-  void EnumerateCaches(const StringsCallback& callback);
+  // Calls the callback with the cache index.
+  void EnumerateCaches(const IndexCallback& callback);
 
   // Calls match on the cache with the given |cache_name|.
   void MatchCache(const std::string& cache_name,
@@ -116,9 +119,13 @@
   void StartAsyncOperationForTesting();
   void CompleteAsyncOperationForTesting();
 
+  // CacheStorageCacheObserver:
+  void CacheSizeUpdated(const CacheStorageCache* cache, int64_t size) override;
+
  private:
   friend class CacheStorageCacheHandle;
   friend class CacheStorageCache;
+  friend class CacheStorageManagerTest;
   class CacheLoader;
   class MemoryLoader;
   class SimpleCacheLoader;
@@ -140,8 +147,7 @@
   // Initializer and its callback are below.
   void LazyInit();
   void LazyInitImpl();
-  void LazyInitDidLoadIndex(
-      std::unique_ptr<std::vector<std::string>> indexed_cache_names);
+  void LazyInitDidLoadIndex(std::unique_ptr<CacheStorageIndex> index);
 
   // The Open and CreateCache callbacks are below.
   void OpenCacheImpl(const std::string& cache_name,
@@ -162,8 +168,7 @@
   void DeleteCacheImpl(const std::string& cache_name,
                        const BoolAndErrorCallback& callback);
   void DeleteCacheDidWriteIndex(
-      const std::string& cache_name,
-      const StringVector& original_ordered_cache_names,
+      std::unique_ptr<CacheStorageCacheHandle> cache_handle,
       const BoolAndErrorCallback& callback,
       bool success);
   void DeleteCacheFinalize(CacheStorageCache* doomed_cache);
@@ -172,7 +177,7 @@
   void DeleteCacheDidCleanUp(bool success);
 
   // The EnumerateCache callbacks are below.
-  void EnumerateCachesImpl(const StringsCallback& callback);
+  void EnumerateCachesImpl(const IndexCallback& callback);
 
   // The MatchCache callbacks are below.
   void MatchCacheImpl(const std::string& cache_name,
@@ -203,6 +208,20 @@
   void GetSizeThenCloseAllCachesImpl(const SizeCallback& callback);
 
   void SizeImpl(const SizeCallback& callback);
+  void SizeRetrievedFromCache(
+      std::unique_ptr<CacheStorageCacheHandle> cache_handle,
+      const base::Closure& closure,
+      int64_t* accumulator,
+      int64_t size);
+
+  void ScheduleWriteIndex();
+  void WriteIndex(const base::Callback<void(bool)>& callback);
+  void WriteIndexImpl(const base::Callback<void(bool)>& callback);
+  bool index_write_pending() const { return !index_write_task_.IsCancelled(); }
+  // Start a scheduled index write immediately. Returns true if a write was
+  // scheduled, or false if not.
+  bool InitiateScheduledIndexWriteForTest(
+      const base::Callback<void(bool)>& callback);
 
   // Whether or not we've loaded the list of cache names into memory.
   bool initialized_;
@@ -225,8 +244,8 @@
   // CacheStorageCacheHandle reference counts
   std::map<CacheStorageCache*, size_t> cache_handle_counts_;
 
-  // The names of caches in the order that they were created.
-  StringVector ordered_cache_names_;
+  // The cache index data.
+  std::unique_ptr<CacheStorageIndex> cache_index_;
 
   // The file path for this CacheStorage.
   base::FilePath origin_path_;
@@ -243,6 +262,8 @@
   // The origin that this CacheStorage is associated with.
   GURL origin_;
 
+  base::CancelableClosure index_write_task_;
+
   base::WeakPtrFactory<CacheStorage> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CacheStorage);
diff --git a/content/browser/cache_storage/cache_storage.proto b/content/browser/cache_storage/cache_storage.proto
index 703c022..06f1973ff 100644
--- a/content/browser/cache_storage/cache_storage.proto
+++ b/content/browser/cache_storage/cache_storage.proto
@@ -12,6 +12,7 @@
   message Cache {
     required string name = 1;
     optional string cache_dir = 2;
+    optional int64 size = 3;
   }
   repeated Cache cache = 1;
   optional string origin = 2;
diff --git a/content/browser/cache_storage/cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc
index 4bc34fc..e84e4f4 100644
--- a/content/browser/cache_storage/cache_storage_cache.cc
+++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -24,6 +24,7 @@
 #include "content/browser/cache_storage/cache_storage.pb.h"
 #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
+#include "content/browser/cache_storage/cache_storage_cache_observer.h"
 #include "content/browser/cache_storage/cache_storage_scheduler.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/referrer.h"
@@ -263,10 +264,11 @@
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
     base::WeakPtr<storage::BlobStorageContext> blob_context) {
-  CacheStorageCache* cache =
-      new CacheStorageCache(origin, cache_name, base::FilePath(), cache_storage,
-                            std::move(request_context_getter),
-                            std::move(quota_manager_proxy), blob_context);
+  CacheStorageCache* cache = new CacheStorageCache(
+      origin, cache_name, base::FilePath(), cache_storage,
+      std::move(request_context_getter), std::move(quota_manager_proxy),
+      blob_context, 0 /* cache_size */);
+  cache->SetObserver(cache_storage);
   cache->InitBackend();
   return base::WrapUnique(cache);
 }
@@ -279,12 +281,13 @@
     const base::FilePath& path,
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-    base::WeakPtr<storage::BlobStorageContext> blob_context) {
-  CacheStorageCache* cache =
-      new CacheStorageCache(origin, cache_name, path, cache_storage,
-                            std::move(request_context_getter),
-                            std::move(quota_manager_proxy), blob_context);
-  cache->InitBackend();
+    base::WeakPtr<storage::BlobStorageContext> blob_context,
+    int64_t cache_size) {
+  CacheStorageCache* cache = new CacheStorageCache(
+      origin, cache_name, path, cache_storage,
+      std::move(request_context_getter), std::move(quota_manager_proxy),
+      blob_context, cache_size);
+  cache->SetObserver(cache_storage), cache->InitBackend();
   return base::WrapUnique(cache);
 }
 
@@ -498,6 +501,11 @@
                             scheduler_->WrapCallbackToRunNext(callback))));
 }
 
+void CacheStorageCache::SetObserver(CacheStorageCacheObserver* observer) {
+  DCHECK((observer == nullptr) ^ (cache_observer_ == nullptr));
+  cache_observer_ = observer;
+}
+
 CacheStorageCache::~CacheStorageCache() {
   quota_manager_proxy_->NotifyOriginNoLongerInUse(origin_);
 }
@@ -509,7 +517,8 @@
     CacheStorage* cache_storage,
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-    base::WeakPtr<storage::BlobStorageContext> blob_context)
+    base::WeakPtr<storage::BlobStorageContext> blob_context,
+    int64_t cache_size)
     : origin_(origin),
       cache_name_(cache_name),
       path_(path),
@@ -519,7 +528,9 @@
       blob_storage_context_(blob_context),
       scheduler_(
           new CacheStorageScheduler(CacheStorageSchedulerClient::CLIENT_CACHE)),
+      cache_size_(cache_size),
       max_query_size_bytes_(kMaxQueryCacheResultBytes),
+      cache_observer_(nullptr),
       memory_only_(path.empty()),
       weak_ptr_factory_(this) {
   DCHECK(!origin_.is_empty());
@@ -1179,12 +1190,18 @@
 void CacheStorageCache::UpdateCacheSizeGotSize(
     std::unique_ptr<CacheStorageCacheHandle> cache_handle,
     int current_cache_size) {
+  DCHECK_NE(current_cache_size, CacheStorage::kSizeUnknown);
   int64_t old_cache_size = cache_size_;
   cache_size_ = current_cache_size;
 
+  int64_t size_delta = current_cache_size - old_cache_size;
+
   quota_manager_proxy_->NotifyStorageModified(
       storage::QuotaClient::kServiceWorkerCache, origin_,
-      storage::kStorageTypeTemporary, current_cache_size - old_cache_size);
+      storage::kStorageTypeTemporary, size_delta);
+
+  if (cache_observer_)
+    cache_observer_->CacheSizeUpdated(this, current_cache_size);
 }
 
 void CacheStorageCache::Delete(const CacheStorageBatchOperation& operation,
@@ -1369,6 +1386,15 @@
 void CacheStorageCache::InitGotCacheSize(const base::Closure& callback,
                                          CacheStorageError cache_create_error,
                                          int cache_size) {
+  // Now that we know the cache size either 1) the cache size should be unknown
+  // (which is why the size was calculated), or 2) it must match the current
+  // size. If the sizes aren't equal then there is a bug in how the cache size
+  // is saved in the store's index.
+  if (cache_size_ != CacheStorage::kSizeUnknown && cache_size_ != cache_size) {
+    // TODO(cmumford): Add UMA for this.
+    LOG(ERROR) << "Cache size/index mismatch";
+    DCHECK_EQ(cache_size_, cache_size);
+  }
   cache_size_ = cache_size;
   initializing_ = false;
   backend_state_ = (cache_create_error == CACHE_STORAGE_OK && backend_ &&
@@ -1379,6 +1405,9 @@
   UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult",
                             cache_create_error, CACHE_STORAGE_ERROR_LAST + 1);
 
+  if (cache_observer_)
+    cache_observer_->CacheSizeUpdated(this, cache_size_);
+
   callback.Run();
 }
 
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h
index 5fe67d0..1d5996c 100644
--- a/content/browser/cache_storage/cache_storage_cache.h
+++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -33,9 +33,11 @@
 }
 
 namespace content {
+class CacheMetadata;
 class CacheStorage;
 class CacheStorageBlobToDiskCache;
 class CacheStorageCacheHandle;
+class CacheStorageCacheObserver;
 class CacheStorageScheduler;
 class TestCacheStorageCache;
 
@@ -81,7 +83,8 @@
       const base::FilePath& path,
       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      base::WeakPtr<storage::BlobStorageContext> blob_context);
+      base::WeakPtr<storage::BlobStorageContext> blob_context,
+      int64_t cache_size);
 
   // Returns ERROR_TYPE_NOT_FOUND if not found.
   void Match(std::unique_ptr<ServiceWorkerFetchRequest> request,
@@ -156,6 +159,13 @@
 
   std::string cache_name() const { return cache_name_; }
 
+  int64_t cache_size() const { return cache_size_; }
+
+  // Set the one observer that will be notified of changes to this cache.
+  // Note: Either the observer must have a lifetime longer than this instance
+  // or call SetObserver(nullptr) to stop receiving notification of changes.
+  void SetObserver(CacheStorageCacheObserver* observer);
+
   base::WeakPtr<CacheStorageCache> AsWeakPtr();
 
  private:
@@ -197,7 +207,8 @@
       CacheStorage* cache_storage,
       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      base::WeakPtr<storage::BlobStorageContext> blob_context);
+      base::WeakPtr<storage::BlobStorageContext> blob_context,
+      int64_t cache_size);
 
   // Returns all entries in this cache.
   void OpenAllEntries(const OpenAllEntriesCallback& callback);
@@ -387,8 +398,9 @@
   BackendState backend_state_ = BACKEND_UNINITIALIZED;
   std::unique_ptr<CacheStorageScheduler> scheduler_;
   bool initializing_ = false;
-  int64_t cache_size_ = 0;
+  int64_t cache_size_;
   size_t max_query_size_bytes_;
+  CacheStorageCacheObserver* cache_observer_;
 
   // Owns the elements of the list
   BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_;
diff --git a/content/browser/cache_storage/cache_storage_cache_observer.h b/content/browser/cache_storage/cache_storage_cache_observer.h
new file mode 100644
index 0000000..d46f7da
--- /dev/null
+++ b/content/browser/cache_storage/cache_storage_cache_observer.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_
+#define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+class CacheStorageCache;
+
+class CONTENT_EXPORT CacheStorageCacheObserver {
+ public:
+  // The cache size has been set.
+  virtual void CacheSizeUpdated(const CacheStorageCache* cache,
+                                int64_t size) = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index f10ab16..fd27f86 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -282,7 +282,8 @@
                           cache_storage,
                           request_context_getter,
                           quota_manager_proxy,
-                          blob_context),
+                          blob_context,
+                          0 /* cache_size */),
         delay_backend_creation_(false) {}
 
   void CreateBackend(const ErrorCallback& callback) override {
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
index ba584c7..621af75 100644
--- a/content/browser/cache_storage/cache_storage_dispatcher_host.cc
+++ b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
@@ -389,10 +389,10 @@
 void CacheStorageDispatcherHost::OnCacheStorageKeysCallback(
     int thread_id,
     int request_id,
-    const std::vector<std::string>& strings) {
+    const CacheStorageIndex& cache_index) {
   std::vector<base::string16> string16s;
-  for (size_t i = 0, max = strings.size(); i < max; ++i)
-    string16s.push_back(base::UTF8ToUTF16(strings[i]));
+  for (const auto& metadata : cache_index.ordered_cache_metadata())
+    string16s.push_back(base::UTF8ToUTF16(metadata.name));
   Send(new CacheStorageMsg_CacheStorageKeysSuccess(thread_id, request_id,
                                                    string16s));
 }
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.h b/content/browser/cache_storage/cache_storage_dispatcher_host.h
index e82018a..5206dac 100644
--- a/content/browser/cache_storage/cache_storage_dispatcher_host.h
+++ b/content/browser/cache_storage/cache_storage_dispatcher_host.h
@@ -15,6 +15,7 @@
 
 #include "base/macros.h"
 #include "content/browser/cache_storage/cache_storage.h"
+#include "content/browser/cache_storage/cache_storage_index.h"
 #include "content/public/browser/browser_message_filter.h"
 
 namespace url {
@@ -111,7 +112,7 @@
                                     CacheStorageError error);
   void OnCacheStorageKeysCallback(int thread_id,
                                   int request_id,
-                                  const std::vector<std::string>& strings);
+                                  const CacheStorageIndex& cache_index);
   void OnCacheStorageMatchCallback(
       int thread_id,
       int request_id,
diff --git a/content/browser/cache_storage/cache_storage_index.cc b/content/browser/cache_storage/cache_storage_index.cc
new file mode 100644
index 0000000..a5ddcb33
--- /dev/null
+++ b/content/browser/cache_storage/cache_storage_index.cc
@@ -0,0 +1,114 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/cache_storage/cache_storage_index.h"
+
+#include <utility>
+
+namespace content {
+
+CacheStorageIndex::CacheStorageIndex()
+    : doomed_cache_metadata_("", CacheStorage::kSizeUnknown) {
+  ClearDoomedCache();
+}
+
+CacheStorageIndex::~CacheStorageIndex() = default;
+
+CacheStorageIndex& CacheStorageIndex::operator=(CacheStorageIndex&& rhs) {
+  DCHECK(!has_doomed_cache_);
+  ordered_cache_metadata_ = std::move(rhs.ordered_cache_metadata_);
+  cache_metadata_map_ = std::move(rhs.cache_metadata_map_);
+  storage_size_ = rhs.storage_size_;
+  rhs.storage_size_ = CacheStorage::kSizeUnknown;
+  return *this;
+}
+
+void CacheStorageIndex::Insert(const CacheMetadata& cache_metadata) {
+  DCHECK(!has_doomed_cache_);
+  DCHECK(cache_metadata_map_.find(cache_metadata.name) ==
+         cache_metadata_map_.end());
+  ordered_cache_metadata_.push_back(cache_metadata);
+  cache_metadata_map_[cache_metadata.name] = --ordered_cache_metadata_.end();
+  storage_size_ = CacheStorage::kSizeUnknown;
+}
+
+void CacheStorageIndex::Delete(const std::string& cache_name) {
+  DCHECK(!has_doomed_cache_);
+  auto it = cache_metadata_map_.find(cache_name);
+  DCHECK(it != cache_metadata_map_.end());
+  ordered_cache_metadata_.erase(it->second);
+  cache_metadata_map_.erase(it);
+  storage_size_ = CacheStorage::kSizeUnknown;
+}
+
+bool CacheStorageIndex::SetCacheSize(const std::string& cache_name,
+                                     int64_t size) {
+  if (has_doomed_cache_)
+    DCHECK_NE(cache_name, doomed_cache_metadata_.name);
+  auto it = cache_metadata_map_.find(cache_name);
+  DCHECK(it != cache_metadata_map_.end());
+  if (it->second->size == size)
+    return false;
+  it->second->size = size;
+  storage_size_ = CacheStorage::kSizeUnknown;
+  return true;
+}
+
+int64_t CacheStorageIndex::GetCacheSize(const std::string& cache_name) const {
+  const auto& it = cache_metadata_map_.find(cache_name);
+  if (it == cache_metadata_map_.end())
+    return CacheStorage::kSizeUnknown;
+  return it->second->size;
+}
+
+int64_t CacheStorageIndex::GetStorageSize() {
+  if (storage_size_ == CacheStorage::kSizeUnknown)
+    UpdateStorageSize();
+  return storage_size_;
+}
+
+void CacheStorageIndex::UpdateStorageSize() {
+  int64_t storage_size = 0;
+  storage_size_ = CacheStorage::kSizeUnknown;
+  for (const CacheMetadata& info : ordered_cache_metadata_) {
+    if (info.size == CacheStorage::kSizeUnknown)
+      return;
+    storage_size += info.size;
+  }
+  storage_size_ = storage_size;
+}
+
+void CacheStorageIndex::DoomCache(const std::string& cache_name) {
+  DCHECK(!has_doomed_cache_);
+  auto map_it = cache_metadata_map_.find(cache_name);
+  DCHECK(map_it != cache_metadata_map_.end());
+  doomed_cache_metadata_ = std::move(*(map_it->second));
+  after_doomed_cache_metadata_ = ordered_cache_metadata_.erase(map_it->second);
+  cache_metadata_map_.erase(map_it);
+  storage_size_ = CacheStorage::kSizeUnknown;
+  has_doomed_cache_ = true;
+}
+
+void CacheStorageIndex::FinalizeDoomedCache() {
+  DCHECK(has_doomed_cache_);
+  ClearDoomedCache();
+}
+
+void CacheStorageIndex::RestoreDoomedCache() {
+  DCHECK(has_doomed_cache_);
+  const auto cache_name = doomed_cache_metadata_.name;
+  cache_metadata_map_[cache_name] = ordered_cache_metadata_.insert(
+      after_doomed_cache_metadata_, std::move(doomed_cache_metadata_));
+  after_doomed_cache_metadata_ = ordered_cache_metadata_.end();
+  storage_size_ = CacheStorage::kSizeUnknown;
+  ClearDoomedCache();
+}
+
+void CacheStorageIndex::ClearDoomedCache() {
+  doomed_cache_metadata_.name.clear();
+  after_doomed_cache_metadata_ = ordered_cache_metadata_.end();
+  has_doomed_cache_ = false;
+}
+
+}  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_index.h b/content/browser/cache_storage/cache_storage_index.h
new file mode 100644
index 0000000..3665ec6
--- /dev/null
+++ b/content/browser/cache_storage/cache_storage_index.h
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_
+#define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_
+
+#include <list>
+#include <string>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "content/browser/cache_storage/cache_storage.h"
+
+namespace content {
+
+class CacheMetadata;
+
+// CacheStorageIndex maintains an ordered list of metadata (CacheMetadata)
+// for each cache owned by a CacheStorage object. This class is not thread safe,
+// and is owned by the CacheStorage.
+class CONTENT_EXPORT CacheStorageIndex {
+ public:
+  struct CacheMetadata {
+    CacheMetadata(const std::string& name, int64_t size)
+        : name(name), size(size) {}
+    std::string name;
+    // The size (in bytes) of the cache. Set to CacheStorage::kSizeUnknown if
+    // size not known.
+    int64_t size;
+  };
+
+  CacheStorageIndex();
+  ~CacheStorageIndex();
+
+  CacheStorageIndex& operator=(CacheStorageIndex&& rhs);
+
+  void Insert(const CacheMetadata& cache_metadata);
+  void Delete(const std::string& cache_name);
+
+  // Sets the cache size. Returns true if the new size is different than the
+  // current size else false.
+  bool SetCacheSize(const std::string& cache_name, int64_t size);
+
+  // Return the size (in bytes) of the specified cache. Will return
+  // CacheStorage::kSizeUnknown if the specified cache does not exist.
+  int64_t GetCacheSize(const std::string& cache_name) const;
+
+  const std::list<CacheMetadata>& ordered_cache_metadata() const {
+    return ordered_cache_metadata_;
+  }
+
+  size_t num_entries() const { return ordered_cache_metadata_.size(); }
+
+  // Will calculate (if necessary), and return the total sum of all cache sizes.
+  int64_t GetStorageSize();
+
+  // Mark the cache as doomed. This removes the cache metadata from the index.
+  // All const methods (eg: num_entries) will behave as if the doomed cache is
+  // not present in the index. Prior to calling any non-const method the doomed
+  // cache must either be finalized (by calling FinalizeDoomedCache) or restored
+  // (by calling RestoreDoomedCache).
+  //
+  // RestoreDoomedCache restores the metadata to the index at the original
+  // position prior to calling DoomCache.
+  void DoomCache(const std::string& cache_name);
+  void FinalizeDoomedCache();
+  void RestoreDoomedCache();
+
+ private:
+  void UpdateStorageSize();
+  void ClearDoomedCache();
+
+  // Use a list to keep saved iterators valid during insert/erase.
+  // Note: ordered by cache creation.
+  std::list<CacheMetadata> ordered_cache_metadata_;
+  std::unordered_map<std::string, std::list<CacheMetadata>::iterator>
+      cache_metadata_map_;
+
+  // The total size of all caches in this store.
+  int64_t storage_size_ = CacheStorage::kSizeUnknown;
+
+  // The doomed cache metadata saved when calling DoomCache.
+  CacheMetadata doomed_cache_metadata_;
+  std::list<CacheMetadata>::iterator after_doomed_cache_metadata_;
+  bool has_doomed_cache_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(CacheStorageIndex);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_
diff --git a/content/browser/cache_storage/cache_storage_index_unittest.cc b/content/browser/cache_storage/cache_storage_index_unittest.cc
new file mode 100644
index 0000000..4e6db8e3
--- /dev/null
+++ b/content/browser/cache_storage/cache_storage_index_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/cache_storage/cache_storage_index.h"
+
+#include <list>
+#include <utility>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class CacheStorageIndexTest : public testing::Test {
+ public:
+  void SetUp() override {}
+  void TearDown() override {}
+  CacheStorageIndexTest() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CacheStorageIndexTest);
+};
+
+TEST_F(CacheStorageIndexTest, TestDefaultConstructor) {
+  CacheStorageIndex index;
+  EXPECT_EQ(0u, index.num_entries());
+  EXPECT_TRUE(index.ordered_cache_metadata().empty());
+  EXPECT_EQ(0u, index.GetStorageSize());
+}
+
+TEST_F(CacheStorageIndexTest, TestSetCacheSize) {
+  CacheStorageIndex index;
+  index.Insert(CacheStorageIndex::CacheMetadata("foo", 12));
+  index.Insert(CacheStorageIndex::CacheMetadata("bar", 19));
+  index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000));
+  EXPECT_EQ(3u, index.num_entries());
+  ASSERT_EQ(3u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031, index.GetStorageSize());
+
+  EXPECT_TRUE(index.SetCacheSize("baz", 2000));
+  EXPECT_EQ(2031, index.GetStorageSize());
+
+  EXPECT_FALSE(index.SetCacheSize("baz", 2000));
+  EXPECT_EQ(2031, index.GetStorageSize());
+
+  EXPECT_EQ(2000, index.GetCacheSize("baz"));
+  EXPECT_EQ(CacheStorage::kSizeUnknown, index.GetCacheSize("<not-present>"));
+}
+
+TEST_F(CacheStorageIndexTest, TestDoomCache) {
+  CacheStorageIndex index;
+  index.Insert(CacheStorageIndex::CacheMetadata("foo", 12));
+  index.Insert(CacheStorageIndex::CacheMetadata("bar", 19));
+  index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000));
+  EXPECT_EQ(3u, index.num_entries());
+  ASSERT_EQ(3u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031, index.GetStorageSize());
+
+  index.DoomCache("bar");
+  EXPECT_EQ(2u, index.num_entries());
+  ASSERT_EQ(2u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031 - 19, index.GetStorageSize());
+  index.RestoreDoomedCache();
+  EXPECT_EQ(3u, index.num_entries());
+  ASSERT_EQ(3u, index.ordered_cache_metadata().size());
+  auto it = index.ordered_cache_metadata().begin();
+  EXPECT_EQ("foo", (it++)->name);
+  EXPECT_EQ("bar", (it++)->name);
+  EXPECT_EQ("baz", (it++)->name);
+  EXPECT_EQ(1031, index.GetStorageSize());
+
+  index.DoomCache("foo");
+  EXPECT_EQ(2u, index.num_entries());
+  ASSERT_EQ(2u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031 - 12, index.GetStorageSize());
+  index.FinalizeDoomedCache();
+  EXPECT_EQ(2u, index.num_entries());
+  ASSERT_EQ(2u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031 - 12, index.GetStorageSize());
+}
+
+TEST_F(CacheStorageIndexTest, TestDelete) {
+  CacheStorageIndex index;
+  index.Insert(CacheStorageIndex::CacheMetadata("bar", 19));
+  index.Insert(CacheStorageIndex::CacheMetadata("foo", 12));
+  index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000));
+  EXPECT_EQ(3u, index.num_entries());
+  ASSERT_EQ(3u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031, index.GetStorageSize());
+
+  auto it = index.ordered_cache_metadata().begin();
+  EXPECT_EQ("bar", it->name);
+  EXPECT_EQ(19u, it->size);
+  it++;
+  EXPECT_EQ("foo", it->name);
+  EXPECT_EQ(12u, it->size);
+  it++;
+  EXPECT_EQ("baz", it->name);
+  EXPECT_EQ(1000u, it->size);
+
+  index.Delete("bar");
+  EXPECT_EQ(2u, index.num_entries());
+  ASSERT_EQ(2u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1012, index.GetStorageSize());
+
+  it = index.ordered_cache_metadata().begin();
+  EXPECT_EQ("foo", it->name);
+  EXPECT_EQ(12u, it->size);
+  it++;
+  EXPECT_EQ("baz", it->name);
+  EXPECT_EQ(1000u, it->size);
+
+  index.Delete("baz");
+  EXPECT_EQ(1u, index.num_entries());
+  ASSERT_EQ(1u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(12, index.GetStorageSize());
+
+  it = index.ordered_cache_metadata().begin();
+  EXPECT_EQ("foo", it->name);
+  EXPECT_EQ(12u, it->size);
+}
+
+TEST_F(CacheStorageIndexTest, TestInsert) {
+  CacheStorageIndex index;
+  index.Insert(CacheStorageIndex::CacheMetadata("foo", 12));
+  index.Insert(CacheStorageIndex::CacheMetadata("bar", 19));
+  index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000));
+  EXPECT_EQ(3u, index.num_entries());
+  ASSERT_EQ(3u, index.ordered_cache_metadata().size());
+  EXPECT_EQ(1031, index.GetStorageSize());
+}
+
+TEST_F(CacheStorageIndexTest, TestMoveOperator) {
+  CacheStorageIndex index;
+  index.Insert(CacheStorageIndex::CacheMetadata("foo", 12));
+  index.Insert(CacheStorageIndex::CacheMetadata("bar", 19));
+  index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000));
+
+  CacheStorageIndex index2;
+  index2 = std::move(index);
+
+  EXPECT_EQ(3u, index2.num_entries());
+  EXPECT_EQ(3u, index2.ordered_cache_metadata().size());
+  ASSERT_EQ(1031, index2.GetStorageSize());
+
+  EXPECT_EQ(0u, index.num_entries());
+  EXPECT_TRUE(index.ordered_cache_metadata().empty());
+  EXPECT_EQ(0u, index.GetStorageSize());
+
+  auto it = index2.ordered_cache_metadata().begin();
+  EXPECT_EQ("foo", it->name);
+  EXPECT_EQ(12u, it->size);
+  it++;
+  EXPECT_EQ("bar", it->name);
+  EXPECT_EQ(19u, it->size);
+  it++;
+  EXPECT_EQ("baz", it->name);
+  EXPECT_EQ(1000u, it->size);
+
+  EXPECT_EQ(3u, index2.num_entries());
+  ASSERT_EQ(3u, index2.ordered_cache_metadata().size());
+  EXPECT_EQ(1031, index2.GetStorageSize());
+}
+
+}  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_manager.cc b/content/browser/cache_storage/cache_storage_manager.cc
index 6c136ae..f0516f9e 100644
--- a/content/browser/cache_storage/cache_storage_manager.cc
+++ b/content/browser/cache_storage/cache_storage_manager.cc
@@ -8,7 +8,6 @@
 
 #include <map>
 #include <set>
-#include <string>
 #include <utility>
 
 #include "base/barrier_closure.h"
@@ -51,8 +50,21 @@
                                          : storage::kQuotaErrorAbort));
 }
 
-// Open the various cache directories' index files and extract their origins and
-// last modified times.
+// Calculate the sum of all cache sizes in this store, but only if all sizes are
+// known. If one or more sizes are not known then return kSizeUnknown.
+int64_t GetCacheStorageSize(const proto::CacheStorageIndex& index) {
+  int64_t storage_size = 0;
+  for (int i = 0, max = index.cache_size(); i < max; ++i) {
+    const proto::CacheStorageIndex::Cache& cache = index.cache(i);
+    if (!cache.has_size() || cache.size() == CacheStorage::kSizeUnknown)
+      return CacheStorage::kSizeUnknown;
+    storage_size += cache.size();
+  }
+  return storage_size;
+}
+
+// Open the various cache directories' index files and extract their origins,
+// sizes (if current), and last modified times.
 void ListOriginsAndLastModifiedOnTaskRunner(
     std::vector<CacheStorageUsageInfo>* usages,
     base::FilePath root_path) {
@@ -61,16 +73,23 @@
 
   base::FilePath path;
   while (!(path = file_enum.Next()).empty()) {
+    base::FilePath index_path = path.AppendASCII(CacheStorage::kIndexFileName);
+    base::File::Info file_info;
+    base::Time index_last_modified;
+    if (GetFileInfo(index_path, &file_info))
+      index_last_modified = file_info.last_modified;
     std::string protobuf;
     base::ReadFileToString(path.AppendASCII(CacheStorage::kIndexFileName),
                            &protobuf);
     proto::CacheStorageIndex index;
     if (index.ParseFromString(protobuf)) {
       if (index.has_origin()) {
-        base::File::Info file_info;
         if (base::GetFileInfo(path, &file_info)) {
+          int64_t storage_size = CacheStorage::kSizeUnknown;
+          if (file_info.last_modified < index_last_modified)
+            storage_size = GetCacheStorageSize(index);
           usages->push_back(CacheStorageUsageInfo(
-              GURL(index.origin()), 0 /* size */, file_info.last_modified));
+              GURL(index.origin()), storage_size, file_info.last_modified));
         }
       }
     }
@@ -117,6 +136,7 @@
                            int64_t size) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  DCHECK_NE(size, CacheStorage::kSizeUnknown);
   usage->total_size_bytes = size;
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
 }
@@ -186,7 +206,7 @@
 
 void CacheStorageManager::EnumerateCaches(
     const GURL& origin,
-    const CacheStorage::StringsCallback& callback) {
+    const CacheStorage::IndexCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   CacheStorage* cache_storage = FindOrCreateCacheStorage(origin);
@@ -276,6 +296,10 @@
                  callback));
 
   for (CacheStorageUsageInfo& usage : *usages_ptr) {
+    if (usage.total_size_bytes != CacheStorage::kSizeUnknown) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, barrier_closure);
+      continue;
+    }
     CacheStorage* cache_storage = FindOrCreateCacheStorage(usage.origin);
     cache_storage->Size(
         base::Bind(&OneOriginSizeReported, barrier_closure, &usage));
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h
index f9f95b0..393f1ce 100644
--- a/content/browser/cache_storage/cache_storage_manager.h
+++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -66,7 +66,7 @@
                    const std::string& cache_name,
                    const CacheStorage::BoolAndErrorCallback& callback);
   void EnumerateCaches(const GURL& origin,
-                       const CacheStorage::StringsCallback& callback);
+                       const CacheStorage::IndexCallback& callback);
   void MatchCache(const GURL& origin,
                   const std::string& cache_name,
                   std::unique_ptr<ServiceWorkerFetchRequest> request,
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 6ebb69c..59ba128 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -7,9 +7,11 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <list>
 #include <set>
 #include <utility>
 
+#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -22,8 +24,10 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage.pb.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
+#include "content/browser/cache_storage/cache_storage_index.h"
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
 #include "content/browser/quota/mock_quota_manager_proxy.h"
 #include "content/public/browser/browser_thread.h"
@@ -44,6 +48,38 @@
 
 namespace content {
 
+namespace {
+
+bool IsIndexFileCurrent(const base::FilePath& cache_dir) {
+  base::File::Info info;
+  const base::FilePath index_path =
+      cache_dir.AppendASCII(CacheStorage::kIndexFileName);
+  if (!GetFileInfo(index_path, &info))
+    return false;
+  base::Time index_last_modified = info.last_modified;
+
+  base::FileEnumerator enumerator(cache_dir, false,
+                                  base::FileEnumerator::DIRECTORIES);
+  for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
+       file_path = enumerator.Next()) {
+    if (!GetFileInfo(file_path, &info))
+      return false;
+    if (index_last_modified < info.last_modified)
+      return false;
+  }
+
+  return true;
+}
+
+void CopyCacheStorageIndex(CacheStorageIndex* dest,
+                           const CacheStorageIndex& src) {
+  DCHECK_EQ(0U, dest->num_entries());
+  for (const auto& cache_metadata : src.ordered_cache_metadata())
+    dest->Insert(cache_metadata);
+}
+
+}  // anonymous namespace
+
 // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
 // the memory.
 std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler(
@@ -65,59 +101,22 @@
         origin2_("http://example2.com") {}
 
   void SetUp() override {
-    ChromeBlobStorageContext* blob_storage_context(
-        ChromeBlobStorageContext::GetFor(&browser_context_));
-    // Wait for ChromeBlobStorageContext to finish initializing.
-    base::RunLoop().RunUntilIdle();
-
-    blob_storage_context_ = blob_storage_context->context();
-
-    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
-    url_request_job_factory_->SetProtocolHandler(
-        "blob", CreateMockBlobProtocolHandler(blob_storage_context->context()));
-
-    net::URLRequestContext* url_request_context =
-        BrowserContext::GetDefaultStoragePartition(&browser_context_)->
-              GetURLRequestContext()->GetURLRequestContext();
-
-    url_request_context->set_job_factory(url_request_job_factory_.get());
-
-    const bool is_incognito = MemoryOnly();
     base::FilePath temp_dir_path;
-    if (!is_incognito) {
+    if (!MemoryOnly())
       ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-      temp_dir_path = temp_dir_.GetPath();
-    }
 
-    quota_policy_ = new MockSpecialStoragePolicy;
-    mock_quota_manager_ = new MockQuotaManager(
-        is_incognito, temp_dir_path, base::ThreadTaskRunnerHandle::Get().get(),
-        base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get());
-    mock_quota_manager_->SetQuota(
-        GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100);
-    mock_quota_manager_->SetQuota(
-        GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100);
-
-    quota_manager_proxy_ = new MockQuotaManagerProxy(
-        mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get());
-
-    cache_manager_ = CacheStorageManager::Create(
-        temp_dir_path, base::ThreadTaskRunnerHandle::Get(),
-        quota_manager_proxy_);
-
-    cache_manager_->SetBlobParametersForCache(
-        BrowserContext::GetDefaultStoragePartition(&browser_context_)->
-            GetURLRequestContext(),
-        blob_storage_context->context()->AsWeakPtr());
+    CreateStorageManager();
   }
 
-  void TearDown() override {
-    quota_manager_proxy_->SimulateQuotaManagerDestroyed();
-    base::RunLoop().RunUntilIdle();
-  }
+  void TearDown() override { DestroyStorageManager(); }
 
   virtual bool MemoryOnly() { return false; }
 
+  void BoolCallback(base::RunLoop* run_loop, bool value) {
+    callback_bool_ = value;
+    run_loop->Quit();
+  }
+
   void BoolAndErrorCallback(base::RunLoop* run_loop,
                             bool value,
                             CacheStorageError error) {
@@ -135,12 +134,24 @@
     run_loop->Quit();
   }
 
-  void StringsCallback(base::RunLoop* run_loop,
-                       const std::vector<std::string>& strings) {
-    callback_strings_ = strings;
+  void CacheMetadataCallback(base::RunLoop* run_loop,
+                             const CacheStorageIndex& cache_index) {
+    callback_cache_index_ = CacheStorageIndex();
+    CopyCacheStorageIndex(&callback_cache_index_, cache_index);
     run_loop->Quit();
   }
 
+  const std::string& GetFirstIndexName() const {
+    return callback_cache_index_.ordered_cache_metadata().front().name;
+  }
+
+  std::vector<std::string> GetIndexNames() const {
+    std::vector<std::string> cache_names;
+    for (const auto& metadata : callback_cache_index_.ordered_cache_metadata())
+      cache_names.push_back(metadata.name);
+    return cache_names;
+  }
+
   void CachePutCallback(base::RunLoop* run_loop, CacheStorageError error) {
     callback_error_ = error;
     run_loop->Quit();
@@ -157,6 +168,84 @@
     run_loop->Quit();
   }
 
+  void CreateStorageManager() {
+    ChromeBlobStorageContext* blob_storage_context(
+        ChromeBlobStorageContext::GetFor(&browser_context_));
+    // Wait for ChromeBlobStorageContext to finish initializing.
+    base::RunLoop().RunUntilIdle();
+
+    blob_storage_context_ = blob_storage_context->context();
+
+    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
+    url_request_job_factory_->SetProtocolHandler(
+        "blob", CreateMockBlobProtocolHandler(blob_storage_context->context()));
+
+    net::URLRequestContext* url_request_context =
+        BrowserContext::GetDefaultStoragePartition(&browser_context_)->
+              GetURLRequestContext()->GetURLRequestContext();
+
+    url_request_context->set_job_factory(url_request_job_factory_.get());
+
+    base::FilePath temp_dir_path;
+    if (!MemoryOnly())
+      temp_dir_path = temp_dir_.GetPath();
+
+    quota_policy_ = new MockSpecialStoragePolicy;
+    mock_quota_manager_ = new MockQuotaManager(
+        MemoryOnly(), temp_dir_path, base::ThreadTaskRunnerHandle::Get().get(),
+        base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get());
+    mock_quota_manager_->SetQuota(
+        GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100);
+    mock_quota_manager_->SetQuota(
+        GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100);
+
+    quota_manager_proxy_ = new MockQuotaManagerProxy(
+        mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get());
+
+    cache_manager_ = CacheStorageManager::Create(
+        temp_dir_path, base::ThreadTaskRunnerHandle::Get(),
+        quota_manager_proxy_);
+
+    cache_manager_->SetBlobParametersForCache(
+        BrowserContext::GetDefaultStoragePartition(&browser_context_)
+            ->GetURLRequestContext(),
+        blob_storage_context->context()->AsWeakPtr());
+  }
+
+  bool FlushCacheStorageIndex(const GURL& origin) {
+    callback_bool_ = false;
+    base::RunLoop loop;
+    bool write_was_scheduled =
+        CacheStorageForOrigin(origin)->InitiateScheduledIndexWriteForTest(
+            base::Bind(&CacheStorageManagerTest::BoolCallback,
+                       base::Unretained(this), &loop));
+    loop.Run();
+    DCHECK(callback_bool_);
+    return write_was_scheduled;
+  }
+
+  void DestroyStorageManager() {
+    if (quota_manager_proxy_)
+      quota_manager_proxy_->SimulateQuotaManagerDestroyed();
+    base::RunLoop().RunUntilIdle();
+    quota_manager_proxy_ = nullptr;
+
+    url_request_job_factory_.reset();
+    blob_storage_context_ = nullptr;
+
+    quota_policy_ = nullptr;
+    mock_quota_manager_ = nullptr;
+
+    callback_cache_handle_ = nullptr;
+    callback_bool_ = false;
+    callback_cache_handle_response_ = nullptr;
+    callback_data_handle_ = nullptr;
+    callback_cache_index_ = CacheStorageIndex();
+    callback_all_origins_usage_.clear();
+
+    cache_manager_ = nullptr;
+  }
+
   bool Open(const GURL& origin, const std::string& cache_name) {
     base::RunLoop loop;
     cache_manager_->OpenCache(
@@ -198,10 +287,10 @@
   size_t Keys(const GURL& origin) {
     base::RunLoop loop;
     cache_manager_->EnumerateCaches(
-        origin, base::Bind(&CacheStorageManagerTest::StringsCallback,
+        origin, base::Bind(&CacheStorageManagerTest::CacheMetadataCallback,
                            base::Unretained(this), base::Unretained(&loop)));
     loop.Run();
-    return callback_strings_.size();
+    return callback_cache_index_.num_entries();
   }
 
   bool StorageMatch(const GURL& origin,
@@ -398,7 +487,7 @@
   CacheStorageError callback_error_;
   std::unique_ptr<ServiceWorkerResponse> callback_cache_handle_response_;
   std::unique_ptr<storage::BlobDataHandle> callback_data_handle_;
-  std::vector<std::string> callback_strings_;
+  CacheStorageIndex callback_cache_index_;
 
   const GURL origin1_;
   const GURL origin2_;
@@ -511,9 +600,9 @@
   std::vector<std::string> expected_keys;
   expected_keys.push_back("foo");
   expected_keys.push_back("bar");
-  EXPECT_EQ(expected_keys, callback_strings_);
+  EXPECT_EQ(expected_keys, GetIndexNames());
   EXPECT_EQ(1u, Keys(origin2_));
-  EXPECT_STREQ("baz", callback_strings_[0].c_str());
+  EXPECT_STREQ("baz", GetFirstIndexName().c_str());
 }
 
 TEST_P(CacheStorageManagerTestP, DeletedKeysGone) {
@@ -522,7 +611,7 @@
   EXPECT_TRUE(Open(origin2_, "baz"));
   EXPECT_TRUE(Delete(origin1_, "bar"));
   EXPECT_EQ(1u, Keys(origin1_));
-  EXPECT_STREQ("foo", callback_strings_[0].c_str());
+  EXPECT_STREQ("foo", GetFirstIndexName().c_str());
 }
 
 TEST_P(CacheStorageManagerTestP, StorageMatchEntryExists) {
@@ -668,7 +757,7 @@
   EXPECT_TRUE(Open(origin1_, "你好"));
   EXPECT_EQ(callback_cache_handle_->value(), cache_handle->value());
   EXPECT_EQ(1u, Keys(origin1_));
-  EXPECT_STREQ("你好", callback_strings_[0].c_str());
+  EXPECT_STREQ("你好", GetFirstIndexName().c_str());
 }
 
 TEST_F(CacheStorageManagerTest, EmptyKey) {
@@ -678,7 +767,7 @@
   EXPECT_TRUE(Open(origin1_, ""));
   EXPECT_EQ(cache_handle->value(), callback_cache_handle_->value());
   EXPECT_EQ(1u, Keys(origin1_));
-  EXPECT_STREQ("", callback_strings_[0].c_str());
+  EXPECT_STREQ("", GetFirstIndexName().c_str());
   EXPECT_TRUE(Has(origin1_, ""));
   EXPECT_TRUE(Delete(origin1_, ""));
   EXPECT_EQ(0u, Keys(origin1_));
@@ -696,7 +785,7 @@
   std::vector<std::string> expected_keys;
   expected_keys.push_back("foo");
   expected_keys.push_back("baz");
-  EXPECT_EQ(expected_keys, callback_strings_);
+  EXPECT_EQ(expected_keys, GetIndexNames());
 }
 
 TEST_F(CacheStorageManagerMemoryOnlyTest, DataLostWhenMemoryOnly) {
@@ -713,7 +802,7 @@
   const std::string bad_name = "../../../../../../../../../../../../../../foo";
   EXPECT_TRUE(Open(origin1_, bad_name));
   EXPECT_EQ(1u, Keys(origin1_));
-  EXPECT_STREQ(bad_name.c_str(), callback_strings_[0].c_str());
+  EXPECT_STREQ(bad_name.c_str(), GetFirstIndexName().c_str());
 }
 
 TEST_F(CacheStorageManagerTest, BadOriginName) {
@@ -722,7 +811,7 @@
   GURL bad_origin("http://../../../../../../../../../../../../../../foo");
   EXPECT_TRUE(Open(bad_origin, "foo"));
   EXPECT_EQ(1u, Keys(bad_origin));
-  EXPECT_STREQ("foo", callback_strings_[0].c_str());
+  EXPECT_STREQ("foo", GetFirstIndexName().c_str());
 }
 
 // With a persistent cache if the client drops its reference to a
@@ -776,6 +865,58 @@
   EXPECT_FALSE(CacheMatch(original_handle->value(), kBazURL));
 }
 
+// Deleted caches can still be modified, but all changes will eventually be
+// thrown away when all references are released.
+TEST_F(CacheStorageManagerTest, DeletedCacheIgnoredInIndex) {
+  const GURL kFooURL("http://example.com/foo");
+  const GURL kBarURL("http://example.com/bar");
+  const GURL kBazURL("http://example.com/baz");
+  const std::string kCacheName = "foo";
+
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  auto original_handle = std::move(callback_cache_handle_);
+  EXPECT_TRUE(CachePut(original_handle->value(), kFooURL));
+  EXPECT_TRUE(Delete(origin1_, kCacheName));
+
+  // Now a second cache using the same name, but with different data.
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  auto new_handle = std::move(callback_cache_handle_);
+  EXPECT_TRUE(CachePut(new_handle->value(), kFooURL));
+  EXPECT_TRUE(CachePut(new_handle->value(), kBarURL));
+  EXPECT_TRUE(CachePut(new_handle->value(), kBazURL));
+  auto new_cache_size = Size(origin1_);
+
+  // Now modify the first cache.
+  EXPECT_TRUE(CachePut(original_handle->value(), kBarURL));
+
+  // Now deref both caches, and recreate the storage manager.
+  original_handle = nullptr;
+  new_handle = nullptr;
+  EXPECT_TRUE(FlushCacheStorageIndex(origin1_));
+  DestroyStorageManager();
+  CreateStorageManager();
+
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  EXPECT_EQ(new_cache_size, Size(origin1_));
+}
+
+TEST_F(CacheStorageManagerTest, CacheSizeCorrectAfterReopen) {
+  const GURL kFooURL("http://example.com/foo");
+  const std::string kCacheName = "foo";
+
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  auto original_handle = std::move(callback_cache_handle_);
+  EXPECT_TRUE(CachePut(original_handle->value(), kFooURL));
+  auto size_before_close = Size(origin1_);
+  EXPECT_GT(size_before_close, 0);
+
+  DestroyStorageManager();
+  CreateStorageManager();
+
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  EXPECT_EQ(size_before_close, Size(origin1_));
+}
+
 // With a memory cache the cache can't be freed from memory until the client
 // calls delete.
 TEST_F(CacheStorageManagerMemoryOnlyTest, MemoryLosesReferenceOnlyAfterDelete) {
@@ -865,6 +1006,125 @@
   }
 }
 
+TEST_F(CacheStorageManagerTest, GetAllOriginsUsageWithOldIndex) {
+  // Write a single value (V1) to the cache.
+  const GURL kFooURL = origin1_.Resolve("foo");
+  const std::string kCacheName = "foo";
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  std::unique_ptr<CacheStorageCacheHandle> original_handle =
+      std::move(callback_cache_handle_);
+
+  EXPECT_TRUE(CachePut(original_handle->value(), kFooURL));
+  int64_t cache_size_v1 = Size(origin1_);
+  base::FilePath storage_dir = original_handle->value()->path().DirName();
+  original_handle = nullptr;
+  EXPECT_GE(cache_size_v1, 0);
+
+  // Close the caches and cache manager.
+  EXPECT_TRUE(FlushCacheStorageIndex(origin1_));
+  DestroyStorageManager();
+
+  // Save a copy of the V1 index.
+  EXPECT_TRUE(IsIndexFileCurrent(storage_dir));
+  base::FilePath index_path = storage_dir.AppendASCII("index.txt");
+  EXPECT_TRUE(base::PathExists(index_path));
+  base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak");
+  EXPECT_TRUE(base::CopyFile(index_path, backup_index_path));
+
+  // Create a new CacheStorageManager that hasn't yet loaded the origin.
+  CreateStorageManager();
+  quota_manager_proxy_->SimulateQuotaManagerDestroyed();
+  cache_manager_ = CacheStorageManager::Create(cache_manager_.get());
+
+  // Create a second value (V2) in the cache.
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  original_handle = std::move(callback_cache_handle_);
+  const GURL kBarURL = origin1_.Resolve("bar");
+  EXPECT_TRUE(CachePut(original_handle->value(), kBarURL));
+  original_handle = nullptr;
+
+  std::vector<CacheStorageUsageInfo> usage = GetAllOriginsUsage();
+  ASSERT_EQ(1ULL, usage.size());
+  int64_t usage_before_close = usage[0].total_size_bytes;
+  EXPECT_GT(usage_before_close, 0);
+
+  // Close the caches and cache manager.
+  DestroyStorageManager();
+
+  // Restore the index to the V1 state. Make the access/mod times of index file
+  // older than the other directories in the store to trigger size
+  // recalculation.
+  EXPECT_TRUE(base::CopyFile(backup_index_path, index_path));
+  base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1);
+  EXPECT_TRUE(base::TouchFile(index_path, t, t));
+  EXPECT_FALSE(IsIndexFileCurrent(storage_dir));
+
+  CreateStorageManager();
+  usage = GetAllOriginsUsage();
+  ASSERT_EQ(1ULL, usage.size());
+
+  EXPECT_EQ(usage_before_close, usage[0].total_size_bytes);
+
+  EXPECT_FALSE(usage[0].last_modified.is_null());
+}
+
+TEST_F(CacheStorageManagerTest, GetOriginSizeWithOldIndex) {
+  // Write a single value (V1) to the cache.
+  const GURL kFooURL = origin1_.Resolve("foo");
+  const std::string kCacheName = "foo";
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  std::unique_ptr<CacheStorageCacheHandle> original_handle =
+      std::move(callback_cache_handle_);
+
+  EXPECT_TRUE(CachePut(original_handle->value(), kFooURL));
+  int64_t cache_size_v1 = Size(origin1_);
+  base::FilePath storage_dir = original_handle->value()->path().DirName();
+  original_handle = nullptr;
+  EXPECT_GE(cache_size_v1, 0);
+
+  // Close the caches and cache manager.
+  EXPECT_TRUE(FlushCacheStorageIndex(origin1_));
+  DestroyStorageManager();
+
+  // Save a copy of the V1 index.
+  EXPECT_TRUE(IsIndexFileCurrent(storage_dir));
+  base::FilePath index_path = storage_dir.AppendASCII("index.txt");
+  EXPECT_TRUE(base::PathExists(index_path));
+  base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak");
+  EXPECT_TRUE(base::CopyFile(index_path, backup_index_path));
+
+  // Create a new CacheStorageManager that hasn't yet loaded the origin.
+  CreateStorageManager();
+  quota_manager_proxy_->SimulateQuotaManagerDestroyed();
+  cache_manager_ = CacheStorageManager::Create(cache_manager_.get());
+
+  // Reopen the cache and write a second value (V2).
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  original_handle = std::move(callback_cache_handle_);
+  const GURL kBarURL = origin1_.Resolve("bar");
+  EXPECT_TRUE(CachePut(original_handle->value(), kBarURL));
+  original_handle = nullptr;
+  int64_t cache_size_v2 = Size(origin1_);
+  EXPECT_GE(cache_size_v2, 0);
+
+  // Close the caches and cache manager.
+  DestroyStorageManager();
+
+  // Restore the index to the V1 state.
+  EXPECT_TRUE(base::CopyFile(backup_index_path, index_path));
+
+  // Make the access/mod times of index file older than the other files in the
+  // cache to trigger size recalculation.
+  base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1);
+  EXPECT_TRUE(base::TouchFile(index_path, t, t));
+  EXPECT_FALSE(IsIndexFileCurrent(storage_dir));
+
+  // Reopen the cache and ensure the size is correct for the V2 value.
+  CreateStorageManager();
+  EXPECT_TRUE(Open(origin1_, kCacheName));
+  EXPECT_EQ(cache_size_v2, Size(origin1_));
+}
+
 TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCaches) {
   EXPECT_TRUE(Open(origin1_, "foo"));
   EXPECT_TRUE(CachePut(callback_cache_handle_->value(),
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc
index 56e9fb10..60083d7e 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.cc
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -19,24 +19,17 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/filesystem/public/interfaces/directory.mojom.h"
-#include "components/leveldb/public/interfaces/leveldb.mojom.h"
 #include "content/browser/dom_storage/dom_storage_area.h"
 #include "content/browser/dom_storage/dom_storage_context_impl.h"
 #include "content/browser/dom_storage/dom_storage_task_runner.h"
+#include "content/browser/dom_storage/local_storage_context_mojo.h"
 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
-#include "content/browser/leveldb_wrapper_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/local_storage_usage_info.h"
 #include "content/public/browser/session_storage_usage_info.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
-#include "mojo/common/common_type_converters.h"
-#include "services/file/public/interfaces/constants.mojom.h"
-#include "services/file/public/interfaces/file_system.mojom.h"
-#include "services/service_manager/public/cpp/connection.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace content {
 namespace {
@@ -82,182 +75,6 @@
 
 }  // namespace
 
-// Used for mojo-based LocalStorage implementation (behind --mojo-local-storage
-// for now).
-class DOMStorageContextWrapper::MojoState {
- public:
-  MojoState(service_manager::Connector* connector,
-            const base::FilePath& subdirectory)
-      : connector_(connector),
-        // TODO(michaeln): Enable writing to disk when db is versioned,
-        // for now using an empty subdirectory to use an in-memory db.
-        // subdirectory_(subdirectory),
-        connection_state_(NO_CONNECTION),
-        weak_ptr_factory_(this) {}
-
-  void OpenLocalStorage(const url::Origin& origin,
-                        mojom::LevelDBWrapperRequest request);
-
- private:
-  void OnLevelDDWrapperHasNoBindings(const url::Origin& origin) {
-    DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end());
-    level_db_wrappers_.erase(origin);
-  }
-
-  void OnUserServiceConnectionComplete() {
-    CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED,
-             file_service_connection_->GetResult());
-  }
-
-  void OnUserServiceConnectionError() {
-    CHECK(false);
-  }
-
-  // Part of our asynchronous directory opening called from OpenLocalStorage().
-  void OnDirectoryOpened(filesystem::mojom::FileError err);
-  void OnDatabaseOpened(leveldb::mojom::DatabaseError status);
-
-  // The (possibly delayed) implementation of OpenLocalStorage(). Can be called
-  // directly from that function, or through |on_database_open_callbacks_|.
-  void BindLocalStorage(const url::Origin& origin,
-                        mojom::LevelDBWrapperRequest request);
-
-  service_manager::Connector* const connector_;
-  const base::FilePath subdirectory_;
-
-  enum ConnectionState {
-    NO_CONNECTION,
-    CONNECTION_IN_PROGRESS,
-    CONNECTION_FINISHED
-  } connection_state_;
-
-  std::unique_ptr<service_manager::Connection> file_service_connection_;
-
-  file::mojom::FileSystemPtr file_system_;
-  filesystem::mojom::DirectoryPtr directory_;
-
-  leveldb::mojom::LevelDBServicePtr leveldb_service_;
-  leveldb::mojom::LevelDBDatabasePtr database_;
-
-  std::vector<base::Closure> on_database_opened_callbacks_;
-
-  // Maps between an origin and its prefixed LevelDB view.
-  std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_;
-
-  base::WeakPtrFactory<MojoState> weak_ptr_factory_;
-};
-
-void DOMStorageContextWrapper::MojoState::OpenLocalStorage(
-    const url::Origin& origin,
-    mojom::LevelDBWrapperRequest request) {
-  // If we don't have a filesystem_connection_, we'll need to establish one.
-  if (connection_state_ == NO_CONNECTION) {
-    CHECK(connector_);
-    file_service_connection_ =
-        connector_->Connect(file::mojom::kServiceName);
-    connection_state_ = CONNECTION_IN_PROGRESS;
-    file_service_connection_->AddConnectionCompletedClosure(
-        base::Bind(&MojoState::OnUserServiceConnectionComplete,
-                   weak_ptr_factory_.GetWeakPtr()));
-    file_service_connection_->SetConnectionLostClosure(
-        base::Bind(&MojoState::OnUserServiceConnectionError,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    if (!subdirectory_.empty()) {
-      // We were given a subdirectory to write to. Get it and use a disk backed
-      // database.
-      file_service_connection_->GetInterface(&file_system_);
-      file_system_->GetSubDirectory(subdirectory_.AsUTF8Unsafe(),
-                                    MakeRequest(&directory_),
-                                    base::Bind(&MojoState::OnDirectoryOpened,
-                                               weak_ptr_factory_.GetWeakPtr()));
-    } else {
-      // We were not given a subdirectory. Use a memory backed database.
-      file_service_connection_->GetInterface(&leveldb_service_);
-      leveldb_service_->OpenInMemory(
-          MakeRequest(&database_), base::Bind(&MojoState::OnDatabaseOpened,
-                                              weak_ptr_factory_.GetWeakPtr()));
-    }
-  }
-
-  if (connection_state_ == CONNECTION_IN_PROGRESS) {
-    // Queue this OpenLocalStorage call for when we have a level db pointer.
-    on_database_opened_callbacks_.push_back(
-        base::Bind(&MojoState::BindLocalStorage, weak_ptr_factory_.GetWeakPtr(),
-                   origin, base::Passed(&request)));
-    return;
-  }
-
-  BindLocalStorage(origin, std::move(request));
-}
-
-void DOMStorageContextWrapper::MojoState::OnDirectoryOpened(
-    filesystem::mojom::FileError err) {
-  if (err != filesystem::mojom::FileError::OK) {
-    // We failed to open the directory; continue with startup so that we create
-    // the |level_db_wrappers_|.
-    OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR);
-    return;
-  }
-
-  // Now that we have a directory, connect to the LevelDB service and get our
-  // database.
-  file_service_connection_->GetInterface(&leveldb_service_);
-
-  leveldb_service_->Open(
-      std::move(directory_), "leveldb", MakeRequest(&database_),
-      base::Bind(&MojoState::OnDatabaseOpened, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void DOMStorageContextWrapper::MojoState::OnDatabaseOpened(
-    leveldb::mojom::DatabaseError status) {
-  if (status != leveldb::mojom::DatabaseError::OK) {
-    // If we failed to open the database, reset the service object so we pass
-    // null pointers to our wrappers.
-    database_.reset();
-    leveldb_service_.reset();
-  }
-
-  // We no longer need the file service; we've either transferred |directory_|
-  // to the leveldb service, or we got a file error and no more is possible.
-  directory_.reset();
-  file_system_.reset();
-
-  // |leveldb_| should be known to either be valid or invalid by now. Run our
-  // delayed bindings.
-  connection_state_ = CONNECTION_FINISHED;
-  for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i)
-    on_database_opened_callbacks_[i].Run();
-  on_database_opened_callbacks_.clear();
-}
-
-void DOMStorageContextWrapper::MojoState::BindLocalStorage(
-    const url::Origin& origin,
-    mojom::LevelDBWrapperRequest request) {
-  // Delay for a moment after a value is set in anticipation
-  // of other values being set, so changes are batched.
-  const int kCommitDefaultDelaySecs = 5;
-
-  // To avoid excessive IO we apply limits to the amount of data being written
-  // and the frequency of writes.
-  const int kMaxBytesPerHour = kPerStorageAreaQuota;
-  const int kMaxCommitsPerHour = 60;
-
-  auto found = level_db_wrappers_.find(origin);
-  if (found == level_db_wrappers_.end()) {
-    level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>(
-        database_.get(), origin.Serialize(),
-        kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance,
-        base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour,
-        kMaxCommitsPerHour,
-        base::Bind(&MojoState::OnLevelDDWrapperHasNoBindings,
-                   base::Unretained(this), origin));
-    found = level_db_wrappers_.find(origin);
-  }
-
-  found->second->Bind(std::move(request));
-}
-
 DOMStorageContextWrapper::DOMStorageContextWrapper(
     service_manager::Connector* connector,
     const base::FilePath& profile_path,
@@ -266,7 +83,11 @@
   base::FilePath storage_dir;
   if (!profile_path.empty())
     storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory);
-  mojo_state_.reset(new MojoState(connector, storage_dir));
+  // TODO(michaeln): Enable writing to disk when db is versioned,
+  // for now using an empty subdirectory to use an in-memory db.
+  // subdirectory_(subdirectory),
+  mojo_state_.reset(new LocalStorageContextMojo(
+      connector, base::FilePath() /* storage_dir */));
 
   base::FilePath data_path;
   if (!profile_path.empty())
@@ -282,7 +103,7 @@
     base::TaskTraits dom_storage_traits =
         base::TaskTraits()
             .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN)
-            .WithFileIO()
+            .MayBlock()
             .WithPriority(base::TaskPriority::USER_BLOCKING);
     primary_sequence =
         base::CreateSequencedTaskRunnerWithTraits(dom_storage_traits);
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h
index e6b5dbd..2a05e800 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.h
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -33,6 +33,7 @@
 namespace content {
 
 class DOMStorageContextImpl;
+class LocalStorageContextMojo;
 
 // This is owned by Storage Partition and encapsulates all its dom storage
 // state.
@@ -93,10 +94,9 @@
 
   void PurgeMemory(DOMStorageContextImpl::PurgeOption purge_option);
 
-  // An inner class to keep all mojo-ish details together and not bleed them
-  // through the public interface.
-  class MojoState;
-  std::unique_ptr<MojoState> mojo_state_;
+  // Keep all mojo-ish details together and not bleed them through the public
+  // interface.
+  std::unique_ptr<LocalStorageContextMojo> mojo_state_;
 
   // To receive memory pressure signals.
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
new file mode 100644
index 0000000..eb34a7aa
--- /dev/null
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -0,0 +1,164 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/dom_storage/local_storage_context_mojo.h"
+
+#include "content/browser/leveldb_wrapper_impl.h"
+#include "content/common/dom_storage/dom_storage_types.h"
+#include "services/file/public/interfaces/constants.mojom.h"
+#include "services/service_manager/public/cpp/connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace content {
+
+namespace {
+const char kOriginSeparator = '\x00';
+}
+
+LocalStorageContextMojo::LocalStorageContextMojo(
+    service_manager::Connector* connector,
+    const base::FilePath& subdirectory)
+    : connector_(connector),
+      subdirectory_(subdirectory),
+      weak_ptr_factory_(this) {}
+
+LocalStorageContextMojo::~LocalStorageContextMojo() {}
+
+void LocalStorageContextMojo::OpenLocalStorage(
+    const url::Origin& origin,
+    mojom::LevelDBWrapperRequest request) {
+  // If we don't have a filesystem_connection_, we'll need to establish one.
+  if (connection_state_ == NO_CONNECTION) {
+    CHECK(connector_);
+    file_service_connection_ = connector_->Connect(file::mojom::kServiceName);
+    connection_state_ = CONNECTION_IN_PROGRESS;
+    file_service_connection_->AddConnectionCompletedClosure(
+        base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete,
+                   weak_ptr_factory_.GetWeakPtr()));
+    file_service_connection_->SetConnectionLostClosure(
+        base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    if (!subdirectory_.empty()) {
+      // We were given a subdirectory to write to. Get it and use a disk backed
+      // database.
+      file_service_connection_->GetInterface(&file_system_);
+      file_system_->GetSubDirectory(
+          subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_),
+          base::Bind(&LocalStorageContextMojo::OnDirectoryOpened,
+                     weak_ptr_factory_.GetWeakPtr()));
+    } else {
+      // We were not given a subdirectory. Use a memory backed database.
+      file_service_connection_->GetInterface(&leveldb_service_);
+      leveldb_service_->OpenInMemory(
+          MakeRequest(&database_),
+          base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
+                     weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+
+  if (connection_state_ == CONNECTION_IN_PROGRESS) {
+    // Queue this OpenLocalStorage call for when we have a level db pointer.
+    on_database_opened_callbacks_.push_back(base::Bind(
+        &LocalStorageContextMojo::BindLocalStorage,
+        weak_ptr_factory_.GetWeakPtr(), origin, base::Passed(&request)));
+    return;
+  }
+
+  BindLocalStorage(origin, std::move(request));
+}
+
+void LocalStorageContextMojo::SetDatabaseForTesting(
+    leveldb::mojom::LevelDBDatabasePtr database) {
+  database_ = std::move(database);
+  OnDatabaseOpened(leveldb::mojom::DatabaseError::OK);
+}
+
+void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings(
+    const url::Origin& origin) {
+  DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end());
+  level_db_wrappers_.erase(origin);
+}
+
+void LocalStorageContextMojo::OnUserServiceConnectionComplete() {
+  CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED,
+           file_service_connection_->GetResult());
+}
+
+void LocalStorageContextMojo::OnUserServiceConnectionError() {
+  CHECK(false);
+}
+
+// Part of our asynchronous directory opening called from OpenLocalStorage().
+void LocalStorageContextMojo::OnDirectoryOpened(
+    filesystem::mojom::FileError err) {
+  if (err != filesystem::mojom::FileError::OK) {
+    // We failed to open the directory; continue with startup so that we create
+    // the |level_db_wrappers_|.
+    OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR);
+    return;
+  }
+
+  // Now that we have a directory, connect to the LevelDB service and get our
+  // database.
+  file_service_connection_->GetInterface(&leveldb_service_);
+
+  leveldb_service_->Open(std::move(directory_), "leveldb",
+                         MakeRequest(&database_),
+                         base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LocalStorageContextMojo::OnDatabaseOpened(
+    leveldb::mojom::DatabaseError status) {
+  if (status != leveldb::mojom::DatabaseError::OK) {
+    // If we failed to open the database, reset the service object so we pass
+    // null pointers to our wrappers.
+    database_.reset();
+    leveldb_service_.reset();
+  }
+
+  // We no longer need the file service; we've either transferred |directory_|
+  // to the leveldb service, or we got a file error and no more is possible.
+  directory_.reset();
+  file_system_.reset();
+
+  // |leveldb_| should be known to either be valid or invalid by now. Run our
+  // delayed bindings.
+  connection_state_ = CONNECTION_FINISHED;
+  for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i)
+    on_database_opened_callbacks_[i].Run();
+  on_database_opened_callbacks_.clear();
+}
+
+// The (possibly delayed) implementation of OpenLocalStorage(). Can be called
+// directly from that function, or through |on_database_open_callbacks_|.
+void LocalStorageContextMojo::BindLocalStorage(
+    const url::Origin& origin,
+    mojom::LevelDBWrapperRequest request) {
+  // Delay for a moment after a value is set in anticipation
+  // of other values being set, so changes are batched.
+  const int kCommitDefaultDelaySecs = 5;
+
+  // To avoid excessive IO we apply limits to the amount of data being written
+  // and the frequency of writes.
+  const int kMaxBytesPerHour = kPerStorageAreaQuota;
+  const int kMaxCommitsPerHour = 60;
+
+  auto found = level_db_wrappers_.find(origin);
+  if (found == level_db_wrappers_.end()) {
+    level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>(
+        database_.get(), origin.Serialize() + kOriginSeparator,
+        kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance,
+        base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour,
+        kMaxCommitsPerHour,
+        base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings,
+                   base::Unretained(this), origin));
+    found = level_db_wrappers_.find(origin);
+  }
+
+  found->second->Bind(std::move(request));
+}
+
+}  // namespace content
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h
new file mode 100644
index 0000000..6ca0df4
--- /dev/null
+++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_
+#define CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "content/common/content_export.h"
+#include "content/common/leveldb_wrapper.mojom.h"
+#include "services/file/public/interfaces/file_system.mojom.h"
+#include "url/origin.h"
+
+namespace service_manager {
+class Connection;
+class Connector;
+}
+
+namespace content {
+
+class LevelDBWrapperImpl;
+
+// Used for mojo-based LocalStorage implementation (behind --mojo-local-storage
+// for now).
+class CONTENT_EXPORT LocalStorageContextMojo {
+ public:
+  LocalStorageContextMojo(service_manager::Connector* connector,
+                          const base::FilePath& subdirectory);
+  ~LocalStorageContextMojo();
+
+  void OpenLocalStorage(const url::Origin& origin,
+                        mojom::LevelDBWrapperRequest request);
+
+  void SetDatabaseForTesting(leveldb::mojom::LevelDBDatabasePtr database);
+
+ private:
+  void OnLevelDBWrapperHasNoBindings(const url::Origin& origin);
+  void OnUserServiceConnectionComplete();
+  void OnUserServiceConnectionError();
+
+  // Part of our asynchronous directory opening called from OpenLocalStorage().
+  void OnDirectoryOpened(filesystem::mojom::FileError err);
+  void OnDatabaseOpened(leveldb::mojom::DatabaseError status);
+
+  // The (possibly delayed) implementation of OpenLocalStorage(). Can be called
+  // directly from that function, or through |on_database_open_callbacks_|.
+  void BindLocalStorage(const url::Origin& origin,
+                        mojom::LevelDBWrapperRequest request);
+
+  service_manager::Connector* const connector_;
+  const base::FilePath subdirectory_;
+
+  enum ConnectionState {
+    NO_CONNECTION,
+    CONNECTION_IN_PROGRESS,
+    CONNECTION_FINISHED
+  } connection_state_ = NO_CONNECTION;
+
+  std::unique_ptr<service_manager::Connection> file_service_connection_;
+
+  file::mojom::FileSystemPtr file_system_;
+  filesystem::mojom::DirectoryPtr directory_;
+
+  leveldb::mojom::LevelDBServicePtr leveldb_service_;
+  leveldb::mojom::LevelDBDatabasePtr database_;
+
+  std::vector<base::Closure> on_database_opened_callbacks_;
+
+  // Maps between an origin and its prefixed LevelDB view.
+  std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_;
+
+  base::WeakPtrFactory<LocalStorageContextMojo> weak_ptr_factory_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
new file mode 100644
index 0000000..df2242f
--- /dev/null
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/dom_storage/local_storage_context_mojo.h"
+
+#include "base/run_loop.h"
+#include "components/leveldb/public/cpp/util.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/mock_leveldb_database.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using leveldb::StdStringToUint8Vector;
+using leveldb::Uint8VectorToStdString;
+
+namespace content {
+
+namespace {
+
+void NoOpSuccess(bool success) {}
+
+}  // namespace
+
+class LocalStorageContextMojoTest : public testing::Test {
+ public:
+  LocalStorageContextMojoTest()
+      : db_(&mock_data_),
+        db_binding_(&db_),
+        context_(nullptr, base::FilePath()) {
+    context_.SetDatabaseForTesting(db_binding_.CreateInterfacePtrAndBind());
+  }
+
+  LocalStorageContextMojo* context() { return &context_; }
+  const std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data() {
+    return mock_data_;
+  }
+
+ private:
+  TestBrowserThreadBundle thread_bundle_;
+  std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_;
+  MockLevelDBDatabase db_;
+  mojo::Binding<leveldb::mojom::LevelDBDatabase> db_binding_;
+
+  LocalStorageContextMojo context_;
+};
+
+TEST_F(LocalStorageContextMojoTest, Basic) {
+  auto key = StdStringToUint8Vector("key");
+  auto value = StdStringToUint8Vector("value");
+
+  mojom::LevelDBWrapperPtr wrapper;
+  context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                              MakeRequest(&wrapper));
+  wrapper->Put(key, value, "source", base::Bind(&NoOpSuccess));
+  wrapper.reset();
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(1u, mock_data().size());
+  EXPECT_EQ(value, mock_data().begin()->second);
+}
+
+TEST_F(LocalStorageContextMojoTest, OriginsAreIndependent) {
+  url::Origin origin1(GURL("http://foobar.com:123"));
+  url::Origin origin2(GURL("http://foobar.com:1234"));
+  auto key1 = StdStringToUint8Vector("4key");
+  auto key2 = StdStringToUint8Vector("key");
+  auto value = StdStringToUint8Vector("value");
+
+  mojom::LevelDBWrapperPtr wrapper;
+  context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
+  wrapper->Put(key1, value, "source", base::Bind(&NoOpSuccess));
+  wrapper.reset();
+
+  context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
+  wrapper->Put(key2, value, "source", base::Bind(&NoOpSuccess));
+  wrapper.reset();
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(2u, mock_data().size());
+  EXPECT_EQ(value, mock_data().begin()->second);
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 55523b85..5bde4bb 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -135,6 +135,35 @@
   return last_entry && last_entry->GetIsOverridingUserAgent();
 }
 
+// Returns true if the PageTransition in the |entry| require this navigation to
+// be treated as a reload. For e.g. navigating to the last committed url via
+// the address bar or clicking on a link which results in a navigation to the
+// last committed or pending navigation, etc.
+bool ShouldTreatNavigationAsReload(const NavigationEntry* entry) {
+  if (!entry)
+    return false;
+
+  // We treat (PAGE_TRANSITION_RELOAD | PAGE_TRANSITION_FROM_ADDRESS_BAR),
+  // PAGE_TRANSITION_TYPED or PAGE_TRANSITION_LINK transitions as navigations
+  // which should be treated as reloads.
+  if ((ui::PageTransitionCoreTypeIs(entry->GetTransitionType(),
+                                    ui::PAGE_TRANSITION_RELOAD) &&
+       (entry->GetTransitionType() & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR))) {
+    return true;
+  }
+
+  if (ui::PageTransitionCoreTypeIs(entry->GetTransitionType(),
+                                   ui::PAGE_TRANSITION_TYPED)) {
+    return true;
+  }
+
+  if (ui::PageTransitionCoreTypeIs(entry->GetTransitionType(),
+                                   ui::PAGE_TRANSITION_LINK)) {
+    return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 // NavigationControllerImpl ----------------------------------------------------
@@ -212,11 +241,14 @@
     NavigationControllerDelegate* delegate,
     BrowserContext* browser_context)
     : browser_context_(browser_context),
-      pending_entry_(NULL),
+      pending_entry_(nullptr),
+      last_pending_entry_(nullptr),
       failed_pending_entry_id_(0),
       last_committed_entry_index_(-1),
       pending_entry_index_(-1),
       transient_entry_index_(-1),
+      last_pending_entry_index_(-1),
+      last_transient_entry_index_(-1),
       delegate_(delegate),
       ssl_manager_(this),
       needs_reload_(false),
@@ -413,6 +445,12 @@
 
 void NavigationControllerImpl::LoadEntry(
     std::unique_ptr<NavigationEntryImpl> entry) {
+  // Remember the last pending entry for which we haven't received a response
+  // yet. This will be deleted in the NavigateToPendingEntry() function.
+  last_pending_entry_ = pending_entry_;
+  last_pending_entry_index_ = pending_entry_index_;
+  last_transient_entry_index_ = transient_entry_index_;
+  pending_entry_ = nullptr;
   // When navigating to a new page, we don't know for sure if we will actually
   // end up leaving the current page.  The new page load could for example
   // result in a download or a 'no content' response (e.g., a mailto: URL).
@@ -1765,10 +1803,56 @@
   // navigation to succeed.  The interstitial will stay visible until the
   // resulting DidNavigate.
   if (delegate_->GetInterstitialPage()) {
-    static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())->
-        CancelForNavigation();
+    static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())
+        ->CancelForNavigation();
   }
 
+  // The last navigation is the last pending navigation which hasn't been
+  // committed yet, or the last committed navigation.
+  NavigationEntryImpl* last_navigation =
+      last_pending_entry_ ? last_pending_entry_ : GetLastCommittedEntry();
+
+  // Convert Enter-in-omnibox to a reload. This is what Blink does in
+  // FrameLoader, but we want to handle it here so that if the navigation is
+  // redirected or handled purely on the browser side in PlzNavigate we have the
+  // same behaviour as Blink would. Note that we don't want to convert to a
+  // reload for history navigations, so this must be above the retrieval of the
+  // pending_entry_ below when pending_entry_index_ is used.
+  if (reload_type == ReloadType::NONE && last_navigation && pending_entry_ &&
+      // Please refer to the ShouldTreatNavigationAsReload() function for info
+      // on which navigations are treated as reloads. In general navigating to
+      // the last committed or pending entry via the address bar, clicking on
+      // a link, etc would be treated as reloads.
+      ShouldTreatNavigationAsReload(pending_entry_) &&
+      // Skip entries with SSL errors.
+      !last_navigation->ssl_error() &&
+      // Ignore interstitial pages
+      last_transient_entry_index_ == -1 &&
+      pending_entry_->frame_tree_node_id() == -1 &&
+      pending_entry_->GetURL() == last_navigation->GetURL() &&
+      !pending_entry_->GetHasPostData() && !last_navigation->GetHasPostData() &&
+      // This check is required for cases like view-source:, etc. Here the URL
+      // of the navigation entry would contain the url of the page, while the
+      // virtual URL contains the full URL including the view-source prefix.
+      last_navigation->GetVirtualURL() == pending_entry_->GetVirtualURL() &&
+      // This check is required for Android WebView loadDataWithBaseURL. Apps
+      // can pass in anything in the base URL and we need to ensure that these
+      // match before classifying it as a reload.
+      (pending_entry_->GetURL().SchemeIs(url::kDataScheme) &&
+               pending_entry_->GetBaseURLForDataURL().is_valid()
+           ? pending_entry_->GetBaseURLForDataURL() ==
+                 last_navigation->GetBaseURLForDataURL()
+           : true)) {
+    reload_type = ReloadType::NORMAL;
+  }
+
+  if (last_pending_entry_index_ == -1 && last_pending_entry_)
+    delete last_pending_entry_;
+
+  last_transient_entry_index_ = -1;
+  last_pending_entry_ = nullptr;
+  last_pending_entry_index_ = -1;
+
   // For session history navigations only the pending_entry_index_ is set.
   if (!pending_entry_) {
     CHECK_NE(pending_entry_index_, -1);
@@ -2011,6 +2095,11 @@
   pending_entry_index_ = -1;
 }
 
+void NavigationControllerImpl::SetPendingNavigationSSLError(bool error) {
+  if (pending_entry_)
+    pending_entry_->set_ssl_error(error);
+}
+
 void NavigationControllerImpl::DiscardTransientEntry() {
   if (transient_entry_index_ == -1)
     return;
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 0970fb9..8412539 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -201,6 +201,10 @@
   // entry is being discarded because it failed to load.
   void DiscardPendingEntry(bool was_failure);
 
+  // Sets a flag on the pending NavigationEntryImpl instance if any that the
+  // navigation failed due to an SSL error.
+  void SetPendingNavigationSSLError(bool error);
+
  private:
   friend class RestoreHelper;
 
@@ -362,6 +366,10 @@
   // the memory management.
   NavigationEntryImpl* pending_entry_;
 
+  // Navigations could occur in succession. This field holds the last pending
+  // entry for which we haven't received a response yet.
+  NavigationEntryImpl* last_pending_entry_;
+
   // If a new entry fails loading, details about it are temporarily held here
   // until the error page is shown (or 0 otherwise).
   //
@@ -386,6 +394,13 @@
   // after the transient entry will become invalid if you navigate forward.
   int transient_entry_index_;
 
+  // The index of the last pending entry if it is in entries, or -1 if it was
+  // created by LoadURL.
+  int last_pending_entry_index_;
+
+  // The index of the last transient entry. Defaults to -1.
+  int last_transient_entry_index_;
+
   // The delegate associated with the controller. Possibly NULL during
   // setup.
   NavigationControllerDelegate* delegate_;
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 080f3d2..637dec0 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -218,6 +218,7 @@
   void DidStartNavigationToPendingEntry(const GURL& url,
                                         ReloadType reload_type) override {
     navigated_url_ = url;
+    last_reload_type_ = reload_type;
   }
 
   void NavigationEntryCommitted(
@@ -262,6 +263,7 @@
  protected:
   GURL navigated_url_;
   size_t navigation_entry_committed_counter_;
+  ReloadType last_reload_type_;
 };
 
 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
@@ -5210,4 +5212,87 @@
   EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL());
 }
 
+// Tests that successive navigations with intermittent duplicate navigations
+// are correctly marked as reload in the navigation controller.
+// We test the cases where in a navigation is pending/comitted before the new
+// navigation is initiated.
+// http://crbug.com/664319
+TEST_F(NavigationControllerTest, MultipleNavigationsAndReload) {
+  NavigationControllerImpl& controller = controller_impl();
+  TestNotificationTracker notifications;
+  RegisterForAllNavNotifications(&notifications, &controller);
+
+  GURL initial_url("http://www.google.com");
+  GURL url_1("http://foo.com");
+  GURL url_2("http://foo2.com");
+
+  // Test 1.
+  // A normal navigation to initial_url should not be marked as a reload.
+  controller.LoadURL(initial_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+  EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(initial_url);
+  EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL());
+
+  main_test_rfh()->SimulateNavigationCommit(initial_url);
+  EXPECT_EQ(ReloadType::NONE, last_reload_type_);
+
+  // Test 2.
+  // A navigation to initial_url with the navigation commit delayed should be
+  // marked as a reload.
+  controller.LoadURL(initial_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+
+  EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(initial_url);
+  EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(ReloadType::NORMAL, last_reload_type_);
+
+  // Test 3.
+  // A navigation to url_1 while the navigation to intial_url is still pending
+  // should not be marked as a reload.
+  controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(url_1);
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(ReloadType::NONE, last_reload_type_);
+
+  // Test 4.
+  // A navigation to url_1 while the previous navigation to url_1 is pending
+  // should be marked as reload.
+  controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(url_1);
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(ReloadType::NORMAL, last_reload_type_);
+
+  main_test_rfh()->SimulateNavigationCommit(initial_url);
+
+  // Test 5
+  // A navigation to url_2 followed by a navigation to the previously pending
+  // url_1 should not be marked as a reload.
+  controller.LoadURL(url_2, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+  EXPECT_EQ(url_2, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(url_2);
+  EXPECT_EQ(url_2, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(ReloadType::NONE, last_reload_type_);
+
+  controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                     std::string());
+
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  main_test_rfh()->SimulateNavigationStart(url_1);
+  EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL());
+  EXPECT_EQ(ReloadType::NONE, last_reload_type_);
+
+  main_test_rfh()->SimulateNavigationCommit(url_2);
+  main_test_rfh()->SimulateNavigationCommit(url_1);
+  main_test_rfh()->SimulateNavigationCommit(url_1);
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 336e0613..79eb126 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -276,7 +276,8 @@
       can_load_local_resources_(false),
       frame_tree_node_id_(-1),
       reload_type_(ReloadType::NONE),
-      started_from_context_menu_(false) {
+      started_from_context_menu_(false),
+      ssl_error_(false) {
 #if defined(OS_ANDROID)
   has_user_gesture_ = false;
 #endif
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index 4b93695b..9782bf1 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -388,6 +388,11 @@
   // Returns the history URL for a data URL to use in Blink.
   GURL GetHistoryURLForDataURL() const;
 
+  // These flags are set when the navigation controller gets notified of an SSL
+  // error while a navigation is pending.
+  void set_ssl_error(bool error) { ssl_error_ = error; }
+  bool ssl_error() const { return ssl_error_; }
+
 #if defined(OS_ANDROID)
   base::TimeTicks intent_received_timestamp() const {
     return intent_received_timestamp_;
@@ -543,6 +548,10 @@
   // time (see TabNavigation for an example of this).
   std::map<std::string, base::string16> extra_data_;
 
+  // Set to true if the navigation controller gets notified about a SSL error
+  // for a pending navigation. Defaults to false.
+  bool ssl_error_;
+
   DISALLOW_COPY_AND_ASSIGN(NavigationEntryImpl);
 };
 
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 83ef7cac..2bacf35 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -47,6 +47,9 @@
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/gpu_switching_manager.h"
 
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_switches.h"
+#endif
 #if defined(OS_MACOSX)
 #include <ApplicationServices/ApplicationServices.h>
 #endif  // OS_MACOSX
@@ -768,6 +771,12 @@
     command_line->AppendSwitch(switches::kCreateDefaultGLContext);
   }
 
+#if defined(USE_OZONE)
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableDrmAtomic)) {
+    command_line->AppendSwitch(switches::kEnableDrmAtomic);
+  }
+#endif
 #if defined(OS_WIN)
   if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VPX_DECODE) &&
       gpu_preferences) {
diff --git a/content/browser/leveldb_wrapper_impl.cc b/content/browser/leveldb_wrapper_impl.cc
index b7cb7135..e60426a2 100644
--- a/content/browser/leveldb_wrapper_impl.cc
+++ b/content/browser/leveldb_wrapper_impl.cc
@@ -34,19 +34,6 @@
 LevelDBWrapperImpl::CommitBatch::CommitBatch() : clear_all_first(false) {}
 LevelDBWrapperImpl::CommitBatch::~CommitBatch() {}
 
-size_t LevelDBWrapperImpl::CommitBatch::GetDataSize() const {
-  if (changed_values.empty())
-    return 0;
-
-  size_t count = 0;
-  for (const auto& pair : changed_values) {
-    count += pair.first.size();
-    if (pair.second)
-      count += pair.second->size();
-  }
-  return count;
-}
-
 LevelDBWrapperImpl::LevelDBWrapperImpl(
     leveldb::mojom::LevelDBDatabase* database,
     const std::string& prefix,
@@ -122,7 +109,7 @@
 
   if (database_) {
     CreateCommitBatchIfNeeded();
-    commit_batch_->changed_values[key] = value;
+    commit_batch_->changed_keys.insert(key);
   }
 
   std::vector<uint8_t> old_value;
@@ -164,7 +151,7 @@
 
   if (database_) {
     CreateCommitBatchIfNeeded();
-    commit_batch_->changed_values[key] = base::nullopt;
+    commit_batch_->changed_keys.insert(std::move(found->first));
   }
 
   std::vector<uint8_t> old_value(std::move(found->second));
@@ -189,7 +176,7 @@
   if (database_ && !map_->empty()) {
     CreateCommitBatchIfNeeded();
     commit_batch_->clear_all_first = true;
-    commit_batch_->changed_values.clear();
+    commit_batch_->changed_keys.clear();
   }
   map_->clear();
   bytes_used_ = 0;
@@ -216,12 +203,12 @@
   callback.Run(true, found->second);
 }
 
-void LevelDBWrapperImpl::GetAll(const std::string& source,
-                                const GetAllCallback& callback) {
+void LevelDBWrapperImpl::GetAll(
+    mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback,
+    const GetAllCallback& callback) {
   if (!map_) {
-    LoadMap(
-        base::Bind(&LevelDBWrapperImpl::GetAll, base::Unretained(this),
-                   source, callback));
+    LoadMap(base::Bind(&LevelDBWrapperImpl::GetAll, base::Unretained(this),
+                       base::Passed(std::move(complete_callback)), callback));
     return;
   }
 
@@ -233,15 +220,20 @@
     all.push_back(std::move(kv));
   }
   callback.Run(leveldb::mojom::DatabaseError::OK, std::move(all));
-  observers_.ForAllPtrs(
-      [source](mojom::LevelDBObserver* observer) {
-        observer->GetAllComplete(source);
-      });
+  if (complete_callback.is_valid()) {
+    mojom::LevelDBWrapperGetAllCallbackAssociatedPtr complete_ptr;
+    complete_ptr.Bind(std::move(complete_callback));
+    complete_ptr->Complete(true);
+  }
 }
 
 void LevelDBWrapperImpl::OnConnectionError() {
   if (!bindings_.empty())
     return;
+  // If any tasks are waiting for load to complete, delay calling the
+  // no_bindings_callback_ until all those tasks have completed.
+  if (!on_load_complete_tasks_.empty())
+    return;
   no_bindings_callback_.Run();
 }
 
@@ -280,6 +272,11 @@
   on_load_complete_tasks_.swap(tasks);
   for (auto& task : tasks)
     task.Run();
+
+  // We might need to call the no_bindings_callback_ here if bindings became
+  // empty while waiting for load to complete.
+  if (bindings_.empty())
+    no_bindings_callback_.Run();
 }
 
 void LevelDBWrapperImpl::CreateCommitBatchIfNeeded() {
@@ -324,11 +321,11 @@
 
 void LevelDBWrapperImpl::CommitChanges() {
   DCHECK(database_);
+  DCHECK(map_);
   if (!commit_batch_)
     return;
 
   commit_rate_limiter_.add_samples(1);
-  data_rate_limiter_.add_samples(commit_batch_->GetDataSize());
 
   // Commit all our changes in a single batch.
   std::vector<leveldb::mojom::BatchedOperationPtr> operations;
@@ -339,22 +336,28 @@
     item->key = prefix_;
     operations.push_back(std::move(item));
   }
-  for (auto& it : commit_batch_->changed_values) {
+  size_t data_size = 0;
+  for (const auto& key: commit_batch_->changed_keys) {
+    data_size += key.size();
     leveldb::mojom::BatchedOperationPtr item =
         leveldb::mojom::BatchedOperation::New();
-    item->key.reserve(prefix_.size() + it.first.size());
+    item->key.reserve(prefix_.size() + key.size());
     item->key.insert(item->key.end(), prefix_.begin(), prefix_.end());
-    item->key.insert(item->key.end(), it.first.begin(), it.first.end());
-    if (!it.second) {
+    item->key.insert(item->key.end(), key.begin(), key.end());
+    auto it = map_->find(key);
+    if (it == map_->end()) {
       item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
     } else {
       item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
-      item->value = std::move(*(it.second));
+      item->value = it->second;
+      data_size += it->second.size();
     }
     operations.push_back(std::move(item));
   }
   commit_batch_.reset();
 
+  data_rate_limiter_.add_samples(data_size);
+
   ++commit_batches_in_flight_;
 
   // TODO(michaeln): Currently there is no guarantee LevelDBDatabaseImp::Write
diff --git a/content/browser/leveldb_wrapper_impl.h b/content/browser/leveldb_wrapper_impl.h
index 663d5d85..df6104ae 100644
--- a/content/browser/leveldb_wrapper_impl.h
+++ b/content/browser/leveldb_wrapper_impl.h
@@ -33,7 +33,7 @@
 class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper {
  public:
   // |no_bindings_callback| will be called when this object has no more
-  // bindings.
+  // bindings and all pending modifications have been processed.
   LevelDBWrapperImpl(leveldb::mojom::LevelDBDatabase* database,
                      const std::string& prefix,
                      size_t max_size,
@@ -55,8 +55,6 @@
   friend class LevelDBWrapperImplTest;
 
   using ValueMap = std::map<std::vector<uint8_t>, std::vector<uint8_t>>;
-  using ChangedValueMap =
-      std::map<std::vector<uint8_t>, base::Optional<std::vector<uint8_t>>>;
 
   // Used to rate limit commits.
   class RateLimiter {
@@ -83,11 +81,10 @@
 
   struct CommitBatch {
     bool clear_all_first;
-    ChangedValueMap changed_values;
+    std::set<std::vector<uint8_t>> changed_keys;
 
     CommitBatch();
     ~CommitBatch();
-    size_t GetDataSize() const;
   };
 
   // LevelDBWrapperImpl:
@@ -103,8 +100,9 @@
                  const DeleteAllCallback& callback) override;
   void Get(const std::vector<uint8_t>& key,
            const GetCallback& callback) override;
-  void GetAll(const std::string& source,
-              const GetAllCallback& callback) override;
+  void GetAll(
+      mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback,
+      const GetAllCallback& callback) override;
 
   void OnConnectionError();
   void LoadMap(const base::Closure& completion_callback);
diff --git a/content/browser/leveldb_wrapper_impl_unittest.cc b/content/browser/leveldb_wrapper_impl_unittest.cc
index 3fd3dd8..a40c58e7 100644
--- a/content/browser/leveldb_wrapper_impl_unittest.cc
+++ b/content/browser/leveldb_wrapper_impl_unittest.cc
@@ -7,7 +7,9 @@
 #include "base/run_loop.h"
 #include "components/leveldb/public/cpp/util.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/mock_leveldb_database.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using leveldb::StdStringToUint8Vector;
@@ -21,146 +23,32 @@
 const char* kTestSource = "source";
 const size_t kTestSizeLimit = 512;
 
-leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key,
-                                           std::vector<uint8_t> value) {
-  leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New();
-  result->key = std::move(key);
-  result->value = std::move(value);
-  return result;
-}
-
-base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) {
-  return base::StringPiece(reinterpret_cast<const char*>(data.data()),
-                           data.size());
-}
-
-bool StartsWith(const std::vector<uint8_t>& key,
-                const std::vector<uint8_t>& prefix) {
-  return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix),
-                          base::CompareCase::SENSITIVE);
-}
-
-std::vector<uint8_t> successor(std::vector<uint8_t> data) {
-  for (unsigned i = data.size(); i > 0; --i) {
-    if (data[i - 1] < 255) {
-      data[i - 1]++;
-      return data;
-    }
-  }
-  NOTREACHED();
-  return data;
-}
-
-class MockLevelDBDatabase : public leveldb::mojom::LevelDBDatabase {
+class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback {
  public:
-  explicit MockLevelDBDatabase(
-      std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
-      : mock_data_(*mock_data) {}
-
-  void Put(const std::vector<uint8_t>& key,
-           const std::vector<uint8_t>& value,
-           const PutCallback& callback) override {
-    FAIL();
-  }
-
-  void Delete(const std::vector<uint8_t>& key,
-              const DeleteCallback& callback) override {
-    FAIL();
-  }
-
-  void DeletePrefixed(const std::vector<uint8_t>& key_prefix,
-                      const DeletePrefixedCallback& callback) override {
-    FAIL();
-  }
-
-  void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations,
-             const WriteCallback& callback) override {
-    for (const auto& op : operations) {
-      switch (op->type) {
-        case leveldb::mojom::BatchOperationType::PUT_KEY:
-          mock_data_[op->key] = *op->value;
-          break;
-        case leveldb::mojom::BatchOperationType::DELETE_KEY:
-          mock_data_.erase(op->key);
-          break;
-        case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY:
-          mock_data_.erase(mock_data_.lower_bound(op->key),
-                           mock_data_.lower_bound(successor(op->key)));
-          break;
-      }
-    }
-    callback.Run(leveldb::mojom::DatabaseError::OK);
-  }
-
-  void Get(const std::vector<uint8_t>& key,
-           const GetCallback& callback) override {
-    FAIL();
-  }
-
-  void GetPrefixed(const std::vector<uint8_t>& key_prefix,
-                   const GetPrefixedCallback& callback) override {
-    std::vector<leveldb::mojom::KeyValuePtr> data;
-    for (const auto& row : mock_data_) {
-      if (StartsWith(row.first, key_prefix)) {
-        data.push_back(CreateKeyValue(row.first, row.second));
-      }
-    }
-    callback.Run(leveldb::mojom::DatabaseError::OK, std::move(data));
-  }
-
-  void GetSnapshot(const GetSnapshotCallback& callback) override { FAIL(); }
-
-  void ReleaseSnapshot(const base::UnguessableToken& snapshot) override {
-    FAIL();
-  }
-
-  void GetFromSnapshot(const base::UnguessableToken& snapshot,
-                       const std::vector<uint8_t>& key,
-                       const GetCallback& callback) override {
-    FAIL();
-  }
-
-  void NewIterator(const NewIteratorCallback& callback) override { FAIL(); }
-
-  void NewIteratorFromSnapshot(
-      const base::UnguessableToken& snapshot,
-      const NewIteratorFromSnapshotCallback& callback) override {
-    FAIL();
-  }
-
-  void ReleaseIterator(const base::UnguessableToken& iterator) override {
-    FAIL();
-  }
-
-  void IteratorSeekToFirst(
-      const base::UnguessableToken& iterator,
-      const IteratorSeekToFirstCallback& callback) override {
-    FAIL();
-  }
-
-  void IteratorSeekToLast(const base::UnguessableToken& iterator,
-                          const IteratorSeekToLastCallback& callback) override {
-    FAIL();
-  }
-
-  void IteratorSeek(const base::UnguessableToken& iterator,
-                    const std::vector<uint8_t>& target,
-                    const IteratorSeekToLastCallback& callback) override {
-    FAIL();
-  }
-
-  void IteratorNext(const base::UnguessableToken& iterator,
-                    const IteratorNextCallback& callback) override {
-    FAIL();
-  }
-
-  void IteratorPrev(const base::UnguessableToken& iterator,
-                    const IteratorPrevCallback& callback) override {
-    FAIL();
+  static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind(
+      mojo::AssociatedGroup* associated_group,
+      bool* result,
+      const base::Closure& callback) {
+    mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo ptr_info;
+    mojom::LevelDBWrapperGetAllCallbackAssociatedRequest request;
+    associated_group->CreateAssociatedInterface(
+        mojo::AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
+    mojo::MakeStrongAssociatedBinding(
+        base::WrapUnique(new GetAllCallback(result, callback)),
+        std::move(request));
+    return ptr_info;
   }
 
  private:
-  std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data_;
+  GetAllCallback(bool* result, const base::Closure& callback)
+      : m_result(result), m_callback(callback) {}
+  void Complete(bool success) override {
+    *m_result = success;
+    m_callback.Run();
+  }
+
+  bool* m_result;
+  base::Closure m_callback;
 };
 
 void NoOp() {}
@@ -211,7 +99,7 @@
 
     level_db_wrapper_.Bind(mojo::MakeRequest(&level_db_wrapper_ptr_));
     mojom::LevelDBObserverAssociatedPtrInfo ptr_info;
-    observer_binding_.Bind(&ptr_info, level_db_wrapper_ptr_.associated_group());
+    observer_binding_.Bind(&ptr_info, associated_group());
     level_db_wrapper_ptr_->AddObserver(std::move(ptr_info));
   }
 
@@ -235,6 +123,9 @@
   }
 
   mojom::LevelDBWrapper* wrapper() { return level_db_wrapper_ptr_.get(); }
+  mojo::AssociatedGroup* associated_group() {
+    return level_db_wrapper_ptr_.associated_group();
+  }
 
   bool GetSync(const std::vector<uint8_t>& key, std::vector<uint8_t>* result) {
     base::RunLoop run_loop;
@@ -309,7 +200,6 @@
   void AllDeleted(const std::string& source) override {
     observations_.push_back({Observation::kDeleteAll, "", "", "", source});
   }
-  void GetAllComplete(const std::string& source) override {}
 
   TestBrowserThreadBundle thread_bundle_;
   std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_;
@@ -353,9 +243,17 @@
 TEST_F(LevelDBWrapperImplTest, GetAll) {
   leveldb::mojom::DatabaseError status;
   std::vector<mojom::KeyValuePtr> data;
-  EXPECT_TRUE(wrapper()->GetAll(kTestSource, &status, &data));
+  base::RunLoop run_loop;
+  bool result = false;
+  EXPECT_TRUE(wrapper()->GetAll(
+      GetAllCallback::CreateAndBind(associated_group(), &result,
+                                    run_loop.QuitClosure()),
+      &status, &data));
   EXPECT_EQ(leveldb::mojom::DatabaseError::OK, status);
   EXPECT_EQ(2u, data.size());
+  EXPECT_FALSE(result);
+  run_loop.Run();
+  EXPECT_TRUE(result);
 }
 
 TEST_F(LevelDBWrapperImplTest, CommitPutToDB) {
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
index a4ef557..5494a0c 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -101,6 +101,22 @@
   DCHECK(!end.last_event_time.is_null());    \
   DCHECK_GE(end.last_event_time, start.first_event_time);
 
+// Touch/wheel to scroll latency using Rappor.
+#define RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY(delegate, name, start, end) \
+  CONFIRM_VALID_TIMING(start, end)                                       \
+  rappor::RapporService* rappor_service =                                \
+      GetContentClient()->browser()->GetRapporService();                 \
+  if (rappor_service && delegate) {                                      \
+    std::unique_ptr<rappor::Sample> sample =                             \
+        rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE);           \
+    delegate->AddDomainInfoToRapporSample(sample.get());                 \
+    sample->SetUInt64Field(                                              \
+        "Latency",                                                       \
+        (end.last_event_time - start.first_event_time).InMicroseconds(), \
+        rappor::NO_NOISE);                                               \
+    rappor_service->RecordSample(name, std::move(sample));               \
+  }
+
 // Touch/wheel to scroll latency that is mostly under 1 second.
 #define UMA_HISTOGRAM_TOUCH_WHEEL_TO_SCROLL_LATENCY(name, start, end)         \
   CONFIRM_VALID_TIMING(start, end)                                            \
@@ -248,6 +264,7 @@
     const ui::LatencyInfo& latency,
     const std::string event_type_name) {
   DCHECK(!latency.coalesced());
+  DCHECK(event_type_name == "Touch" || event_type_name == "Wheel");
   if (latency.coalesced())
     return;
 
@@ -266,6 +283,12 @@
             ".TimeToScrollUpdateSwapBegin2",
         original_component, gpu_swap_begin_component);
 
+    RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY(
+        render_widget_host_delegate,
+        "Event.Latency.ScrollBegin." + event_type_name +
+            ".TimeToScrollUpdateSwapBegin2",
+        original_component, gpu_swap_begin_component);
+
     // TODO(lanwei): Will remove them when M56 is stable, see
     // https://crbug.com/669618.
     UMA_HISTOGRAM_TOUCH_WHEEL_TO_SCROLL_LATENCY(
@@ -284,21 +307,10 @@
               ".TimeToScrollUpdateSwapBegin2",
           original_component, gpu_swap_begin_component);
 
-      rappor::RapporService* rappor_service =
-          GetContentClient()->browser()->GetRapporService();
-      if (rappor_service && render_widget_host_delegate) {
-        std::unique_ptr<rappor::Sample> sample =
-            rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE);
-        render_widget_host_delegate->AddDomainInfoToRapporSample(sample.get());
-        sample->SetUInt64Field("Latency",
-                               (gpu_swap_begin_component.last_event_time -
-                                original_component.first_event_time)
-                                   .InMicroseconds(),
-                               rappor::NO_NOISE);
-        rappor_service->RecordSample(
-            "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2",
-            std::move(sample));
-      }
+      RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY(
+          render_widget_host_delegate,
+          "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2",
+          original_component, gpu_swap_begin_component);
     }
   } else {
     // No original component found.
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
index a511ed7..908b65d 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -91,6 +91,49 @@
     ResetHistograms();
   }
 
+  ::testing::AssertionResult RapporSampleAssert(const char* rappor_name,
+                                                int count) {
+    rappor::TestSample::Shadow* sample_obj =
+        test_browser_client_.getTestRapporService()->GetRecordedSampleForMetric(
+            rappor_name);
+    if (count) {
+      if (!sample_obj)
+        return ::testing::AssertionFailure()
+               << rappor_name << " rappor sample should not be null";
+
+      const auto& domain_it = sample_obj->string_fields.find("Domain");
+      if (domain_it == sample_obj->string_fields.end())
+        return ::testing::AssertionFailure()
+               << rappor_name << " rappor sample should contain the string "
+                                 "attribute \"Domain\"";
+      const auto& domain = domain_it->second;
+      if (domain != "bar.com")
+        return ::testing::AssertionFailure()
+               << rappor_name << " rappor expected bar.com domain but had "
+               << domain << " domain";
+
+      const auto& latency_it = sample_obj->uint64_fields.find("Latency");
+      if (latency_it == sample_obj->uint64_fields.end())
+        return ::testing::AssertionFailure()
+               << rappor_name << " rappor sample should contain the uint64 "
+                                 "attribute \"Latency\"";
+      const auto& latency_noise = latency_it->second.second;
+      if (latency_noise != rappor::NO_NOISE)
+        return ::testing::AssertionFailure()
+               << rappor_name
+               << " rappor expected rappor::NO_NOISE latency but had "
+               << latency_noise << " latency";
+
+      return ::testing::AssertionSuccess();
+    } else {
+      if (!sample_obj)
+        return ::testing::AssertionSuccess();
+      else
+        return ::testing::AssertionFailure() << rappor_name
+                                             << " rappor sample should be null";
+    }
+  }
+
   ::testing::AssertionResult HistogramSizeEq(const char* histogram_name,
                                              int size) {
     uint64_t histogram_size =
@@ -135,6 +178,8 @@
 };
 
 TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToFirstScrollHistograms) {
+  const GURL url("http://www.foo.bar.com/subpage/1");
+  contents()->NavigateAndCommit(url);
   for (bool rendering_on_main : {false, true}) {
     for (bool is_running_navigation_hint_task : {false, true}) {
       ResetHistograms();
@@ -160,6 +205,24 @@
                                    INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
         tracker()->OnFrameSwapped(wheel_latency,
                                   is_running_navigation_hint_task);
+
+        // Rappor metrics.
+        EXPECT_TRUE(
+            RapporSampleAssert("Event.Latency.ScrollUpdate.Touch."
+                               "TimeToScrollUpdateSwapBegin2",
+                               0));
+        EXPECT_TRUE(
+            RapporSampleAssert("Event.Latency.ScrollBegin.Touch."
+                               "TimeToScrollUpdateSwapBegin2",
+                               0));
+        EXPECT_TRUE(
+            RapporSampleAssert("Event.Latency.ScrollBegin.Wheel."
+                               "TimeToScrollUpdateSwapBegin2",
+                               2));
+        EXPECT_EQ(
+            2, test_browser_client_.getTestRapporService()->GetReportsCount());
+
+        // UMA histograms.
         EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelUI", 1));
         EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelAcked", 1));
 
@@ -289,6 +352,8 @@
 }
 
 TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToFirstScrollHistograms) {
+  const GURL url("http://www.foo.bar.com/subpage/1");
+  contents()->NavigateAndCommit(url);
   for (bool rendering_on_main : {false, true}) {
     for (bool is_running_navigation_hint_task : {false, true}) {
       ResetHistograms();
@@ -338,6 +403,23 @@
                                   is_running_navigation_hint_task);
       }
 
+      // Rappor metrics.
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollUpdate.Touch."
+                             "TimeToScrollUpdateSwapBegin2",
+                             0));
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollBegin.Touch."
+                             "TimeToScrollUpdateSwapBegin2",
+                             2));
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollBegin.Wheel."
+                             "TimeToScrollUpdateSwapBegin2",
+                             0));
+      EXPECT_EQ(2,
+                test_browser_client_.getTestRapporService()->GetReportsCount());
+
+      // UMA histograms.
       EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchUI", 1));
       EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchAcked", 1));
       EXPECT_TRUE(HistogramSizeEq(
@@ -472,24 +554,23 @@
                                   is_running_navigation_hint_task);
       }
 
-      // Rappor metrics
-      rappor::TestSample::Shadow* sample_obj =
-          test_browser_client_.getTestRapporService()
-              ->GetRecordedSampleForMetric(
-                  "Event.Latency.ScrollUpdate.Touch."
-                  "TimeToScrollUpdateSwapBegin2");
-      EXPECT_NE(nullptr, sample_obj);
-      const auto& domain_it = sample_obj->string_fields.find("Domain");
-      EXPECT_NE(domain_it, sample_obj->string_fields.end());
-      EXPECT_EQ("bar.com", domain_it->second);
-      const auto& latency_it = sample_obj->uint64_fields.find("Latency");
-      EXPECT_NE(latency_it, sample_obj->uint64_fields.end());
-      EXPECT_EQ(rappor::NO_NOISE, latency_it->second.second);
-
+      // Rappor metrics.
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollUpdate.Touch."
+                             "TimeToScrollUpdateSwapBegin2",
+                             2));
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollBegin.Touch."
+                             "TimeToScrollUpdateSwapBegin2",
+                             0));
+      EXPECT_TRUE(
+          RapporSampleAssert("Event.Latency.ScrollBegin.Wheel."
+                             "TimeToScrollUpdateSwapBegin2",
+                             0));
       EXPECT_EQ(2,
                 test_browser_client_.getTestRapporService()->GetReportsCount());
 
-      // UMA histograms
+      // UMA histograms.
       EXPECT_TRUE(HistogramSizeEq(
           "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2", 0));
       EXPECT_TRUE(HistogramSizeEq(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 415e9df..3ac8a1a7 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -166,7 +166,6 @@
 #include "device/battery/battery_monitor_impl.h"
 #include "device/gamepad/gamepad_monitor.h"
 #include "device/power_monitor/power_monitor_message_broadcaster.h"
-#include "device/time_zone_monitor/time_zone_monitor.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gpu_switches.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
@@ -1247,16 +1246,6 @@
         registry.get(), base::Bind(&CreateMemoryCoordinatorHandle, GetID()));
   }
 
-  // BrowserMainLoop, which owns TimeZoneMonitor, is alive for the lifetime of
-  // Mojo communication (see BrowserMainLoop::ShutdownThreadsAndCleanUp(),
-  // which shuts down Mojo). Hence, passing that TimeZoneMonitor instance as
-  // a raw pointer here is safe.
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&device::TimeZoneMonitor::Bind,
-                 base::Unretained(
-                     BrowserMainLoop::GetInstance()->time_zone_monitor())));
-
   AddUIThreadInterface(
       registry.get(),
       base::Bind(&device::PowerMonitorMessageBroadcaster::Create));
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index cc1d268..17cc03d 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1518,8 +1518,9 @@
 }
 
 void RenderWidgetHostViewAndroid::SendBeginFrame(cc::BeginFrameArgs args) {
-  TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame",
-               "frame_time_us", args.frame_time.ToInternalValue());
+  TRACE_EVENT2("cc", "RenderWidgetHostViewAndroid::SendBeginFrame",
+               "frame_number", args.sequence_number, "frame_time_us",
+               args.frame_time.ToInternalValue());
 
   // Synchronous compositor does not use deadline-based scheduling.
   // TODO(brianderson): Replace this hardcoded deadline after Android
diff --git a/content/browser/resource_context_impl.cc b/content/browser/resource_context_impl.cc
index 62b4396..93e2890d 100644
--- a/content/browser/resource_context_impl.cc
+++ b/content/browser/resource_context_impl.cc
@@ -87,7 +87,7 @@
       new UserDataAdapter<StreamContext>(
           StreamContext::GetFor(browser_context)));
 
-  resource_context->DetachUserDataThread();
+  resource_context->DetachFromSequence();
 }
 
 }  // namespace content
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index c9cf39e..df2e757 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -105,9 +105,11 @@
     return;
   }
 
-  SSLManager* manager =
-      static_cast<NavigationControllerImpl*>(&web_contents->GetController())
-          ->ssl_manager();
+  NavigationControllerImpl* controller =
+      static_cast<NavigationControllerImpl*>(&web_contents->GetController());
+  controller->SetPendingNavigationSSLError(true);
+
+  SSLManager* manager = controller->ssl_manager();
   manager->OnCertError(std::move(handler));
 }
 
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 29f7f094a..945f5da 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -177,6 +177,8 @@
     "sync_load_response.h",
     "thread_safe_sender.cc",
     "thread_safe_sender.h",
+    "url_loader_client_impl.cc",
+    "url_loader_client_impl.h",
     "url_response_body_consumer.cc",
     "url_response_body_consumer.h",
     "v8_value_converter_impl.cc",
@@ -212,6 +214,7 @@
 
   public_deps = [
     "//gpu/ipc/client:client",
+    "//services/service_manager/public/interfaces:interfaces_blink",
   ]
 
   deps = [
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index a87fcd54..4f3c2aa 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -48,7 +48,10 @@
 #include "content/child/web_url_request_util.h"
 #include "content/child/worker_thread_registry.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/service_manager_connection.h"
 #include "net/base/net_errors.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/interfaces/connector.mojom.h"
 #include "third_party/WebKit/public/platform/WebData.h"
 #include "third_party/WebKit/public/platform/WebFloatPoint.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
@@ -71,11 +74,6 @@
 
 namespace content {
 
-namespace {
-
-
-}  // namespace
-
 static int ToMessageID(WebLocalizedString::Name name) {
   switch (name) {
     case WebLocalizedString::AXAMPMFieldText:
@@ -786,6 +784,19 @@
   return false;
 }
 
+void BlinkPlatformImpl::bindServiceConnector(
+    mojo::ScopedMessagePipeHandle remote_handle) {
+  if (!ChildThreadImpl::current())
+    return;
+
+  service_manager::mojom::ConnectorRequest chromium_request;
+  chromium_request.Bind(std::move(remote_handle));
+  ChildThreadImpl::current()
+      ->GetServiceManagerConnection()
+      ->GetConnector()
+      ->BindRequest(std::move(chromium_request));
+}
+
 size_t BlinkPlatformImpl::actualMemoryUsageMB() {
   return GetMemoryUsageKB() >> 10;
 }
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index 0deda78..a4553fb 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -76,6 +76,9 @@
   size_t actualMemoryUsageMB() override;
   size_t numberOfProcessors() override;
 
+  void bindServiceConnector(
+      mojo::ScopedMessagePipeHandle remote_handle) override;
+
   size_t maxDecodedImageBytes() override;
   uint32_t getUniqueIdForProcess() override;
   blink::WebString userAgent() override;
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 4cfd5f4..3cbc9ff 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -27,7 +27,7 @@
 #include "content/child/shared_memory_received_data_factory.h"
 #include "content/child/site_isolation_stats_gatherer.h"
 #include "content/child/sync_load_response.h"
-#include "content/child/url_response_body_consumer.h"
+#include "content/child/url_loader_client_impl.h"
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/navigation_params.h"
 #include "content/common/resource_messages.h"
@@ -39,9 +39,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/resource_type.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/associated_group.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
 #include "net/base/net_errors.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_response_headers.h"
@@ -76,82 +74,6 @@
   return next_request_id++;
 }
 
-class URLLoaderClientImpl final : public mojom::URLLoaderClient {
- public:
-  URLLoaderClientImpl(int request_id,
-                      ResourceDispatcher* resource_dispatcher,
-                      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : binding_(this),
-        request_id_(request_id),
-        resource_dispatcher_(resource_dispatcher),
-        task_runner_(std::move(task_runner)) {}
-  ~URLLoaderClientImpl() override {
-    if (body_consumer_)
-      body_consumer_->Cancel();
-  }
-
-  void OnReceiveResponse(
-      const ResourceResponseHead& response_head,
-      mojom::DownloadedTempFilePtr downloaded_file) override {
-    has_received_response_ = true;
-    if (body_consumer_)
-      body_consumer_->Start();
-    downloaded_file_ = std::move(downloaded_file);
-    resource_dispatcher_->OnMessageReceived(
-        ResourceMsg_ReceivedResponse(request_id_, response_head));
-  }
-
-  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
-                         const ResourceResponseHead& response_head) override {
-    DCHECK(!has_received_response_);
-    DCHECK(!body_consumer_);
-    resource_dispatcher_->OnMessageReceived(ResourceMsg_ReceivedRedirect(
-        request_id_, redirect_info, response_head));
-  }
-
-  void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override {
-    resource_dispatcher_->OnMessageReceived(
-        ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len));
-  }
-
-  void OnTransferSizeUpdated(int32_t transfer_size_diff) override {
-    resource_dispatcher_->OnTransferSizeUpdated(request_id_,
-                                                transfer_size_diff);
-  }
-
-  void OnStartLoadingResponseBody(
-      mojo::ScopedDataPipeConsumerHandle body) override {
-    DCHECK(!body_consumer_);
-    body_consumer_ = new URLResponseBodyConsumer(
-        request_id_, resource_dispatcher_, std::move(body), task_runner_);
-    if (has_received_response_)
-      body_consumer_->Start();
-  }
-
-  void OnComplete(const ResourceRequestCompletionStatus& status) override {
-    if (!body_consumer_) {
-      resource_dispatcher_->OnMessageReceived(
-          ResourceMsg_RequestComplete(request_id_, status));
-      return;
-    }
-    body_consumer_->OnComplete(status);
-  }
-
-  void Bind(mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info,
-            mojo::AssociatedGroup* associated_group) {
-    binding_.Bind(client_ptr_info, associated_group);
-  }
-
- private:
-  mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
-  scoped_refptr<URLResponseBodyConsumer> body_consumer_;
-  mojom::DownloadedTempFilePtr downloaded_file_;
-  const int request_id_;
-  bool has_received_response_ = false;
-  ResourceDispatcher* const resource_dispatcher_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-};
-
 void CheckSchemeForReferrerPolicy(const ResourceRequest& request) {
   if ((request.referrer_policy == blink::WebReferrerPolicyDefault ||
        request.referrer_policy ==
@@ -548,7 +470,7 @@
   }
   // Cancel the request if it didn't complete, and clean it up so the bridge
   // will receive no more messages.
-  if (info.completion_time.is_null())
+  if (info.completion_time.is_null() && !info.url_loader)
     message_sender_->Send(new ResourceHostMsg_CancelRequest(request_id));
   RemovePendingRequest(request_id);
 }
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h
index 48a7f57..39557b2 100644
--- a/content/child/resource_dispatcher.h
+++ b/content/child/resource_dispatcher.h
@@ -50,6 +50,7 @@
 class SharedMemoryReceivedDataFactory;
 struct SiteIsolationResponseMetaData;
 struct SyncLoadResponse;
+class URLLoaderClientImpl;
 
 namespace mojom {
 class URLLoaderFactory;
@@ -192,7 +193,7 @@
 
     // For mojo loading.
     mojom::URLLoaderAssociatedPtr url_loader;
-    std::unique_ptr<mojom::URLLoaderClient> url_loader_client;
+    std::unique_ptr<URLLoaderClientImpl> url_loader_client;
   };
   using PendingRequestMap = std::map<int, std::unique_ptr<PendingRequestInfo>>;
 
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc
index 3e4bb15..758376de 100644
--- a/content/child/resource_dispatcher_unittest.cc
+++ b/content/child/resource_dispatcher_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/child/request_extra_data.h"
+#include "content/child/test_request_peer.h"
 #include "content/common/appcache_interfaces.h"
 #include "content/common/resource_messages.h"
 #include "content/common/resource_request.h"
@@ -51,107 +52,6 @@
 static const char kTestRedirectHeaders[] =
   "HTTP/1.1 302 Found\nLocation:http://www.google.com/\n\n";
 
-// Listens for request response data and stores it so that it can be compared
-// to the reference data.
-class TestRequestPeer : public RequestPeer {
- public:
-  struct Context;
-  TestRequestPeer(ResourceDispatcher* dispatcher, Context* context)
-      : dispatcher_(dispatcher), context_(context) {}
-
-  void OnUploadProgress(uint64_t position, uint64_t size) override {}
-
-  bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          const ResourceResponseInfo& info) override {
-    EXPECT_FALSE(context_->cancelled);
-    ++context_->seen_redirects;
-    if (context_->defer_on_redirect)
-      dispatcher_->SetDefersLoading(context_->request_id, true);
-    return context_->follow_redirects;
-  }
-
-  void OnReceivedResponse(const ResourceResponseInfo& info) override {
-    EXPECT_FALSE(context_->cancelled);
-    EXPECT_FALSE(context_->received_response);
-    context_->received_response = true;
-    if (context_->cancel_on_receive_response) {
-      dispatcher_->Cancel(context_->request_id);
-      context_->cancelled = true;
-    }
-  }
-
-  void OnDownloadedData(int len, int encoded_data_length) override {
-    EXPECT_FALSE(context_->cancelled);
-    context_->total_downloaded_data_length += len;
-    context_->total_encoded_data_length += encoded_data_length;
-  }
-
-  void OnReceivedData(std::unique_ptr<ReceivedData> data) override {
-    if (context_->cancelled)
-      return;
-    EXPECT_TRUE(context_->received_response);
-    EXPECT_FALSE(context_->complete);
-    context_->data.append(data->payload(), data->length());
-
-    if (context_->cancel_on_receive_data) {
-      dispatcher_->Cancel(context_->request_id);
-      context_->cancelled = true;
-    }
-  }
-
-  void OnTransferSizeUpdated(int transfer_size_diff) override {
-    if (context_->cancelled)
-      return;
-    context_->total_encoded_data_length += transfer_size_diff;
-  }
-
-  void OnCompletedRequest(int error_code,
-                          bool was_ignored_by_handler,
-                          bool stale_copy_in_cache,
-                          const base::TimeTicks& completion_time,
-                          int64_t total_transfer_size,
-                          int64_t encoded_body_size) override {
-    if (context_->cancelled)
-      return;
-    EXPECT_TRUE(context_->received_response);
-    EXPECT_FALSE(context_->complete);
-    context_->complete = true;
-  }
-
-  struct Context {
-    // True if should follow redirects, false if should cancel them.
-    bool follow_redirects = true;
-    // True if the request should be deferred on redirects.
-    bool defer_on_redirect = false;
-
-    // Number of total redirects seen.
-    int seen_redirects = 0;
-
-    bool cancel_on_receive_response = false;
-    bool cancel_on_receive_data = false;
-    bool received_response = false;
-
-    // Data received. If downloading to file, remains empty.
-    std::string data;
-
-    // Total encoded data length, regardless of whether downloading to a file or
-    // not.
-    int total_encoded_data_length = 0;
-    // Total length when downloading to a file.
-    int total_downloaded_data_length = 0;
-
-    bool complete = false;
-    bool cancelled = false;
-    int request_id = -1;
-  };
-
- private:
-  ResourceDispatcher* dispatcher_;
-  Context* context_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestRequestPeer);
-};
-
 // Sets up the message sender override for the unit test.
 class ResourceDispatcherTest : public testing::Test, public IPC::Sender {
  public:
diff --git a/content/child/test_request_peer.cc b/content/child/test_request_peer.cc
new file mode 100644
index 0000000..25200d53
--- /dev/null
+++ b/content/child/test_request_peer.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/child/test_request_peer.h"
+
+#include "content/child/resource_dispatcher.h"
+#include "net/url_request/redirect_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TestRequestPeer::TestRequestPeer(ResourceDispatcher* dispatcher,
+                                 Context* context)
+    : dispatcher_(dispatcher), context_(context) {}
+
+TestRequestPeer::~TestRequestPeer() = default;
+
+void TestRequestPeer::OnUploadProgress(uint64_t position, uint64_t size) {
+  EXPECT_FALSE(context_->complete);
+}
+
+bool TestRequestPeer::OnReceivedRedirect(const net::RedirectInfo& redirect_info,
+                                         const ResourceResponseInfo& info) {
+  EXPECT_FALSE(context_->cancelled);
+  EXPECT_FALSE(context_->complete);
+  ++context_->seen_redirects;
+  if (context_->defer_on_redirect)
+    dispatcher_->SetDefersLoading(context_->request_id, true);
+  return context_->follow_redirects;
+}
+
+void TestRequestPeer::OnReceivedResponse(const ResourceResponseInfo& info) {
+  EXPECT_FALSE(context_->cancelled);
+  EXPECT_FALSE(context_->received_response);
+  EXPECT_FALSE(context_->complete);
+  context_->received_response = true;
+  if (context_->cancel_on_receive_response) {
+    dispatcher_->Cancel(context_->request_id);
+    context_->cancelled = true;
+  }
+}
+
+void TestRequestPeer::OnDownloadedData(int len, int encoded_data_length) {
+  EXPECT_TRUE(context_->received_response);
+  EXPECT_FALSE(context_->cancelled);
+  EXPECT_FALSE(context_->complete);
+  context_->total_downloaded_data_length += len;
+  context_->total_encoded_data_length += encoded_data_length;
+}
+
+void TestRequestPeer::OnReceivedData(std::unique_ptr<ReceivedData> data) {
+  if (context_->cancelled)
+    return;
+  EXPECT_TRUE(context_->received_response);
+  EXPECT_FALSE(context_->complete);
+  context_->data.append(data->payload(), data->length());
+
+  if (context_->cancel_on_receive_data) {
+    dispatcher_->Cancel(context_->request_id);
+    context_->cancelled = true;
+  }
+}
+
+void TestRequestPeer::OnTransferSizeUpdated(int transfer_size_diff) {
+  EXPECT_TRUE(context_->received_response);
+  EXPECT_FALSE(context_->complete);
+  if (context_->cancelled)
+    return;
+  context_->total_encoded_data_length += transfer_size_diff;
+}
+
+void TestRequestPeer::OnCompletedRequest(int error_code,
+                                         bool was_ignored_by_handler,
+                                         bool stale_copy_in_cache,
+                                         const base::TimeTicks& completion_time,
+                                         int64_t total_transfer_size,
+                                         int64_t encoded_body_size) {
+  if (context_->cancelled)
+    return;
+  EXPECT_TRUE(context_->received_response);
+  EXPECT_FALSE(context_->complete);
+  context_->complete = true;
+}
+
+TestRequestPeer::Context::Context() = default;
+TestRequestPeer::Context::~Context() = default;
+
+}  // namespace content
diff --git a/content/child/test_request_peer.h b/content/child/test_request_peer.h
new file mode 100644
index 0000000..81162ac
--- /dev/null
+++ b/content/child/test_request_peer.h
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_CHILD_TEST_REQUEST_PEER_H_
+#define CONTENT_CHILD_TEST_REQUEST_PEER_H_
+
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include "base/time/time.h"
+#include "content/public/child/request_peer.h"
+
+namespace net {
+struct RedirectInfo;
+}  // namespace net
+
+namespace content {
+
+class ReceivedData;
+class ResourceDispatcher;
+struct ResourceResponseInfo;
+
+// Listens for request response data and stores it so that it can be compared
+// to the reference data.
+class TestRequestPeer : public RequestPeer {
+ public:
+  struct Context;
+  TestRequestPeer(ResourceDispatcher* dispatcher, Context* context);
+  ~TestRequestPeer() override;
+
+  void OnUploadProgress(uint64_t position, uint64_t size) override;
+  bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
+                          const ResourceResponseInfo& info) override;
+  void OnReceivedResponse(const ResourceResponseInfo& info) override;
+  void OnDownloadedData(int len, int encoded_data_length) override;
+  void OnReceivedData(std::unique_ptr<ReceivedData> data) override;
+  void OnTransferSizeUpdated(int transfer_size_diff) override;
+  void OnCompletedRequest(int error_code,
+                          bool was_ignored_by_handler,
+                          bool stale_copy_in_cache,
+                          const base::TimeTicks& completion_time,
+                          int64_t total_transfer_size,
+                          int64_t encoded_body_size) override;
+
+  struct Context final {
+    Context();
+    ~Context();
+
+    // True if should follow redirects, false if should cancel them.
+    bool follow_redirects = true;
+    // True if the request should be deferred on redirects.
+    bool defer_on_redirect = false;
+
+    // Number of total redirects seen.
+    int seen_redirects = 0;
+
+    bool cancel_on_receive_response = false;
+    bool cancel_on_receive_data = false;
+    bool received_response = false;
+
+    // Data received. If downloading to file, remains empty.
+    std::string data;
+
+    // Total encoded data length, regardless of whether downloading to a file or
+    // not.
+    int total_encoded_data_length = 0;
+    // Total length when downloading to a file.
+    int total_downloaded_data_length = 0;
+
+    bool complete = false;
+    bool cancelled = false;
+    int request_id = -1;
+  };
+
+ private:
+  ResourceDispatcher* dispatcher_;
+  Context* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestRequestPeer);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_CHILD_TEST_REQUEST_PEER_H_
diff --git a/content/child/url_loader_client_impl.cc b/content/child/url_loader_client_impl.cc
new file mode 100644
index 0000000..b9305043
--- /dev/null
+++ b/content/child/url_loader_client_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/child/url_loader_client_impl.h"
+
+#include "base/callback.h"
+#include "base/single_thread_task_runner.h"
+#include "content/child/resource_dispatcher.h"
+#include "content/child/url_response_body_consumer.h"
+#include "content/common/resource_messages.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "net/url_request/redirect_info.h"
+
+namespace content {
+
+URLLoaderClientImpl::URLLoaderClientImpl(
+    int request_id,
+    ResourceDispatcher* resource_dispatcher,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : binding_(this),
+      request_id_(request_id),
+      resource_dispatcher_(resource_dispatcher),
+      task_runner_(std::move(task_runner)) {}
+
+URLLoaderClientImpl::~URLLoaderClientImpl() {
+  if (body_consumer_)
+    body_consumer_->Cancel();
+}
+
+void URLLoaderClientImpl::Bind(
+    mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info,
+    mojo::AssociatedGroup* associated_group) {
+  binding_.Bind(client_ptr_info, associated_group);
+}
+
+void URLLoaderClientImpl::OnReceiveResponse(
+    const ResourceResponseHead& response_head,
+    mojom::DownloadedTempFilePtr downloaded_file) {
+  has_received_response_ = true;
+  if (body_consumer_)
+    body_consumer_->Start();
+  downloaded_file_ = std::move(downloaded_file);
+  Dispatch(ResourceMsg_ReceivedResponse(request_id_, response_head));
+}
+
+void URLLoaderClientImpl::OnReceiveRedirect(
+    const net::RedirectInfo& redirect_info,
+    const ResourceResponseHead& response_head) {
+  DCHECK(!has_received_response_);
+  DCHECK(!body_consumer_);
+  Dispatch(
+      ResourceMsg_ReceivedRedirect(request_id_, redirect_info, response_head));
+}
+
+void URLLoaderClientImpl::OnDataDownloaded(int64_t data_len,
+                                           int64_t encoded_data_len) {
+  Dispatch(ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len));
+}
+
+void URLLoaderClientImpl::OnTransferSizeUpdated(int32_t transfer_size_diff) {
+  resource_dispatcher_->OnTransferSizeUpdated(request_id_,
+                                              transfer_size_diff);
+}
+
+void URLLoaderClientImpl::OnStartLoadingResponseBody(
+    mojo::ScopedDataPipeConsumerHandle body) {
+  DCHECK(!body_consumer_);
+  body_consumer_ = new URLResponseBodyConsumer(
+      request_id_, resource_dispatcher_, std::move(body), task_runner_);
+  if (has_received_response_)
+    body_consumer_->Start();
+}
+
+void URLLoaderClientImpl::OnComplete(
+    const ResourceRequestCompletionStatus& status) {
+  if (!body_consumer_) {
+    Dispatch(ResourceMsg_RequestComplete(request_id_, status));
+    return;
+  }
+  body_consumer_->OnComplete(status);
+}
+
+void URLLoaderClientImpl::Dispatch(const IPC::Message& message) {
+  resource_dispatcher_->OnMessageReceived(message);
+}
+
+}  // namespace content
diff --git a/content/child/url_loader_client_impl.h b/content/child/url_loader_client_impl.h
new file mode 100644
index 0000000..3710c07
--- /dev/null
+++ b/content/child/url_loader_client_impl.h
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_
+#define CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_
+
+#include <stdint.h>
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
+#include "content/common/url_loader.mojom.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace mojo {
+class AssociatedGroup;
+}  // namespace mojo
+
+namespace net {
+struct RedirectInfo;
+}  // namespace net
+
+namespace content {
+class ResourceDispatcher;
+class URLResponseBodyConsumer;
+struct ResourceRequestCompletionStatus;
+struct ResourceResponseHead;
+
+class CONTENT_EXPORT URLLoaderClientImpl final : public mojom::URLLoaderClient {
+ public:
+  URLLoaderClientImpl(int request_id,
+                      ResourceDispatcher* resource_dispatcher,
+                      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~URLLoaderClientImpl() override;
+
+  void Bind(mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info,
+            mojo::AssociatedGroup* associated_group);
+
+  // mojom::URLLoaderClient implementation
+  void OnReceiveResponse(const ResourceResponseHead& response_head,
+                         mojom::DownloadedTempFilePtr downloaded_file) override;
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         const ResourceResponseHead& response_head) override;
+  void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override;
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override;
+  void OnComplete(const ResourceRequestCompletionStatus& status) override;
+
+private:
+  void Dispatch(const IPC::Message& message);
+
+  mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
+  scoped_refptr<URLResponseBodyConsumer> body_consumer_;
+  mojom::DownloadedTempFilePtr downloaded_file_;
+  const int request_id_;
+  bool has_received_response_ = false;
+  ResourceDispatcher* const resource_dispatcher_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_
diff --git a/content/child/url_loader_client_impl_unittest.cc b/content/child/url_loader_client_impl_unittest.cc
new file mode 100644
index 0000000..cec32a7
--- /dev/null
+++ b/content/child/url_loader_client_impl_unittest.cc
@@ -0,0 +1,354 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/child/url_loader_client_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/child/resource_dispatcher.h"
+#include "content/child/test_request_peer.h"
+#include "content/common/url_loader_factory.mojom.h"
+#include "ipc/ipc_sender.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "net/url_request/redirect_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class URLLoaderClientImplTest : public ::testing::Test,
+                                IPC::Sender,
+                                mojom::URLLoaderFactory {
+ protected:
+  URLLoaderClientImplTest()
+      : dispatcher_(new ResourceDispatcher(this, message_loop_.task_runner())),
+        mojo_binding_(this) {
+    url_loader_factory_proxy_ = mojo_binding_.CreateInterfacePtrAndBind();
+
+    request_id_ = dispatcher_->StartAsync(
+        base::MakeUnique<ResourceRequest>(), 0, nullptr, url::Origin(),
+        base::MakeUnique<TestRequestPeer>(dispatcher_.get(),
+                                          &request_peer_context_),
+        blink::WebURLRequest::LoadingIPCType::Mojo,
+        url_loader_factory_proxy_.get(),
+        url_loader_factory_proxy_.associated_group());
+    request_peer_context_.request_id = request_id_;
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(url_loader_client_);
+  }
+
+  void TearDown() override {
+    url_loader_client_ = nullptr;
+    url_loader_factory_proxy_ = nullptr;
+  }
+
+  bool Send(IPC::Message* message) override {
+    ADD_FAILURE() << "IPC::Sender::Send should not be called.";
+    return false;
+  }
+
+  void CreateLoaderAndStart(
+      mojom::URLLoaderAssociatedRequest request,
+      int32_t routing_id,
+      int32_t request_id,
+      const ResourceRequest& url_request,
+      mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) override {
+    url_loader_client_.Bind(std::move(client_ptr_info));
+  }
+
+  void SyncLoad(int32_t routing_id,
+                int32_t request_id,
+                const ResourceRequest& request,
+                const SyncLoadCallback& callback) override {
+    NOTREACHED();
+  }
+
+  static MojoCreateDataPipeOptions DataPipeOptions() {
+    MojoCreateDataPipeOptions options;
+    options.struct_size = sizeof(MojoCreateDataPipeOptions);
+    options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+    options.element_num_bytes = 1;
+    options.capacity_num_bytes = 4096;
+    return options;
+  }
+
+  base::MessageLoop message_loop_;
+  std::unique_ptr<ResourceDispatcher> dispatcher_;
+  TestRequestPeer::Context request_peer_context_;
+  int request_id_ = 0;
+  mojom::URLLoaderClientAssociatedPtr url_loader_client_;
+  mojom::URLLoaderFactoryPtr url_loader_factory_proxy_;
+  mojo::Binding<mojom::URLLoaderFactory> mojo_binding_;
+};
+
+TEST_F(URLLoaderClientImplTest, OnReceiveResponse) {
+  ResourceResponseHead response_head;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+}
+
+TEST_F(URLLoaderClientImplTest, ResponseBody) {
+  ResourceResponseHead response_head;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ("hello", request_peer_context_.data);
+}
+
+// OnStartLoadingResponseBody can be called before OnReceiveResponse. Because
+// of the lack of the ordering guarantee between the message channel and the
+// data pipe, bytes can arrive before OnReceiveResponse. URLLoaderClientImpl
+// should restore the order.
+TEST_F(URLLoaderClientImplTest, ResponseBodyShouldComeAfterResponse) {
+  ResourceResponseHead response_head;
+
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ("", request_peer_context_.data);
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_EQ("", request_peer_context_.data);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ("hello", request_peer_context_.data);
+}
+
+TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) {
+  ResourceResponseHead response_head;
+  net::RedirectInfo redirect_info;
+
+  url_loader_client_->OnReceiveRedirect(redirect_info, response_head);
+
+  EXPECT_EQ(0, request_peer_context_.seen_redirects);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, request_peer_context_.seen_redirects);
+}
+
+TEST_F(URLLoaderClientImplTest, OnDataDownloaded) {
+  ResourceResponseHead response_head;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnDataDownloaded(8, 13);
+  url_loader_client_->OnDataDownloaded(2, 1);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_EQ(0, request_peer_context_.total_downloaded_data_length);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ(10, request_peer_context_.total_downloaded_data_length);
+  EXPECT_EQ(14, request_peer_context_.total_encoded_data_length);
+}
+
+TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) {
+  ResourceResponseHead response_head;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnTransferSizeUpdated(4);
+  url_loader_client_->OnTransferSizeUpdated(4);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ(8, request_peer_context_.total_encoded_data_length);
+}
+
+TEST_F(URLLoaderClientImplTest, OnCompleteWithoutResponseBody) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+}
+
+TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+  data_pipe.producer_handle.reset();
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_EQ("", request_peer_context_.data);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ("hello", request_peer_context_.data);
+
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.complete);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_TRUE(request_peer_context_.complete);
+}
+
+// Due to the lack of ordering guarantee, it is possible that the response body
+// bytes arrives after the completion message. URLLoaderClientImpl should
+// restore the order.
+TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  url_loader_client_->OnComplete(completion_status);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_FALSE(request_peer_context_.complete);
+
+  data_pipe.producer_handle.reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_TRUE(request_peer_context_.complete);
+}
+
+TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponseWithoutResponseBody) {
+  request_peer_context_.cancel_on_receive_response = true;
+
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_TRUE(request_peer_context_.cancelled);
+}
+
+TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponseWithResponseBody) {
+  request_peer_context_.cancel_on_receive_response = true;
+
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  base::RunLoop().RunUntilIdle();
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_TRUE(request_peer_context_.cancelled);
+}
+
+TEST_F(URLLoaderClientImplTest, CancelOnReceiveData) {
+  request_peer_context_.cancel_on_receive_data = true;
+
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  base::RunLoop().RunUntilIdle();
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_TRUE(request_peer_context_.cancelled);
+}
+
+}  // namespace content
diff --git a/content/common/leveldb_wrapper.mojom b/content/common/leveldb_wrapper.mojom
index 6295be0b..e099618 100644
--- a/content/common/leveldb_wrapper.mojom
+++ b/content/common/leveldb_wrapper.mojom
@@ -15,11 +15,6 @@
              string source);
   KeyDeleted(array<uint8> key, array<uint8> old_value, string source);
   AllDeleted(string source);
-
-  // Since the GetAll call is synchronous, observers need this asynchronously
-  // delivered notification to avoid applying changes to the returned array
-  // that it already contains.
-  GetAllComplete(string source);
 };
 
 struct KeyValue {
@@ -27,6 +22,16 @@
   array<uint8> value;
 };
 
+// Since the GetAll call is synchronous, LevelDBWrapper users need this
+// asynchronously delivered notification to avoid applying changes to the
+// returned array that it already contains. This is not sent over the
+// normal LevelDBObserver interface as there can be many observers and
+// only the connection that made the GetAll call needs to be notified of
+// its completion.
+interface LevelDBWrapperGetAllCallback {
+  Complete(bool success);
+};
+
 // A wrapper around leveldb that supports giving notifications when values
 // change.
 interface LevelDBWrapper {
@@ -48,6 +53,6 @@
 
   // Only used with small databases. Returns all key/value pairs.
   [Sync]
-  GetAll(string source)
+  GetAll(associated LevelDBWrapperGetAllCallback complete_callback)
     => (leveldb.mojom.DatabaseError status, array<KeyValue> data);
 };
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index a6f43af..57b206c 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -94,6 +94,7 @@
         waitForEventLogs("selectionchange");
         clearEventLogs();
 
+        waitAndVerifyUpdateSelection(0, 0, 0, -1, -1);
         resetAllStates();
     }
 
@@ -776,33 +777,6 @@
         assertTextsAroundCursor("blablargblarg", null, "");
     }
 
-    // crbug.com/606059
-    @MediumTest
-    @Feature({"TextInput"})
-    @RetryOnFailure
-    public void testPasteLongText() throws Exception {
-        final int textLength = 25000;
-        final String text = new String(new char[textLength]).replace("\0", "a");
-        setClip(text);
-        assertClipboardContents(getActivity(), text);
-
-        focusElement("textarea");
-        waitAndVerifyUpdateSelection(0, 0, 0, -1, -1);
-
-        // In order to reproduce the bug, we need some text after the pasting text.
-        commitText("hello", 1);
-        waitAndVerifyUpdateSelection(1, 5, 5, -1, -1);
-
-        setSelection(0, 0);
-        waitAndVerifyUpdateSelection(2, 0, 0, -1, -1);
-
-        // It will crash after the 3rd paste if ImeThread is not enabled.
-        for (int i = 0; i < 10; i++) {
-            paste();
-            waitAndVerifyUpdateSelection(3 + i, textLength * (i + 1), textLength * (i + 1), -1, -1);
-        }
-    }
-
     @SmallTest
     @Feature({"TextInput"})
     @DisabledTest(message = "crbug.com/673588")
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index eacc854..da87521 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -19,6 +19,7 @@
       },
       "requires": {
         "content_browser": [ "renderer" ],
+        "device": [ "device:time_zone_monitor" ],
         "ui": [
           "discardable_memory",
           "gpu_client"
diff --git a/content/public/common/browser_side_navigation_policy.cc b/content/public/common/browser_side_navigation_policy.cc
index a94eff1..698ce3f 100644
--- a/content/public/common/browser_side_navigation_policy.cc
+++ b/content/public/common/browser_side_navigation_policy.cc
@@ -5,13 +5,16 @@
 #include "content/public/common/browser_side_navigation_policy.h"
 
 #include "base/command_line.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 
 namespace content {
 
 bool IsBrowserSideNavigationEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableBrowserSideNavigation);
+  return
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableBrowserSideNavigation) ||
+      base::FeatureList::IsEnabled(features::kBrowserSideNavigation);
 }
 
 }  // namespace content
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 9e01f6f..e3b3a25 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -19,6 +19,10 @@
 const base::Feature kBrotliEncoding{"brotli-encoding",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables browser side navigation (aka PlzNavigate). http://crbug.com/368813
+const base::Feature kBrowserSideNavigation{"browser-side-navigation",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If Canvas2D Image Chromium is allowed, this feature controls whether it is
 // enabled.
 const base::Feature kCanvas2DImageChromium{"Canvas2DImageChromium",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 0ea5008..0aed60d 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -18,6 +18,7 @@
 // alongside the definition of their values in the .cc file.
 CONTENT_EXPORT extern const base::Feature kAsmJsToWebAssembly;
 CONTENT_EXPORT extern const base::Feature kBrotliEncoding;
+CONTENT_EXPORT extern const base::Feature kBrowserSideNavigation;
 CONTENT_EXPORT extern const base::Feature kCanvas2DImageChromium;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc
index e2e4f3b5..b09dcba 100644
--- a/content/renderer/dom_storage/local_storage_cached_area.cc
+++ b/content/renderer/dom_storage/local_storage_cached_area.cc
@@ -15,9 +15,12 @@
 #include "content/common/storage_partition_service.mojom.h"
 #include "content/renderer/dom_storage/local_storage_area.h"
 #include "content/renderer/dom_storage/local_storage_cached_areas.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h"
 
+namespace content {
+
 namespace {
 
 base::string16 Uint8VectorToString16(const std::vector<uint8_t>& input) {
@@ -30,9 +33,29 @@
   return std::vector<uint8_t>(data, data + input.size() * sizeof(base::char16));
 }
 
-}  // namespace
+class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback {
+ public:
+  static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind(
+      mojo::AssociatedGroup* associated_group,
+      const base::Callback<void(bool)>& callback) {
+    mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo ptr_info;
+    mojom::LevelDBWrapperGetAllCallbackAssociatedRequest request;
+    associated_group->CreateAssociatedInterface(
+        mojo::AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
+    mojo::MakeStrongAssociatedBinding(
+        base::WrapUnique(new GetAllCallback(callback)), std::move(request));
+    return ptr_info;
+  }
 
-namespace content {
+ private:
+  explicit GetAllCallback(const base::Callback<void(bool)>& callback)
+      : m_callback(callback) {}
+  void Complete(bool success) override { m_callback.Run(success); }
+
+  base::Callback<void(bool)> m_callback;
+};
+
+}  // namespace
 
 // These methods are used to pack and unpack the page_url/storage_area_id into
 // source strings to/from the browser.
@@ -218,18 +241,6 @@
       base::NullableString16(), origin_.GetURL(), page_url, originating_area);
 }
 
-void LocalStorageCachedArea::GetAllComplete(const std::string& source) {
-  // Since the GetAll method is synchronous, we need this asynchronously
-  // delivered notification to avoid applying changes to the returned array
-  // that we already have.
-  if (source == get_all_request_id_) {
-    DCHECK(ignore_all_mutations_);
-    DCHECK(!get_all_request_id_.empty());
-    ignore_all_mutations_ = false;
-    get_all_request_id_.clear();
-  }
-}
-
 void LocalStorageCachedArea::KeyAddedOrChanged(
     const std::vector<uint8_t>& key,
     const std::vector<uint8_t>& new_value,
@@ -272,10 +283,13 @@
 
   base::TimeTicks before = base::TimeTicks::Now();
   ignore_all_mutations_ = true;
-  get_all_request_id_ = base::Uint64ToString(base::RandUint64());
   leveldb::mojom::DatabaseError status = leveldb::mojom::DatabaseError::OK;
   std::vector<content::mojom::KeyValuePtr> data;
-  leveldb_->GetAll(get_all_request_id_, &status, &data);
+  leveldb_->GetAll(GetAllCallback::CreateAndBind(
+                       leveldb_.associated_group(),
+                       base::Bind(&LocalStorageCachedArea::OnGetAllComplete,
+                                  weak_factory_.GetWeakPtr())),
+                   &status, &data);
 
   DOMStorageValuesMap values;
   for (size_t i = 0; i < data.size(); ++i) {
@@ -336,6 +350,15 @@
   ignore_all_mutations_ = false;
 }
 
+void LocalStorageCachedArea::OnGetAllComplete(bool success) {
+  // Since the GetAll method is synchronous, we need this asynchronously
+  // delivered notification to avoid applying changes to the returned array
+  // that we already have.
+  DCHECK(success);
+  DCHECK(ignore_all_mutations_);
+  ignore_all_mutations_ = false;
+}
+
 void LocalStorageCachedArea::Reset() {
   map_ = NULL;
   ignore_key_mutations_.clear();
diff --git a/content/renderer/dom_storage/local_storage_cached_area.h b/content/renderer/dom_storage/local_storage_cached_area.h
index 11915a9b..e2902af 100644
--- a/content/renderer/dom_storage/local_storage_cached_area.h
+++ b/content/renderer/dom_storage/local_storage_cached_area.h
@@ -76,7 +76,6 @@
                   const std::vector<uint8_t>& old_value,
                   const std::string& source) override;
   void AllDeleted(const std::string& source) override;
-  void GetAllComplete(const std::string& source) override;
 
   // Common helper for KeyAdded() and KeyChanged()
   void KeyAddedOrChanged(const std::vector<uint8_t>& key,
@@ -91,6 +90,7 @@
   void OnSetItemComplete(const base::string16& key, bool success);
   void OnRemoveItemComplete(const base::string16& key, bool success);
   void OnClearComplete(bool success);
+  void OnGetAllComplete(bool success);
 
   // Resets the object back to its newly constructed state.
   void Reset();
@@ -99,7 +99,6 @@
   scoped_refptr<DOMStorageMap> map_;
   std::map<base::string16, int> ignore_key_mutations_;
   bool ignore_all_mutations_ = false;
-  std::string get_all_request_id_;
   mojom::LevelDBWrapperPtr leveldb_;
   mojo::AssociatedBinding<mojom::LevelDBObserver> binding_;
   LocalStorageCachedAreas* cached_areas_;
diff --git a/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc b/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc
index 97f67a36..d990a60 100644
--- a/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc
+++ b/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc
@@ -45,7 +45,7 @@
   int route_id = 0;
 
   ViewMsg_BeginFrame msg(
-      route_id, cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+      route_id, cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1));
 
   CompositorForwardingMessageFilter::Handler plus_handler =
       base::Bind(&CompositorForwardingMessageFilterTestHandler::OnPlusMethod,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index cf00e8f..2916c78 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -602,18 +602,7 @@
     const base::TimeTicks& browser_navigation_start,
     const base::TimeTicks& renderer_navigation_start) {
   DCHECK(!browser_navigation_start.is_null());
-  base::TimeTicks navigation_start =
-      std::min(browser_navigation_start, renderer_navigation_start);
-  base::TimeDelta difference =
-      renderer_navigation_start - browser_navigation_start;
-  if (difference > base::TimeDelta()) {
-    UMA_HISTOGRAM_TIMES("Navigation.Start.RendererBrowserDifference.Positive",
-                        difference);
-  } else {
-    UMA_HISTOGRAM_TIMES("Navigation.Start.RendererBrowserDifference.Negative",
-                        -difference);
-  }
-  return navigation_start;
+  return std::min(browser_navigation_start, renderer_navigation_start);
 }
 
 // PlzNavigate
diff --git a/content/renderer/service_worker/embedded_worker_dispatcher.cc b/content/renderer/service_worker/embedded_worker_dispatcher.cc
index bf046f2..57e0085e10 100644
--- a/content/renderer/service_worker/embedded_worker_dispatcher.cc
+++ b/content/renderer/service_worker/embedded_worker_dispatcher.cc
@@ -58,9 +58,9 @@
 
 void EmbeddedWorkerDispatcher::WorkerContextDestroyed(
     int embedded_worker_id) {
-  UnregisterWorker(embedded_worker_id);
   RenderThreadImpl::current()->thread_safe_sender()->Send(
       new EmbeddedWorkerHostMsg_WorkerStopped(embedded_worker_id));
+  UnregisterWorker(embedded_worker_id);
 }
 
 void EmbeddedWorkerDispatcher::OnStartWorker(
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 38014c8..aa699bc 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -28,12 +28,18 @@
 void EmbeddedWorkerInstanceClientImpl::StopWorkerCompleted() {
   DCHECK(embedded_worker_id_);
   DCHECK(stop_callback_);
-  dispatcher_->UnregisterWorker(embedded_worker_id_.value());
-  embedded_worker_id_.reset();
-  stop_callback_.Run();
   TRACE_EVENT0("ServiceWorker",
                "EmbeddedWorkerInstanceClientImpl::StopWorkerCompleted");
+  // TODO(falken): The signals to the browser should be in the order:
+  // (1) WorkerStopped (via stop_callback_)
+  // (2) ProviderDestroyed (via UnregisterWorker destroying
+  //     WebEmbeddedWorkerImpl)
+  // But this ordering is currently not guaranteed since the Mojo pipes are
+  // different. https://crbug.com/676526
+  stop_callback_.Run();
   stop_callback_.Reset();
+  dispatcher_->UnregisterWorker(embedded_worker_id_.value());
+  embedded_worker_id_.reset();
   wrapper_ = nullptr;
 }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d2e8d356..bf3ff46e 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -151,6 +151,8 @@
     "mock_keyboard.h",
     "mock_keyboard_driver_win.cc",
     "mock_keyboard_driver_win.h",
+    "mock_leveldb_database.cc",
+    "mock_leveldb_database.h",
     "mock_permission_manager.cc",
     "mock_permission_manager.h",
     "mock_render_process.cc",
@@ -222,6 +224,7 @@
     "//cc/ipc",
     "//cc/surfaces",
     "//components/display_compositor",
+    "//components/leveldb/public/interfaces",
     "//content/app:both_for_content_tests",
     "//content/browser:for_content_tests",
     "//content/browser/speech/proto",
@@ -994,6 +997,7 @@
     "../browser/byte_stream_unittest.cc",
     "../browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc",
     "../browser/cache_storage/cache_storage_cache_unittest.cc",
+    "../browser/cache_storage/cache_storage_index_unittest.cc",
     "../browser/cache_storage/cache_storage_manager_unittest.cc",
     "../browser/cache_storage/cache_storage_operation_unittest.cc",
     "../browser/cache_storage/cache_storage_scheduler_unittest.cc",
@@ -1016,6 +1020,7 @@
     "../browser/dom_storage/dom_storage_area_unittest.cc",
     "../browser/dom_storage/dom_storage_context_impl_unittest.cc",
     "../browser/dom_storage/dom_storage_database_unittest.cc",
+    "../browser/dom_storage/local_storage_context_mojo_unittest.cc",
     "../browser/dom_storage/session_storage_database_unittest.cc",
     "../browser/download/base_file_unittest.cc",
     "../browser/download/base_file_win_unittest.cc",
@@ -1276,6 +1281,9 @@
     "../child/shared_memory_data_consumer_handle_unittest.cc",
     "../child/shared_memory_received_data_factory_unittest.cc",
     "../child/site_isolation_stats_gatherer_unittest.cc",
+    "../child/test_request_peer.cc",
+    "../child/test_request_peer.h",
+    "../child/url_loader_client_impl_unittest.cc",
     "../child/url_response_body_consumer_unittest.cc",
     "../child/v8_value_converter_impl_unittest.cc",
     "../child/web_data_consumer_handle_impl_unittest.cc",
@@ -1388,7 +1396,6 @@
     "//cc/surfaces",
     "//components/display_compositor",
     "//components/leveldb/public/cpp",
-    "//components/leveldb/public/interfaces",
     "//components/payments:payment_app",
     "//components/rappor:test_support",
     "//content:resources",
diff --git a/content/test/DEPS b/content/test/DEPS
index 3591ebda..34a4d4c 100644
--- a/content/test/DEPS
+++ b/content/test/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   # Allow inclusion of specific components that we depend on.
   # See comment in content/DEPS for which components are allowed.
+  "+components/leveldb",
   "+components/scheduler/renderer",
   "+components/scheduler/test",
 
diff --git a/content/test/data/dom_storage/sanity_check.js b/content/test/data/dom_storage/sanity_check.js
index c41e493..ae547bb 100644
--- a/content/test/data/dom_storage/sanity_check.js
+++ b/content/test/data/dom_storage/sanity_check.js
@@ -12,7 +12,7 @@
     sanityCheck(window.localStorage);
     debug('Checking window.sessionStorage');
     sanityCheck(window.sessionStorage);
-    done();
+    window.setTimeout(done, 0);
   } catch(e) {
     fail(e);
   }
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index 4f1b59a..757260a 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -24,10 +24,6 @@
     self.Fail('Pixel_ScissorTestWithPreserveDrawingBuffer',
         ['android'], bug=521588)
 
-    # TODO(xlai): Check / generate reference images.
-    self.Fail('Pixel_OffscreenCanvas2DResizeOnWorker', bug=662498)
-    self.Fail('Pixel_OffscreenCanvasWebglResizeOnWorker', bug=662498)
-
     # TODO(ccameron) fix these on Mac Retina
     self.Fail('Pixel_CSS3DBlueBox', ['mac'], bug=533690)
 
diff --git a/content/test/mock_leveldb_database.cc b/content/test/mock_leveldb_database.cc
new file mode 100644
index 0000000..60a5db7
--- /dev/null
+++ b/content/test/mock_leveldb_database.cc
@@ -0,0 +1,177 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/mock_leveldb_database.h"
+
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+
+namespace content {
+
+namespace {
+
+leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key,
+                                           std::vector<uint8_t> value) {
+  leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New();
+  result->key = std::move(key);
+  result->value = std::move(value);
+  return result;
+}
+
+base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) {
+  return base::StringPiece(reinterpret_cast<const char*>(data.data()),
+                           data.size());
+}
+
+bool StartsWith(const std::vector<uint8_t>& key,
+                const std::vector<uint8_t>& prefix) {
+  return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix),
+                          base::CompareCase::SENSITIVE);
+}
+
+std::vector<uint8_t> successor(std::vector<uint8_t> data) {
+  for (unsigned i = data.size(); i > 0; --i) {
+    if (data[i - 1] < 255) {
+      data[i - 1]++;
+      return data;
+    }
+  }
+  NOTREACHED();
+  return data;
+}
+
+}  // namespace
+
+MockLevelDBDatabase::MockLevelDBDatabase(
+    std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
+    : mock_data_(*mock_data) {}
+
+void MockLevelDBDatabase::Put(const std::vector<uint8_t>& key,
+                              const std::vector<uint8_t>& value,
+                              const PutCallback& callback) {
+  mock_data_[key] = value;
+  callback.Run(leveldb::mojom::DatabaseError::OK);
+}
+
+void MockLevelDBDatabase::Delete(const std::vector<uint8_t>& key,
+                                 const DeleteCallback& callback) {
+  mock_data_.erase(key);
+  callback.Run(leveldb::mojom::DatabaseError::OK);
+}
+
+void MockLevelDBDatabase::DeletePrefixed(
+    const std::vector<uint8_t>& key_prefix,
+    const DeletePrefixedCallback& callback) {
+  mock_data_.erase(mock_data_.lower_bound(key_prefix),
+                   mock_data_.lower_bound(successor(key_prefix)));
+  callback.Run(leveldb::mojom::DatabaseError::OK);
+}
+
+void MockLevelDBDatabase::Write(
+    std::vector<leveldb::mojom::BatchedOperationPtr> operations,
+    const WriteCallback& callback) {
+  for (const auto& op : operations) {
+    switch (op->type) {
+      case leveldb::mojom::BatchOperationType::PUT_KEY:
+        mock_data_[op->key] = *op->value;
+        break;
+      case leveldb::mojom::BatchOperationType::DELETE_KEY:
+        mock_data_.erase(op->key);
+        break;
+      case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY:
+        mock_data_.erase(mock_data_.lower_bound(op->key),
+                         mock_data_.lower_bound(successor(op->key)));
+        break;
+    }
+  }
+  callback.Run(leveldb::mojom::DatabaseError::OK);
+}
+
+void MockLevelDBDatabase::Get(const std::vector<uint8_t>& key,
+                              const GetCallback& callback) {
+  if (mock_data_.find(key) != mock_data_.end()) {
+    callback.Run(leveldb::mojom::DatabaseError::OK, mock_data_[key]);
+  } else {
+    callback.Run(leveldb::mojom::DatabaseError::NOT_FOUND,
+                 std::vector<uint8_t>());
+  }
+}
+
+void MockLevelDBDatabase::GetPrefixed(
+    const std::vector<uint8_t>& key_prefix,
+    const GetPrefixedCallback& callback) {
+  std::vector<leveldb::mojom::KeyValuePtr> data;
+  for (const auto& row : mock_data_) {
+    if (StartsWith(row.first, key_prefix)) {
+      data.push_back(CreateKeyValue(row.first, row.second));
+    }
+  }
+  callback.Run(leveldb::mojom::DatabaseError::OK, std::move(data));
+}
+
+void MockLevelDBDatabase::GetSnapshot(
+    const GetSnapshotCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::ReleaseSnapshot(
+    const base::UnguessableToken& snapshot) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::GetFromSnapshot(
+    const base::UnguessableToken& snapshot,
+    const std::vector<uint8_t>& key,
+    const GetCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::NewIterator(
+    const NewIteratorCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::NewIteratorFromSnapshot(
+    const base::UnguessableToken& snapshot,
+    const NewIteratorFromSnapshotCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::ReleaseIterator(
+    const base::UnguessableToken& iterator) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::IteratorSeekToFirst(
+    const base::UnguessableToken& iterator,
+    const IteratorSeekToFirstCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::IteratorSeekToLast(
+    const base::UnguessableToken& iterator,
+    const IteratorSeekToLastCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::IteratorSeek(
+    const base::UnguessableToken& iterator,
+    const std::vector<uint8_t>& target,
+    const IteratorSeekToLastCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::IteratorNext(
+    const base::UnguessableToken& iterator,
+    const IteratorNextCallback& callback) {
+  NOTREACHED();
+}
+
+void MockLevelDBDatabase::IteratorPrev(
+    const base::UnguessableToken& iterator,
+    const IteratorPrevCallback& callback) {
+  NOTREACHED();
+}
+
+}  // namespace content
diff --git a/content/test/mock_leveldb_database.h b/content/test/mock_leveldb_database.h
new file mode 100644
index 0000000..29a2abd
--- /dev/null
+++ b/content/test/mock_leveldb_database.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/leveldb/public/interfaces/leveldb.mojom.h"
+
+namespace content {
+
+// Simple implementation of the leveldb::mojom::LevelDBDatabase interface that
+// is backed by a std::map.
+
+class MockLevelDBDatabase : public leveldb::mojom::LevelDBDatabase {
+ public:
+  // |mock_data| must not be null and must outlive this MockLevelDBDatabase
+  // instance.
+  explicit MockLevelDBDatabase(
+      std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data);
+
+  // LevelDBDatabase:
+  void Put(const std::vector<uint8_t>& key,
+           const std::vector<uint8_t>& value,
+           const PutCallback& callback) override;
+  void Delete(const std::vector<uint8_t>& key,
+              const DeleteCallback& callback) override;
+  void DeletePrefixed(const std::vector<uint8_t>& key_prefix,
+                      const DeletePrefixedCallback& callback) override;
+  void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations,
+             const WriteCallback& callback) override;
+  void Get(const std::vector<uint8_t>& key,
+           const GetCallback& callback) override;
+  void GetPrefixed(const std::vector<uint8_t>& key_prefix,
+                   const GetPrefixedCallback& callback) override;
+  void GetSnapshot(const GetSnapshotCallback& callback) override;
+  void ReleaseSnapshot(const base::UnguessableToken& snapshot) override;
+  void GetFromSnapshot(const base::UnguessableToken& snapshot,
+                       const std::vector<uint8_t>& key,
+                       const GetCallback& callback) override;
+  void NewIterator(const NewIteratorCallback& callback) override;
+  void NewIteratorFromSnapshot(
+      const base::UnguessableToken& snapshot,
+      const NewIteratorFromSnapshotCallback& callback) override;
+  void ReleaseIterator(const base::UnguessableToken& iterator) override;
+  void IteratorSeekToFirst(
+      const base::UnguessableToken& iterator,
+      const IteratorSeekToFirstCallback& callback) override;
+  void IteratorSeekToLast(const base::UnguessableToken& iterator,
+                          const IteratorSeekToLastCallback& callback) override;
+  void IteratorSeek(const base::UnguessableToken& iterator,
+                    const std::vector<uint8_t>& target,
+                    const IteratorSeekToLastCallback& callback) override;
+  void IteratorNext(const base::UnguessableToken& iterator,
+                    const IteratorNextCallback& callback) override;
+  void IteratorPrev(const base::UnguessableToken& iterator,
+                    const IteratorPrevCallback& callback) override;
+
+ private:
+  std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data_;
+};
+
+}  // namespace content
diff --git a/extensions/renderer/resources/media_router_bindings.js b/extensions/renderer/resources/media_router_bindings.js
index 40ebfab8..0373e036 100644
--- a/extensions/renderer/resources/media_router_bindings.js
+++ b/extensions/renderer/resources/media_router_bindings.js
@@ -5,22 +5,16 @@
 var mediaRouter;
 
 define('media_router_bindings', [
-    'mojo/public/js/bindings',
-    'mojo/public/js/core',
     'content/public/renderer/frame_interfaces',
     'chrome/browser/media/router/mojo/media_router.mojom',
     'extensions/common/mojo/keep_alive.mojom',
     'mojo/common/time.mojom',
-    'mojo/public/js/connection',
-    'mojo/public/js/router',
-], function(bindings,
-            core,
-            frameInterfaces,
+    'mojo/public/js/bindings',
+], function(frameInterfaces,
             mediaRouterMojom,
             keepAliveMojom,
             timeMojom,
-            connector,
-            routerModule) {
+            bindings) {
   'use strict';
 
   /**
@@ -187,14 +181,14 @@
   /**
    * Creates a new MediaRouter.
    * Converts a route struct to its Mojo form.
-   * @param {!MediaRouterService} service
+   * @param {!mediaRouterMojom.MediaRouterPtr} service
    * @constructor
    */
   function MediaRouter(service) {
     /**
      * The Mojo service proxy. Allows extension code to call methods that reside
      * in the browser.
-     * @type {!MediaRouterService}
+     * @type {!mediaRouterMojom.MediaRouterPtr}
      */
     this.service_ = service;
 
@@ -287,10 +281,10 @@
    */
   MediaRouter.prototype.setKeepAlive = function(keepAlive) {
     if (keepAlive === false && this.keepAlive_) {
-      this.keepAlive_.close();
+      this.keepAlive_.ptr.reset();
       this.keepAlive_ = null;
     } else if (keepAlive === true && !this.keepAlive_) {
-      this.keepAlive_ = new routerModule.Router(
+      this.keepAlive_ = new keepAliveMojom.KeepAlivePtr(
           frameInterfaces.getInterface(keepAliveMojom.KeepAlive.name));
     }
   };
@@ -498,8 +492,6 @@
    * @constructor
    */
   function MediaRouteProvider(mediaRouter) {
-    mediaRouterMojom.MediaRouteProvider.stubClass.call(this);
-
     /**
      * Object containing JS callbacks into Provider Manager code.
      * @type {!MediaRouterHandlers}
@@ -512,8 +504,6 @@
      */
     this.mediaRouter_ = mediaRouter;
   }
-  MediaRouteProvider.prototype = Object.create(
-      mediaRouterMojom.MediaRouteProvider.stubClass.prototype);
 
   /*
    * Sets the callback handler used to invoke methods in the provider manager.
@@ -813,9 +803,8 @@
     });
   };
 
-  mediaRouter = new MediaRouter(connector.bindHandleToProxy(
-      frameInterfaces.getInterface(mediaRouterMojom.MediaRouter.name),
-      mediaRouterMojom.MediaRouter));
+  mediaRouter = new MediaRouter(new mediaRouterMojom.MediaRouterPtr(
+      frameInterfaces.getInterface(mediaRouterMojom.MediaRouter.name)));
 
   return mediaRouter;
 });
diff --git a/extensions/renderer/resources/mime_handler_private_custom_bindings.js b/extensions/renderer/resources/mime_handler_private_custom_bindings.js
index 4a515394..1fa03663 100644
--- a/extensions/renderer/resources/mime_handler_private_custom_bindings.js
+++ b/extensions/renderer/resources/mime_handler_private_custom_bindings.js
@@ -15,13 +15,11 @@
 var servicePromise = Promise.all([
     requireAsync('content/public/renderer/frame_interfaces'),
     requireAsync('extensions/common/api/mime_handler.mojom'),
-    requireAsync('mojo/public/js/router'),
 ]).then(function(modules) {
   var frameInterfaces = modules[0];
   var mojom = modules[1];
-  var routerModule = modules[2];
-  return new mojom.MimeHandlerService.proxyClass(new routerModule.Router(
-      frameInterfaces.getInterface(mojom.MimeHandlerService.name)));
+  return new mojom.MimeHandlerServicePtr(
+      frameInterfaces.getInterface(mojom.MimeHandlerService.name));
 });
 
 // Stores a promise to the GetStreamInfo() result to avoid making additional
diff --git a/extensions/test/data/api_test_base_unittest.js b/extensions/test/data/api_test_base_unittest.js
index 6aa8b78..094a623 100644
--- a/extensions/test/data/api_test_base_unittest.js
+++ b/extensions/test/data/api_test_base_unittest.js
@@ -36,14 +36,14 @@
   },
   function testMojoModulesAreAvailable() {
     Promise.all([
-      requireAsync('mojo/public/js/connection'),
+      requireAsync('mojo/public/js/bindings'),
       requireAsync('mojo/public/js/core'),
       requireAsync('content/public/renderer/frame_interfaces'),
     ]).then(test.callback(function(modules) {
-      var connection = modules[0];
+      var bindings = modules[0];
       var core = modules[1];
       var frameInterfaces = modules[2];
-      test.assertTrue(!!connection.Connection);
+      test.assertTrue(!!bindings.Binding);
       test.assertTrue(!!core.createMessagePipe);
       test.assertTrue(!!frameInterfaces.getInterface);
     }));
diff --git a/gpu/config/gpu_control_list_jsons.h b/gpu/config/gpu_control_list_jsons.h
index 671aa9e..71d574c 100644
--- a/gpu/config/gpu_control_list_jsons.h
+++ b/gpu/config/gpu_control_list_jsons.h
@@ -10,7 +10,6 @@
 namespace gpu {
 
 GPU_EXPORT extern const char kGpuDriverBugListJson[];
-GPU_EXPORT extern const char kGpuSwitchingListJson[];
 GPU_EXPORT extern const char kSoftwareRenderingListJson[];
 
 }  // namespace gpu
diff --git a/headless/lib/embedder_mojo_browsertest.cc b/headless/lib/embedder_mojo_browsertest.cc
index 3fb4ed1..0ae117b33 100644
--- a/headless/lib/embedder_mojo_browsertest.cc
+++ b/headless/lib/embedder_mojo_browsertest.cc
@@ -152,16 +152,12 @@
         "// fires after the requested modules have been loaded.              \n"
         "define([                                                            \n"
         "    'headless/lib/embedder_test.mojom',                             \n"
-        "    'mojo/public/js/core',                                          \n"
-        "    'mojo/public/js/router',                                        \n"
         "    'content/public/renderer/frame_interfaces',                     \n"
-        "    ], function(embedderMojom, mojoCore, routerModule,              \n"
-        "                frameInterfaces) {                                  \n"
+        "    ], function(embedderMojom, frameInterfaces) {                   \n"
         "  var testEmbedderService =                                         \n"
-        "      new embedderMojom.TestEmbedderService.proxyClass(             \n"
-        "          new routerModule.Router(                                  \n"
-        "              frameInterfaces.getInterface(                         \n"
-        "                  embedderMojom.TestEmbedderService.name)));        \n"
+        "      new embedderMojom.TestEmbedderServicePtr(                     \n"
+        "          frameInterfaces.getInterface(                             \n"
+        "              embedderMojom.TestEmbedderService.name));             \n"
         "                                                                    \n"
         "  // Send a message to the embedder!                                \n"
         "  testEmbedderService.returnTestResult('hello world');              \n"
diff --git a/headless/test/data/page_one.html b/headless/test/data/page_one.html
index 469b95ac..a29176e 100644
--- a/headless/test/data/page_one.html
+++ b/headless/test/data/page_one.html
@@ -6,16 +6,10 @@
 // fires after the requested modules have been loaded.
 define([
     'headless/lib/embedder_test.mojom',
-    'mojo/public/js/core',
-    'mojo/public/js/router',
     'content/public/renderer/frame_interfaces',
-    ], function(embedderMojom, mojoCore, routerModule,
-                frameInterfaces) {
-  var testEmbedderService =
-      new embedderMojom.TestEmbedderService.proxyClass(
-          new routerModule.Router(
-              frameInterfaces.getInterface(
-                  embedderMojom.TestEmbedderService.name)));
+    ], function(embedderMojom, frameInterfaces) {
+  var testEmbedderService = new embedderMojom.TestEmbedderServicePtr(
+      frameInterfaces.getInterface(embedderMojom.TestEmbedderService.name));
 
   // Send a message to the embedder!
   testEmbedderService.returnTestResult('page one');
diff --git a/headless/test/data/page_two.html b/headless/test/data/page_two.html
index 7cf22d4e..47edec7 100644
--- a/headless/test/data/page_two.html
+++ b/headless/test/data/page_two.html
@@ -6,16 +6,10 @@
 // fires after the requested modules have been loaded.
 define([
     'headless/lib/embedder_test.mojom',
-    'mojo/public/js/core',
-    'mojo/public/js/router',
     'content/public/renderer/frame_interfaces',
-    ], function(embedderMojom, mojoCore, routerModule,
-                frameInterfaces) {
-  var testEmbedderService =
-      new embedderMojom.TestEmbedderService.proxyClass(
-          new routerModule.Router(
-              frameInterfaces.getInterface(
-                  embedderMojom.TestEmbedderService.name)));
+    ], function(embedderMojom, frameInterfaces) {
+  var testEmbedderService = new embedderMojom.TestEmbedderServicePtr(
+      frameInterfaces.getInterface(embedderMojom.TestEmbedderService.name));
 
   // Send a message to the embedder!
   testEmbedderService.returnTestResult('page two');
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index 5ec36c1..5a494ce 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -54,7 +54,7 @@
       builders { name: "linux_chromium_compile_dbg_ng" }
       builders {
         name: "linux_chromium_headless_rel"
-        experiment_percentage: 10
+        experiment_percentage: 100
       }
       builders { name: "linux_chromium_rel_ng" }
     }
diff --git a/ios/OWNERS b/ios/OWNERS
index d1bdac3..0fbae9fe 100644
--- a/ios/OWNERS
+++ b/ios/OWNERS
@@ -1,5 +1,5 @@
-droger@chromium.org
 eugenebut@chromium.org
+marq@chromium.org
 noyau@chromium.org
 rohitrao@chromium.org
 sdefresne@chromium.org
@@ -7,4 +7,4 @@
 # per-file rules:
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
-per-file *.gyp=*
+per-file BUILD.gn=*
diff --git a/ios/chrome/browser/autofill/autofill_agent.mm b/ios/chrome/browser/autofill/autofill_agent.mm
index 1b95bf5a..b716c4a 100644
--- a/ios/chrome/browser/autofill/autofill_agent.mm
+++ b/ios/chrome/browser/autofill/autofill_agent.mm
@@ -693,7 +693,6 @@
                                fieldName:(const std::string&)fieldName
                                     type:(const std::string&)type
                                    value:(const std::string&)value
-                                 keyCode:(int)keyCode
                             inputMissing:(BOOL)inputMissing {
   if (!browserState_->GetPrefs()->GetBoolean(autofill::prefs::kAutofillEnabled))
     return;
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index 833c7581..cec559d 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -484,7 +484,6 @@
                                fieldName:(const std::string&)fieldName
                                     type:(const std::string&)type
                                    value:(const std::string&)value
-                                 keyCode:(int)keyCode
                             inputMissing:(BOOL)inputMissing {
   web::URLVerificationTrustLevel trustLevel;
   const GURL pageURL(webState->GetCurrentURL(&trustLevel));
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
index 68250b9e..c44ad27 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -285,8 +285,6 @@
                                  fieldName:"field"
                                       type:"type"
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
   EXPECT_TRUE(GetSuggestionView(input_accessory_view_));
 
@@ -303,8 +301,6 @@
                                  fieldName:"field"
                                       type:"blur"  // blur!
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
   EXPECT_FALSE(GetSuggestionView(input_accessory_view_));
 }
@@ -320,8 +316,6 @@
                                  fieldName:"field"
                                       type:"type"
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
 
   // The suggestions accessory view should be empty.
@@ -348,8 +342,6 @@
                                  fieldName:"field"
                                       type:"type"
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
 
   // The providers should each be asked if they have suggestions for the
@@ -397,8 +389,6 @@
                                  fieldName:"field"
                                       type:"type"
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
 
   // Since the first provider has suggestions available, it and only it
@@ -436,8 +426,6 @@
                                  fieldName:"field"
                                       type:"type"
                                      value:"value"
-                                   keyCode:web::WebStateObserver::
-                                               kInvalidFormKeyCode
                               inputMissing:false];
 
   // Selecting a suggestion should notify the delegate.
diff --git a/ios/chrome/browser/crash_loop_detection_util.h b/ios/chrome/browser/crash_loop_detection_util.h
index 0d8118b..ec8692f 100644
--- a/ios/chrome/browser/crash_loop_detection_util.h
+++ b/ios/chrome/browser/crash_loop_detection_util.h
@@ -25,6 +25,9 @@
 // elapsed, or some deliberate user action has been taken).
 void ResetFailedStartupAttemptCount();
 
+// Resets the hidden state of failed startup attempt count for testing.
+void ResetFailedStartupAttemptCountForTests();
+
 }  // namespace crash_util
 
 #endif  // IOS_CHROME_BROWSER_CRASH_LOOP_DETECTION_UTIL_H_
diff --git a/ios/chrome/browser/crash_loop_detection_util.mm b/ios/chrome/browser/crash_loop_detection_util.mm
index 2eb1913..0de92d6c 100644
--- a/ios/chrome/browser/crash_loop_detection_util.mm
+++ b/ios/chrome/browser/crash_loop_detection_util.mm
@@ -11,13 +11,13 @@
 #endif
 
 namespace {
+static int startup_attempt_count = -1;
 NSString* const kAppStartupFailureCountKey = @"AppStartupFailureCount";
 }
 
 namespace crash_util {
 
 int GetFailedStartupAttemptCount() {
-  static int startup_attempt_count = -1;
   if (startup_attempt_count == -1) {
     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
     startup_attempt_count = [defaults integerForKey:kAppStartupFailureCountKey];
@@ -42,4 +42,9 @@
   }
 }
 
+void ResetFailedStartupAttemptCountForTests() {
+  ResetFailedStartupAttemptCount();
+  startup_attempt_count = -1;
+}
+
 }  // namespace crash_util
diff --git a/ios/chrome/browser/crash_loop_detection_util_unittest.mm b/ios/chrome/browser/crash_loop_detection_util_unittest.mm
index c36670a0..ea620e8 100644
--- a/ios/chrome/browser/crash_loop_detection_util_unittest.mm
+++ b/ios/chrome/browser/crash_loop_detection_util_unittest.mm
@@ -15,6 +15,8 @@
 typedef PlatformTest CrashLoopDetectionUtilTest;
 
 TEST_F(CrashLoopDetectionUtilTest, FullCycle) {
+  crash_util::ResetFailedStartupAttemptCountForTests();
+
   // Simulate one prior crash.
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
   [defaults setInteger:1 forKey:kAppStartupAttemptCountKey];
diff --git a/ios/chrome/browser/dom_distiller/distiller_viewer.cc b/ios/chrome/browser/dom_distiller/distiller_viewer.cc
index a0bd94ab..46a26ab 100644
--- a/ios/chrome/browser/dom_distiller/distiller_viewer.cc
+++ b/ios/chrome/browser/dom_distiller/distiller_viewer.cc
@@ -39,13 +39,14 @@
 void DistillerViewer::OnArticleReady(
     const dom_distiller::DistilledArticleProto* article_proto) {
   DomDistillerRequestViewBase::OnArticleReady(article_proto);
-  if (article_proto->pages_size() > 0) {
+  bool is_empty = article_proto->pages_size() == 0 ||
+                  article_proto->pages(0).html().empty();
+  if (!is_empty) {
     std::vector<ImageInfo> images;
     for (int i = 0; i < article_proto->pages(0).image_size(); i++) {
       auto image = article_proto->pages(0).image(i);
       images.push_back(ImageInfo{GURL(image.url()), image.data()});
     }
-
     const std::string html = viewer::GetUnsafeArticleTemplateHtml(
         url_.spec(), distilled_page_prefs_->GetTheme(),
         distilled_page_prefs_->GetFontFamily());
diff --git a/ios/chrome/browser/metrics/first_user_action_recorder.cc b/ios/chrome/browser/metrics/first_user_action_recorder.cc
index d93836cd5..aac5e0c 100644
--- a/ios/chrome/browser/metrics/first_user_action_recorder.cc
+++ b/ios/chrome/browser/metrics/first_user_action_recorder.cc
@@ -91,8 +91,6 @@
       background_duration_(background_duration),
       action_callback_(base::Bind(&FirstUserActionRecorder::OnUserAction,
                                   base::Unretained(this))) {
-  base::SetRecordActionTaskRunner(
-      web::WebThread::GetTaskRunnerForThread(web::WebThread::UI));
   base::AddActionCallback(action_callback_);
 }
 
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
index 4c942ada..c02088a 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
@@ -69,9 +69,7 @@
 class MobileSessionShutdownMetricsProviderTest
     : public testing::TestWithParam<int> {
  public:
-  MobileSessionShutdownMetricsProviderTest()
-      : task_runner_(new base::TestSimpleTaskRunner) {
-    base::SetRecordActionTaskRunner(task_runner_);
+  MobileSessionShutdownMetricsProviderTest() {
     metrics::MetricsService::RegisterPrefs(local_state_.registry());
   }
 
@@ -82,7 +80,6 @@
   std::unique_ptr<metrics::MetricsService> metrics_service_;
   std::unique_ptr<MobileSessionShutdownMetricsProviderForTesting>
       metrics_provider_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MobileSessionShutdownMetricsProviderTest);
diff --git a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm
index d96792be..e1c8020c 100644
--- a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm
+++ b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm
@@ -275,8 +275,6 @@
                                    fieldName:base::SysNSStringToUTF8(field_name)
                                         type:base::SysNSStringToUTF8(type)
                                        value:""
-                                     keyCode:web::WebStateObserver::
-                                                 kInvalidFormKeyCode
                                 inputMissing:false];
   }
 
diff --git a/ios/chrome/browser/reading_list/BUILD.gn b/ios/chrome/browser/reading_list/BUILD.gn
index d68daec..500a8c3 100644
--- a/ios/chrome/browser/reading_list/BUILD.gn
+++ b/ios/chrome/browser/reading_list/BUILD.gn
@@ -11,8 +11,6 @@
     "reading_list_download_service.h",
     "reading_list_download_service_factory.cc",
     "reading_list_download_service_factory.h",
-    "reading_list_entry_loading_util.h",
-    "reading_list_entry_loading_util.mm",
     "reading_list_model_factory.cc",
     "reading_list_model_factory.h",
     "reading_list_web_state_observer.h",
@@ -44,7 +42,6 @@
   testonly = true
   sources = [
     "offline_url_utils_unittest.cc",
-    "reading_list_entry_loading_util_unittest.mm",
     "url_downloader_unittest.mm",
   ]
   deps = [
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.cc b/ios/chrome/browser/reading_list/offline_url_utils.cc
index b3d3037..e2ceb6c 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils.cc
+++ b/ios/chrome/browser/reading_list/offline_url_utils.cc
@@ -45,14 +45,12 @@
 }
 
 GURL FileURLForDistilledURL(const GURL& distilled_url,
-                            const base::FilePath& profile_path,
+                            const base::FilePath& offline_path,
                             GURL* resources_root_url) {
   if (!distilled_url.is_valid()) {
     return GURL();
   }
   DCHECK(distilled_url.SchemeIs(kChromeUIScheme));
-  base::FilePath offline_path = OfflineRootDirectoryPath(profile_path);
-
   GURL file_url(base::StringPrintf("%s%s", url::kFileScheme,
                                    url::kStandardSchemeSeparator) +
                 offline_path.value() + distilled_url.path());
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.h b/ios/chrome/browser/reading_list/offline_url_utils.h
index bee8586..479b75c 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils.h
+++ b/ios/chrome/browser/reading_list/offline_url_utils.h
@@ -22,9 +22,9 @@
 // The file URL pointing to the local file to load to display |distilled_url|.
 // If |resources_root_url| is not nullptr, it is set to a file URL to the
 // directory conatining all the resources needed by |distilled_url|.
-// |profile_path| is the path to the profile directory.
+// |offline_path| is the root path to the directory containing offline files.
 GURL FileURLForDistilledURL(const GURL& distilled_url,
-                            const base::FilePath& profile_path,
+                            const base::FilePath& offline_path,
                             GURL* resources_root_url);
 
 // Returns whether the URL points to a chrome offline URL.
diff --git a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
index 7beea10..d711ca5 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
+++ b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
@@ -48,20 +48,20 @@
 // Checks the resource root for chrome://offline/MD5/page.html is
 // file://profile_path/Offline/MD5
 TEST(OfflineURLUtilsTest, FileURLForDistilledURLTest) {
-  base::FilePath profile_path("/profile_path");
+  base::FilePath offline_path("/profile_path/Offline");
   GURL file_url =
-      reading_list::FileURLForDistilledURL(GURL(), profile_path, nullptr);
+      reading_list::FileURLForDistilledURL(GURL(), offline_path, nullptr);
   EXPECT_FALSE(file_url.is_valid());
 
   GURL distilled_url("chrome://offline/MD5/page.html");
-  file_url = reading_list::FileURLForDistilledURL(distilled_url, profile_path,
+  file_url = reading_list::FileURLForDistilledURL(distilled_url, offline_path,
                                                   nullptr);
   EXPECT_TRUE(file_url.is_valid());
   EXPECT_TRUE(file_url.SchemeIsFile());
   EXPECT_EQ("/profile_path/Offline/MD5/page.html", file_url.path());
 
   GURL resource_url;
-  file_url = reading_list::FileURLForDistilledURL(distilled_url, profile_path,
+  file_url = reading_list::FileURLForDistilledURL(distilled_url, offline_path,
                                                   &resource_url);
   EXPECT_TRUE(resource_url.is_valid());
   EXPECT_TRUE(resource_url.SchemeIsFile());
diff --git a/ios/chrome/browser/reading_list/reading_list_download_service.cc b/ios/chrome/browser/reading_list/reading_list_download_service.cc
index 24cdc66d..35047e8 100644
--- a/ios/chrome/browser/reading_list/reading_list_download_service.cc
+++ b/ios/chrome/browser/reading_list/reading_list_download_service.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/reading_list/ios/offline_url_utils.h"
 #include "components/reading_list/ios/reading_list_entry.h"
 #include "components/reading_list/ios/reading_list_model.h"
 #include "ios/web/public/web_thread.h"
@@ -42,6 +43,7 @@
     PrefService* prefs,
     base::FilePath chrome_profile_path)
     : reading_list_model_(reading_list_model),
+      chrome_profile_path_(chrome_profile_path),
       had_connection_(!net::NetworkChangeNotifier::IsOffline()),
       weak_ptr_factory_(this) {
   DCHECK(reading_list_model);
@@ -62,6 +64,10 @@
   reading_list_model_->AddObserver(this);
 }
 
+base::FilePath ReadingListDownloadService::OfflineRoot() const {
+  return reading_list::OfflineRootDirectoryPath(chrome_profile_path_);
+}
+
 void ReadingListDownloadService::Shutdown() {
   reading_list_model_->RemoveObserver(this);
 }
diff --git a/ios/chrome/browser/reading_list/reading_list_download_service.h b/ios/chrome/browser/reading_list/reading_list_download_service.h
index 62cd7b40..8f70e28 100644
--- a/ios/chrome/browser/reading_list/reading_list_download_service.h
+++ b/ios/chrome/browser/reading_list/reading_list_download_service.h
@@ -41,6 +41,9 @@
   // Initializes the reading list download service.
   void Initialize();
 
+  // The root folder containing all the offline files.
+  virtual base::FilePath OfflineRoot() const;
+
   // KeyedService implementation.
   void Shutdown() override;
 
@@ -83,6 +86,7 @@
       net::NetworkChangeNotifier::ConnectionType type) override;
 
   ReadingListModel* reading_list_model_;
+  base::FilePath chrome_profile_path_;
   std::unique_ptr<URLDownloader> url_downloader_;
   std::vector<GURL> url_to_download_cellular_;
   std::vector<GURL> url_to_download_wifi_;
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h
deleted file mode 100644
index 0218cda..0000000
--- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_
-#define IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_
-
-class ReadingListEntry;
-class ReadingListModel;
-
-namespace web {
-class WebState;
-};
-
-namespace reading_list {
-
-// Loads the URL of the |entry| into the |web_state|. If the entry is
-// successfully loaded, marks the entry as read.
-void LoadReadingListEntry(ReadingListEntry const& entry,
-                          ReadingListModel* model,
-                          web::WebState* web_state);
-
-// Loads the distilled URL of the |entry| into the |web_state|, and marks
-// |entry| as read. |entry->DistilledState()| must be |PROCESSED|.
-void LoadReadingListDistilled(ReadingListEntry const& entry,
-                              ReadingListModel* model,
-                              web::WebState* web_state);
-};
-
-#endif  // IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm
deleted file mode 100644
index 3b34fc6..0000000
--- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
-
-#include "components/reading_list/ios/reading_list_entry.h"
-#include "components/reading_list/ios/reading_list_model.h"
-#include "ios/chrome/browser/reading_list/offline_url_utils.h"
-#include "ios/chrome/browser/reading_list/reading_list_web_state_observer.h"
-#import "ios/web/public/navigation_manager.h"
-#import "ios/web/public/web_state/web_state.h"
-#include "net/base/network_change_notifier.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace reading_list {
-
-void LoadReadingListEntry(ReadingListEntry const& entry,
-                          ReadingListModel* model,
-                          web::WebState* web_state) {
-  // TODO(crbug.com/625617): Evaluate whether NetworkChangeNotifier
-  // correctly detects when users are offline.
-  bool open_distilled_entry =
-      net::NetworkChangeNotifier::IsOffline() &&
-      entry.DistilledState() == ReadingListEntry::PROCESSED;
-  if (open_distilled_entry) {
-    return LoadReadingListDistilled(entry, model, web_state);
-  }
-
-  DCHECK(entry.URL().is_valid());
-  web::NavigationManager::WebLoadParams params(entry.URL());
-  params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
-  web_state->GetNavigationManager()->LoadURLWithParams(params);
-  ReadingListWebStateObserver* web_state_observer =
-      ReadingListWebStateObserver::FromWebState(web_state, model);
-  web_state_observer->StartCheckingProgress(entry.URL());
-}
-
-void LoadReadingListDistilled(ReadingListEntry const& entry,
-                              ReadingListModel* model,
-                              web::WebState* web_state) {
-  DCHECK(entry.DistilledState() == ReadingListEntry::PROCESSED);
-  GURL url =
-      reading_list::DistilledURLForPath(entry.DistilledPath(), entry.URL());
-  DCHECK(url.is_valid());
-  web::NavigationManager::WebLoadParams params(url);
-  params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
-  web_state->GetNavigationManager()->LoadURLWithParams(params);
-  model->SetReadStatus(entry.URL(), true);
-}
-
-}  //  namespace reading_list
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm b/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm
deleted file mode 100644
index 36e19e1e..0000000
--- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/reading_list/ios/reading_list_model_impl.h"
-#include "ios/chrome/browser/reading_list/offline_url_utils.h"
-#import "ios/web/public/navigation_item.h"
-#import "ios/web/public/navigation_manager.h"
-#include "ios/web/public/test/web_test_with_web_state.h"
-#import "ios/web/public/web_state/web_state.h"
-#include "net/base/network_change_notifier.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// A mock NetworkChangeNotifier that will report the network state passed in the
-// constructor.
-class MockNetworkChangeNotifier : public net::NetworkChangeNotifier {
- public:
-  MockNetworkChangeNotifier(ConnectionType connection)
-      : NetworkChangeNotifier() {
-    connection_ = connection;
-  }
-
-  ~MockNetworkChangeNotifier() override {}
-
-  ConnectionType GetCurrentConnectionType() const override {
-    return connection_;
-  };
-
- private:
-  ConnectionType connection_;
-};
-
-// Test fixture to test loading of Reading list entries.
-typedef web::WebTestWithWebState ReadingListEntryLoadingUtilTest;
-
-// Tests that loading a not distilled entry with network will load online
-// version.
-TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOnlineWODistilled) {
-  MockNetworkChangeNotifier network_enabler(
-      net::NetworkChangeNotifier::CONNECTION_WIFI);
-  GURL url("http://foo.bar");
-  auto reading_list_model =
-      base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
-  reading_list_model->AddEntry(url, "title");
-  const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url);
-  reading_list::LoadReadingListEntry(*entry, reading_list_model.get(),
-                                     web_state());
-  web::NavigationManager* navigation_manager =
-      web_state()->GetNavigationManager();
-  EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url);
-  // Entry is only marked read when successfully loaded.
-  EXPECT_FALSE(entry->IsRead());
-}
-
-// Tests that loading a distilled entry with network will load online version.
-TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOnlineWithistilled) {
-  MockNetworkChangeNotifier network_enabler(
-      net::NetworkChangeNotifier::CONNECTION_WIFI);
-  GURL url("http://foo.bar");
-  std::string distilled_path = "distilled/page.html";
-  auto reading_list_model =
-      base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
-  reading_list_model->AddEntry(url, "title");
-  reading_list_model->SetEntryDistilledPath(url,
-                                            base::FilePath(distilled_path));
-  const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url);
-  reading_list::LoadReadingListEntry(*entry, reading_list_model.get(),
-                                     web_state());
-  web::NavigationManager* navigation_manager =
-      web_state()->GetNavigationManager();
-  EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url);
-  // Entry is only marked read when successfully loaded.
-  EXPECT_FALSE(entry->IsRead());
-}
-
-// Tests that loading a not distilled entry without network will load online
-// version.
-TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOfflineWODistilled) {
-  MockNetworkChangeNotifier network_disabler(
-      net::NetworkChangeNotifier::CONNECTION_NONE);
-  GURL url("http://foo.bar");
-  auto reading_list_model =
-      base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
-  reading_list_model->AddEntry(url, "title");
-  const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url);
-  reading_list::LoadReadingListEntry(*entry, reading_list_model.get(),
-                                     web_state());
-  web::NavigationManager* navigation_manager =
-      web_state()->GetNavigationManager();
-  EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url);
-  // Entry is only marked read when successfully loaded.
-  EXPECT_FALSE(entry->IsRead());
-}
-
-// Tests that loading a distilled entry without network will load offline
-// version.
-TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOfflineWithDistilled) {
-  MockNetworkChangeNotifier network_disabler(
-      net::NetworkChangeNotifier::CONNECTION_NONE);
-  GURL url("http://foo.bar");
-  std::string distilled_path = "distilled/page.html";
-  auto reading_list_model =
-      base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
-  reading_list_model->AddEntry(url, "title");
-  reading_list_model->SetEntryDistilledPath(url,
-                                            base::FilePath(distilled_path));
-  const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url);
-  reading_list::LoadReadingListEntry(*entry, reading_list_model.get(),
-                                     web_state());
-  web::NavigationManager* navigation_manager =
-      web_state()->GetNavigationManager();
-  EXPECT_NE(navigation_manager->GetPendingItem()->GetURL(), url);
-  EXPECT_EQ(
-      navigation_manager->GetPendingItem()->GetURL(),
-      reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL()));
-  EXPECT_TRUE(entry->IsRead());
-}
-
-// Tests that loading a distilled version of an entry.
-TEST_F(ReadingListEntryLoadingUtilTest, TestLoadReadingListDistilled) {
-  GURL url("http://foo.bar");
-  std::string distilled_path = "distilled/page.html";
-  auto reading_list_model =
-      base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
-  reading_list_model->AddEntry(url, "title");
-  reading_list_model->SetEntryDistilledPath(url,
-                                            base::FilePath(distilled_path));
-  const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url);
-  reading_list::LoadReadingListDistilled(*entry, reading_list_model.get(),
-                                         web_state());
-  web::NavigationManager* navigation_manager =
-      web_state()->GetNavigationManager();
-  EXPECT_NE(navigation_manager->GetPendingItem()->GetURL(), url);
-  EXPECT_EQ(
-      navigation_manager->GetPendingItem()->GetURL(),
-      reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL()));
-  EXPECT_TRUE(entry->IsRead());
-}
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.h b/ios/chrome/browser/reading_list/reading_list_web_state_observer.h
index 47fcfb6f..1c16afe 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.h
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.h
@@ -12,6 +12,10 @@
 
 class ReadingListModel;
 
+namespace web {
+class NavigationItem;
+}
+
 // Observes the loading of pages coming from the reading list, determines
 // whether loading an offline version of the page is needed, and actually
 // trigger the loading of the offline page (if possible).
@@ -23,34 +27,44 @@
 
   ~ReadingListWebStateObserver() override;
 
-  // Starts checking that the current navigation is loading quickly enough [1].
-  // If not, starts to load a distilled version of the page (if there is any).
-  // If that same WebStateObserver was already checking that a page was loading
-  // quickly enough, stops checking the loading of that page.
-  // [1] A page loading quickly enough is a page that has loaded 15% within
-  // 1 second.
-  void StartCheckingProgress(const GURL& pending_url);
-
  private:
   ReadingListWebStateObserver(web::WebState* web_state,
                               ReadingListModel* reading_list_model);
 
-  // Looks at the loading percentage. If less than 15%, attemps to load the
-  // offline version of that page.
+  // Looks at the loading percentage. If less than 25% * time, attemps to load
+  // the offline version of that page.
+  // |time| is the number of seconds since |StartCheckingProgress| was called.
   void VerifyIfReadingListEntryStartedLoading();
 
   friend class ReadingListWebStateObserverUserDataWrapper;
 
-  // Stop checking the loading of the |pending_url_|.
+  // Stops checking the loading of the |pending_url_|.
   // The WebState will still be observed, but no action will be done on events.
   void StopCheckingProgress();
 
+  // Loads the offline version of the URL in place of the current page.
+  void LoadOfflineReadingListEntry(web::NavigationItem* item);
+
+  // Returns if the current page with |url| has an offline version that can be
+  // displayed if the normal loading fails.
+  bool IsUrlAvailableOffline(const GURL& url) const;
+
+  // Checks if |item| should be observed or not.
+  // A non-null item should be observed if it is not already loading an offline
+  // URL.
+  bool ShouldObserveItem(web::NavigationItem* item) const;
+
   // WebContentsObserver implementation.
-  void DidStopLoading() override;
   void PageLoaded(
       web::PageLoadCompletionStatus load_completion_status) override;
   void WebStateDestroyed() override;
 
+  // Starts checking that the current navigation is loading quickly enough [1].
+  // If not, starts to load a distilled version of the page (if there is any).
+  // [1] A page loading quickly enough is a page that has loaded 25% within
+  // 1 second, 50% within 2 seconds and 75% within 3 seconds.
+  void DidStartLoading() override;
+
   ReadingListModel* reading_list_model_;
   std::unique_ptr<base::Timer> timer_;
   GURL pending_url_;
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
index b4aa9c09..cc079b7 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -9,7 +9,8 @@
 #include "base/memory/ptr_util.h"
 #include "components/reading_list/ios/reading_list_model.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/reading_list/offline_url_utils.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/navigation_manager.h"
@@ -83,27 +84,50 @@
   DCHECK(reading_list_model_);
 }
 
-void ReadingListWebStateObserver::DidStopLoading() {
-  StopCheckingProgress();
-}
-
-void ReadingListWebStateObserver::PageLoaded(
-    web::PageLoadCompletionStatus load_completion_status) {
-  if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS &&
-      pending_url_.is_valid()) {
-    reading_list_model_->SetReadStatus(pending_url_, true);
+bool ReadingListWebStateObserver::ShouldObserveItem(
+    web::NavigationItem* item) const {
+  if (!item) {
+    return false;
   }
-  StopCheckingProgress();
+  GURL loading_url = item->GetURL();
+  return !loading_url.SchemeIs(kChromeUIScheme) ||
+         loading_url.host() != kChromeUIOfflineHost;
 }
 
-void ReadingListWebStateObserver::WebStateDestroyed() {
-  StopCheckingProgress();
-  web_state()->RemoveUserData(kObserverKey);
+bool ReadingListWebStateObserver::IsUrlAvailableOffline(const GURL& url) const {
+  const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
+  return entry && entry->DistilledState() == ReadingListEntry::PROCESSED;
 }
 
-void ReadingListWebStateObserver::StartCheckingProgress(
-    const GURL& pending_url) {
-  pending_url_ = pending_url;
+void ReadingListWebStateObserver::DidStartLoading() {
+  if (!reading_list_model_->loaded() || !web_state() ||
+      web_state()->IsShowingWebInterstitial()) {
+    StopCheckingProgress();
+    return;
+  }
+
+  web::NavigationManager* manager = web_state()->GetNavigationManager();
+  web::NavigationItem* item = manager->GetPendingItem();
+
+  // Manager->GetPendingItem() returns null on reload.
+  // TODO(crbug.com/676129): Remove this workaround once GetPendingItem()
+  // returns the correct value on reload.
+  if (!item) {
+    item = manager->GetLastCommittedItem();
+  }
+
+  if (!ShouldObserveItem(item)) {
+    StopCheckingProgress();
+    return;
+  }
+
+  pending_url_ = item->GetVirtualURL();
+  if (!IsUrlAvailableOffline(pending_url_)) {
+    // No need to launch the timer as there is no offline version to show.
+    // Track |pending_url_| to mark the entry as read in case of a successful
+    // load.
+    return;
+  }
   try_number_ = 0;
   timer_.reset(new base::Timer(false, true));
   const base::TimeDelta kDelayUntilLoadingProgressIsChecked =
@@ -115,29 +139,82 @@
           base::Unretained(this)));
 }
 
+void ReadingListWebStateObserver::PageLoaded(
+    web::PageLoadCompletionStatus load_completion_status) {
+  web::NavigationItem* item =
+      web_state()->GetNavigationManager()->GetLastCommittedItem();
+  if (!item || !pending_url_.is_valid()) {
+    StopCheckingProgress();
+    return;
+  }
+
+  if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS) {
+    reading_list_model_->SetReadStatus(pending_url_, true);
+  } else {
+    LoadOfflineReadingListEntry(item);
+  }
+  StopCheckingProgress();
+}
+
+void ReadingListWebStateObserver::WebStateDestroyed() {
+  StopCheckingProgress();
+  web_state()->RemoveUserData(kObserverKey);
+}
+
 void ReadingListWebStateObserver::StopCheckingProgress() {
-  pending_url_ = GURL();
+  pending_url_ = GURL::EmptyGURL();
   timer_.reset();
 }
 
 void ReadingListWebStateObserver::VerifyIfReadingListEntryStartedLoading() {
   if (!pending_url_.is_valid()) {
+    StopCheckingProgress();
     return;
   }
-  const ReadingListEntry* entry =
-      reading_list_model_->GetEntryByURL(pending_url_);
-  if (!entry || entry->DistilledState() != ReadingListEntry::PROCESSED) {
+  web::NavigationManager* manager = web_state()->GetNavigationManager();
+  web::NavigationItem* item = manager->GetPendingItem();
+
+  // Manager->GetPendingItem() returns null on reload.
+  // TODO(crbug.com/676129): Remove this workaround once GetPendingItem()
+  // returns the correct value on reload.
+  if (!item) {
+    item = manager->GetLastCommittedItem();
+  }
+  if (!item || !pending_url_.is_valid() ||
+      !IsUrlAvailableOffline(pending_url_)) {
+    StopCheckingProgress();
     return;
   }
   try_number_++;
   double progress = web_state()->GetLoadingProgress();
   const double kMinimumExpectedProgressPerStep = 0.25;
   if (progress < try_number_ * kMinimumExpectedProgressPerStep) {
-    reading_list::LoadReadingListDistilled(*entry, reading_list_model_,
-                                           web_state());
+    LoadOfflineReadingListEntry(item);
+    StopCheckingProgress();
+    return;
   }
   if (try_number_ >= 3) {
     // Loading reached 75%, let the page finish normal loading.
+    // Do not call |StopCheckingProgress()| as |pending_url_| is still
+    // needed to mark the entry read on success loading or to display
+    // offline version on error.
     timer_->Stop();
   }
 }
+
+void ReadingListWebStateObserver::LoadOfflineReadingListEntry(
+    web::NavigationItem* item) {
+  DCHECK(item);
+  if (!pending_url_.is_valid() || !IsUrlAvailableOffline(pending_url_)) {
+    return;
+  }
+  const ReadingListEntry* entry =
+      reading_list_model_->GetEntryByURL(pending_url_);
+  DCHECK(entry->DistilledState() == ReadingListEntry::PROCESSED);
+  GURL url =
+      reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL());
+  item->SetURL(url);
+  item->SetVirtualURL(pending_url_);
+  web_state()->GetNavigationManager()->Reload(false);
+  reading_list_model_->SetReadStatus(entry->URL(), true);
+}
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index be04f826..234647ad 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -46,6 +46,8 @@
     "//components/metrics_services_manager",
     "//components/navigation_metrics",
     "//components/prefs",
+    "//components/reading_list/core",
+    "//components/reading_list/ios",
     "//components/search_engines",
     "//components/sessions",
     "//components/signin/core/browser",
@@ -71,6 +73,7 @@
     "//ios/chrome/browser/net",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/passwords:passwords_internal",
+    "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/sessions:sessions_internal",
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 6c7d0c2..a0a17d9 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -41,6 +41,8 @@
 #include "components/navigation_metrics/navigation_metrics.h"
 #include "components/navigation_metrics/origins_seen_service.h"
 #include "components/prefs/pref_service.h"
+#include "components/reading_list/core/reading_list_switches.h"
+#include "components/reading_list/ios/reading_list_model.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/sessions/core/session_types.h"
 #include "components/sessions/ios/ios_serialized_navigation_builder.h"
@@ -74,6 +76,8 @@
 #import "ios/chrome/browser/passwords/password_controller.h"
 #import "ios/chrome/browser/passwords/passwords_ui_delegate_impl.h"
 #include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
+#include "ios/chrome/browser/reading_list/reading_list_web_state_observer.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
@@ -579,6 +583,12 @@
     InfoBarManagerImpl::CreateForWebState(self.webState);
     IOSSecurityStateTabHelper::CreateForWebState(self.webState);
 
+    if (reading_list::switches::IsReadingListEnabled()) {
+      ReadingListModel* model =
+          ReadingListModelFactory::GetForBrowserState(browserState_);
+      ReadingListWebStateObserver::FromWebState(self.webState, model);
+    }
+
     tabInfoBarObserver_.reset(new TabInfoBarObserver(self));
     tabInfoBarObserver_->SetShouldObserveInfoBarManager(true);
 
diff --git a/ios/chrome/browser/ui/actions/settings_actions.h b/ios/chrome/browser/ui/actions/settings_actions.h
index 2800571..15d9d9a 100644
--- a/ios/chrome/browser/ui/actions/settings_actions.h
+++ b/ios/chrome/browser/ui/actions/settings_actions.h
@@ -12,6 +12,8 @@
 #import <Foundation/Foundation.h>
 
 // Target/Action methods relating to the Settings UI.
+// (Actions should only be used to communicate into or between the View
+// Controller layer).
 @protocol SettingsActions
 @optional
 // Show the Settings UI over whatever UI is currently active.
diff --git a/ios/chrome/browser/ui/actions/tab_grid_actions.h b/ios/chrome/browser/ui/actions/tab_grid_actions.h
index 94ddd7e..72539f7 100644
--- a/ios/chrome/browser/ui/actions/tab_grid_actions.h
+++ b/ios/chrome/browser/ui/actions/tab_grid_actions.h
@@ -12,6 +12,8 @@
 #import <Foundation/Foundation.h>
 
 // Target/Action methods relating to the tab grid.
+// (Actions should only be used to communicate into or between the View
+// Controller layer).
 @protocol TabGridActions
 @optional
 // Dismisses whatever UI is currently active and shows the tab grid.
diff --git a/ios/chrome/browser/ui/actions/tools_menu_actions.h b/ios/chrome/browser/ui/actions/tools_menu_actions.h
index c903ad0..3d6d02b 100644
--- a/ios/chrome/browser/ui/actions/tools_menu_actions.h
+++ b/ios/chrome/browser/ui/actions/tools_menu_actions.h
@@ -12,6 +12,8 @@
 #import <Foundation/Foundation.h>
 
 // Target/Action methods relating to the Tools menu  UI.
+// (Actions should only be used to communicate into or between the View
+// Controller layer).
 @protocol ToolsMenuActions
 
 @optional
diff --git a/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm b/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm
index 922bdd37..09d7543e 100644
--- a/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm
+++ b/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm
@@ -302,9 +302,9 @@
       [[CollectionViewModel alloc] init]);
   [model addSectionWithIdentifier:SectionIdentifierCheese];
 
+  logging::SetLogAssertHandler(&LogSink);
   bool out_of_bounds_exception_thrown = false;
   @try {
-    logging::SetLogAssertHandler(&LogSink);
     [model indexInItemTypeForIndexPath:[NSIndexPath indexPathForItem:0
                                                            inSection:0]];
   } @catch (NSException* exception) {
@@ -313,6 +313,7 @@
     }
   }
   EXPECT_TRUE(out_of_bounds_exception_thrown);
+  logging::SetLogAssertHandler(nullptr);
 }
 
 TEST(CollectionViewModelTest, RemoveItems) {
diff --git a/ios/chrome/browser/ui/commands/BUILD.gn b/ios/chrome/browser/ui/commands/BUILD.gn
index ce2d052..e3a31ca 100644
--- a/ios/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/chrome/browser/ui/commands/BUILD.gn
@@ -46,3 +46,15 @@
     "//url",
   ]
 }
+
+# Clean Skeleton targets
+source_set("commands_clean_skeleton") {
+  sources = [
+    "settings_commands.h",
+    "tab_commands.h",
+    "tab_grid_commands.h",
+    "toolbar_commands.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/chrome/browser/ui/commands/settings_commands.h b/ios/chrome/browser/ui/commands/settings_commands.h
new file mode 100644
index 0000000..cb0d2bf
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/settings_commands.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_
+
+// ======                        New Architecture                         =====
+// =         This code is only used in the new iOS Chrome architecture.       =
+// ============================================================================
+
+// Command protocol for commands relating to the Settings UI.
+// (Commands are for communicating into or within the coordinator layer).
+@protocol SettingsCommands
+// Display the settings UI.
+- (void)showSettings;
+// Dismiss the settings UI.
+- (void)closeSettings;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/tab_commands.h b/ios/chrome/browser/ui/commands/tab_commands.h
new file mode 100644
index 0000000..d8086b47
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/tab_commands.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
+
+// ======                        New Architecture                         =====
+// =         This code is only used in the new iOS Chrome architecture.       =
+// ============================================================================
+
+// Command protocol for commands relating to tabs.
+// (Commands are for communicating into or within the coordinator layer).
+@protocol TabCommands
+// Display the tab corresponding to |indexPath|. The receiver determines how
+// this correspondence relates to the tab model(s) it knows about.
+- (void)showTabAtIndexPath:(NSIndexPath*)indexPath;
+@end
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/tab_grid_commands.h b/ios/chrome/browser/ui/commands/tab_grid_commands.h
new file mode 100644
index 0000000..2e8faad
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/tab_grid_commands.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_
+
+// ======                        New Architecture                         =====
+// =         This code is only used in the new iOS Chrome architecture.       =
+// ============================================================================
+
+// Command protocol for commands relating to the tab grid UI.
+// (Commands are for communicating into or within the coordinator layer).
+@protocol TabGridCommands
+// Display the tab grid.
+- (void)showTabGrid;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/toolbar_commands.h b/ios/chrome/browser/ui/commands/toolbar_commands.h
new file mode 100644
index 0000000..675c738
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/toolbar_commands.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_
+
+// ======                        New Architecture                         =====
+// =         This code is only used in the new iOS Chrome architecture.       =
+// ============================================================================
+
+// Command protocol for commands relating to the toolbar UI.
+// (Commands are for communicating into or within the coordinator layer).
+@protocol ToolbarCommands
+// Shows the tools menu.
+- (void)showToolsMenu;
+// Closes the tools menu.
+- (void)closeToolsMenu;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/fancy_ui/BUILD.gn b/ios/chrome/browser/ui/fancy_ui/BUILD.gn
index 7af8ee0e..2cab462 100644
--- a/ios/chrome/browser/ui/fancy_ui/BUILD.gn
+++ b/ios/chrome/browser/ui/fancy_ui/BUILD.gn
@@ -33,4 +33,5 @@
     "//base:i18n",
     "//testing/gtest",
   ]
+  configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm b/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm
index ecfb47f..7af825e 100644
--- a/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm
+++ b/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm
@@ -5,9 +5,12 @@
 #import "ios/chrome/browser/ui/fancy_ui/bidi_container_view.h"
 
 #include "base/i18n/rtl.h"
-#include "base/mac/scoped_nsobject.h"
 #include "testing/platform_test.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 class BidiContainerViewTest : public PlatformTest {
@@ -23,10 +26,9 @@
     const char* locale,
     UIViewAutoresizing autoresizing) {
   base::i18n::SetICUDefaultLocale(base::i18n::GetCanonicalLocale(locale));
-  base::scoped_nsobject<BidiContainerView> view(
-      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
-  base::scoped_nsobject<UILabel> label(
-      [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]);
+  BidiContainerView* view =
+      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+  UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)];
   [label setAutoresizingMask:autoresizing];
   [view addSubview:label];
   [view layoutSubviews];
@@ -36,10 +38,9 @@
 TEST_F(BidiContainerViewTest, InitializeLeftToRight) {
   base::i18n::SetICUDefaultLocale(
       base::i18n::GetCanonicalLocale("en" /* English */));
-  base::scoped_nsobject<BidiContainerView> view(
-      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
-  base::scoped_nsobject<UILabel> label(
-      [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]);
+  BidiContainerView* view =
+      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+  UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)];
   [view addSubview:label];
   [view layoutSubviews];
   CGRect labelFrame = [label frame];
@@ -52,10 +53,9 @@
 TEST_F(BidiContainerViewTest, InitializeRightToLeft) {
   base::i18n::SetICUDefaultLocale(
       base::i18n::GetCanonicalLocale("he" /* Hebrew */));
-  base::scoped_nsobject<BidiContainerView> view(
-      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
-  base::scoped_nsobject<UILabel> label(
-      [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]);
+  BidiContainerView* view =
+      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+  UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)];
   [view addSubview:label];
   [view layoutSubviews];
   CGRect labelFrame = [label frame];
@@ -69,13 +69,11 @@
 TEST_F(BidiContainerViewTest, InitializeRightToLeftTwoViews) {
   base::i18n::SetICUDefaultLocale(
       base::i18n::GetCanonicalLocale("he" /* Hebrew */));
-  base::scoped_nsobject<BidiContainerView> view(
-      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
-  base::scoped_nsobject<UILabel> label1(
-      [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]);
+  BidiContainerView* view =
+      [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+  UILabel* label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)];
   [view addSubview:label1];
-  base::scoped_nsobject<UILabel> label2(
-      [[UILabel alloc] initWithFrame:CGRectMake(60, 30, 30, 50)]);
+  UILabel* label2 = [[UILabel alloc] initWithFrame:CGRectMake(60, 30, 30, 50)];
   [view addSubview:label2];
   [view layoutSubviews];
   EXPECT_EQ(100 - 20 - 40 /* view.width - label1.originX - label1.width */,
diff --git a/ios/chrome/browser/ui/icons/BUILD.gn b/ios/chrome/browser/ui/icons/BUILD.gn
index 8114034..506c6b1d 100644
--- a/ios/chrome/browser/ui/icons/BUILD.gn
+++ b/ios/chrome/browser/ui/icons/BUILD.gn
@@ -2,34 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-bundle_data("assets") {
-  sources = [
-    "resources/Icons.xcassets/ic_arrow_back.imageset/Contents.json",
-    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back.png",
-    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_2x.png",
-    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_3x.png",
-    "resources/Icons.xcassets/ic_chevron_right.imageset/Contents.json",
-    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right.png",
-    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_2x.png",
-    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_3x.png",
-    "resources/Icons.xcassets/ic_close.imageset/Contents.json",
-    "resources/Icons.xcassets/ic_close.imageset/ic_close.png",
-    "resources/Icons.xcassets/ic_close.imageset/ic_close_2x.png",
-    "resources/Icons.xcassets/ic_close.imageset/ic_close_3x.png",
-    "resources/Icons.xcassets/ic_info.imageset/Contents.json",
-    "resources/Icons.xcassets/ic_info.imageset/ic_info.png",
-    "resources/Icons.xcassets/ic_info.imageset/ic_info_2x.png",
-    "resources/Icons.xcassets/ic_info.imageset/ic_info_3x.png",
-    "resources/Icons.xcassets/ic_search.imageset/Contents.json",
-    "resources/Icons.xcassets/ic_search.imageset/ic_search.png",
-    "resources/Icons.xcassets/ic_search.imageset/ic_search_2x.png",
-    "resources/Icons.xcassets/ic_search.imageset/ic_search_3x.png",
-  ]
-  outputs = [
-    "{{bundle_resources_dir}}/{{source_file_part}}",
-  ]
-}
-
 source_set("icons") {
   sources = [
     "chrome_icon.h",
@@ -59,4 +31,33 @@
     "//third_party/ocmock",
     "//ui/base",
   ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+bundle_data("assets") {
+  sources = [
+    "resources/Icons.xcassets/ic_arrow_back.imageset/Contents.json",
+    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back.png",
+    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_2x.png",
+    "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_3x.png",
+    "resources/Icons.xcassets/ic_chevron_right.imageset/Contents.json",
+    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right.png",
+    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_2x.png",
+    "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_3x.png",
+    "resources/Icons.xcassets/ic_close.imageset/Contents.json",
+    "resources/Icons.xcassets/ic_close.imageset/ic_close.png",
+    "resources/Icons.xcassets/ic_close.imageset/ic_close_2x.png",
+    "resources/Icons.xcassets/ic_close.imageset/ic_close_3x.png",
+    "resources/Icons.xcassets/ic_info.imageset/Contents.json",
+    "resources/Icons.xcassets/ic_info.imageset/ic_info.png",
+    "resources/Icons.xcassets/ic_info.imageset/ic_info_2x.png",
+    "resources/Icons.xcassets/ic_info.imageset/ic_info_3x.png",
+    "resources/Icons.xcassets/ic_search.imageset/Contents.json",
+    "resources/Icons.xcassets/ic_search.imageset/ic_search.png",
+    "resources/Icons.xcassets/ic_search.imageset/ic_search_2x.png",
+    "resources/Icons.xcassets/ic_search.imageset/ic_search_3x.png",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/{{source_file_part}}",
+  ]
 }
diff --git a/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm b/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm
index 84533010..1de818d 100644
--- a/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm
+++ b/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm
@@ -9,6 +9,10 @@
 #import "third_party/ocmock/ocmock_extensions.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @protocol MockTarget
 - (void)doSomething;
 @end
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
index 538e52e7..a03f7b7 100644
--- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
@@ -6,8 +6,9 @@
 
 #include <map>
 
+#include "base/bind.h"
 #include "base/metrics/field_trial.h"
-#include "base/test/user_action_tester.h"
+#include "base/metrics/user_metrics.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -28,12 +29,17 @@
  public:
   NotificationPromoWhatsNewTest()
       : promo_(&local_state_),
+        action_callback_(
+            base::Bind(&NotificationPromoWhatsNewTest::OnUserAction,
+                       base::Unretained(this))),
         field_trial_list_(new base::FieldTrialList(NULL)) {
     ios::NotificationPromo::RegisterPrefs(local_state_.registry());
     local_state_.registry()->RegisterInt64Pref(metrics::prefs::kInstallDate, 0);
+    base::AddActionCallback(action_callback_);
   }
 
   ~NotificationPromoWhatsNewTest() override {
+    base::RemoveActionCallback(action_callback_);
     variations::testing::ClearAllVariationParams();
   }
 
@@ -98,9 +104,19 @@
       EXPECT_EQ(icon, promo_.icon());
   }
 
+  void OnUserAction(const std::string& user_action) {
+    user_action_count_map_[user_action]++;
+  }
+
+  int GetUserActionCount(const std::string& user_action) {
+    return user_action_count_map_[user_action];
+  }
+
  protected:
   TestingPrefServiceSimple local_state_;
   NotificationPromoWhatsNew promo_;
+  base::ActionCallback action_callback_;
+  std::map<std::string, int> user_action_count_map_;
 
  private:
   std::unique_ptr<base::FieldTrialList> field_trial_list_;
@@ -203,20 +219,16 @@
        "IDS_IOS_APP_RATING_PROMO_STRING", "0", "chrome_command", "",
        "ratethisapp", "RateThisAppPromo", "logo", "0", "0");
 
-  base::UserActionTester user_action_tester;
   // Assert that promo is appropriately set up to be viewed.
   ASSERT_TRUE(promo_.CanShow());
   promo_.HandleViewed();
-  EXPECT_EQ(1, user_action_tester.GetActionCount(
-                   "WhatsNewPromoViewed_RateThisAppPromo"));
+  EXPECT_EQ(1, GetUserActionCount("WhatsNewPromoViewed_RateThisAppPromo"));
 
   // Verify that the promo closed user action count is 0 before |HandleClosed()|
   // is called.
-  EXPECT_EQ(0, user_action_tester.GetActionCount(
-                   "WhatsNewPromoClosed_RateThisAppPromo"));
+  EXPECT_EQ(0, GetUserActionCount("WhatsNewPromoClosed_RateThisAppPromo"));
   promo_.HandleClosed();
-  EXPECT_EQ(1, user_action_tester.GetActionCount(
-                   "WhatsNewPromoClosed_RateThisAppPromo"));
+  EXPECT_EQ(1, GetUserActionCount("WhatsNewPromoClosed_RateThisAppPromo"));
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
index 90454c1..7d1945ebd9 100644
--- a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
+++ b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
@@ -10,18 +10,23 @@
 #include "components/reading_list/ios/reading_list_model.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/reading_list/offline_url_utils.h"
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
+#include "ios/chrome/browser/reading_list/reading_list_download_service.h"
+#include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h"
 #include "ios/web/public/browser_state.h"
-#include "net/base/network_change_notifier.h"
+#import "ios/web/public/navigation_item.h"
+#import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-#pragma mark -
-#pragma mark Public
+@interface OfflinePageNativeContent ()
+// Restores the last committed item to its initial state.
+- (void)restoreOnlineURL;
+@end
 
 @implementation OfflinePageNativeContent {
   // The virtual URL that will be displayed to the user.
@@ -42,14 +47,18 @@
   DCHECK(browserState);
   DCHECK(URL.is_valid());
 
-  if (reading_list::switches::IsReadingListEnabled()) {
-    _model = ReadingListModelFactory::GetForBrowserState(
-        ios::ChromeBrowserState::FromBrowserState(browserState));
-  }
+  DCHECK(reading_list::switches::IsReadingListEnabled());
+  _model = ReadingListModelFactory::GetForBrowserState(
+      ios::ChromeBrowserState::FromBrowserState(browserState));
+  base::FilePath offline_root =
+      ReadingListDownloadServiceFactory::GetForBrowserState(
+          ios::ChromeBrowserState::FromBrowserState(browserState))
+          ->OfflineRoot();
+
   _webState = webState;
   GURL resourcesRoot;
-  GURL fileURL = reading_list::FileURLForDistilledURL(
-      URL, browserState->GetStatePath(), &resourcesRoot);
+  GURL fileURL =
+      reading_list::FileURLForDistilledURL(URL, offline_root, &resourcesRoot);
 
   StaticHtmlViewController* HTMLViewController =
       [[StaticHtmlViewController alloc] initWithFileURL:fileURL
@@ -62,21 +71,29 @@
                            URL:URL];
 }
 
+- (void)willBeDismissed {
+  [self restoreOnlineURL];
+}
+
+- (void)close {
+  [self restoreOnlineURL];
+  [super close];
+}
+
 - (GURL)virtualURL {
   return _virtualURL;
 }
 
 - (void)reload {
-  if (!_model || net::NetworkChangeNotifier::IsOffline()) {
-    [super reload];
-    return;
-  }
-  const ReadingListEntry* entry = _model->GetEntryByURL([self virtualURL]);
-  if (entry) {
-    reading_list::LoadReadingListEntry(*entry, _model, _webState);
-  } else {
-    [super reload];
-  }
+  [self restoreOnlineURL];
+  _webState->GetNavigationManager()->Reload(false);
+}
+
+- (void)restoreOnlineURL {
+  web::NavigationItem* item =
+      _webState->GetNavigationManager()->GetLastCommittedItem();
+  item->SetURL([self virtualURL]);
+  item->SetVirtualURL([self virtualURL]);
 }
 
 @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
index f3273a7..7000f2a 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
@@ -7,7 +7,6 @@
 #include "base/logging.h"
 #include "components/reading_list/ios/reading_list_entry.h"
 #include "components/reading_list/ios/reading_list_model.h"
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
 #include "ios/web/public/web_state/web_state.h"
 #include "url/gurl.h"
 
@@ -56,18 +55,20 @@
   if (!webState || _readingListModel->unread_size() == 0) {
     return;
   }
-  int64_t updatetime = 0;
-  const ReadingListEntry* first_entry = nullptr;
+  int64_t updateTime = 0;
+  const ReadingListEntry* firstEntry = nullptr;
   for (const auto& url : _readingListModel->Keys()) {
     const ReadingListEntry* entry = _readingListModel->GetEntryByURL(url);
-    if (!entry->IsRead() && entry->UpdateTime() > updatetime) {
-      updatetime = entry->UpdateTime();
-      first_entry = entry;
+    if (!entry->IsRead() && entry->UpdateTime() > updateTime) {
+      updateTime = entry->UpdateTime();
+      firstEntry = entry;
     }
   }
-  DCHECK_GT(updatetime, 0);
-  DCHECK_NE(first_entry, nullptr);
-  reading_list::LoadReadingListEntry(*first_entry, _readingListModel, webState);
+  DCHECK_GT(updateTime, 0);
+  DCHECK(firstEntry);
+  web::NavigationManager::WebLoadParams params(firstEntry->URL());
+  params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
+  webState->GetNavigationManager()->LoadURLWithParams(params);
 }
 
 @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
index 7ee1123..e4d4b79 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
@@ -19,7 +19,6 @@
 #include "components/url_formatter/url_formatter.h"
 #include "ios/chrome/browser/reading_list/offline_url_utils.h"
 #include "ios/chrome/browser/reading_list/reading_list_download_service.h"
-#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h"
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
@@ -423,8 +422,9 @@
 
   Tab* currentTab = _tabModel.currentTab;
   DCHECK(currentTab);
-  reading_list::LoadReadingListEntry(*entry, self.readingListModel,
-                                     currentTab.webState);
+  web::NavigationManager::WebLoadParams params(entry->URL());
+  params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
+  [currentTab webState]->GetNavigationManager()->LoadURLWithParams(params);
   [self dismiss];
 }
 
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index e358659..7fdc973 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -196,10 +196,13 @@
   sources = [
     "passphrase_collection_view_controller_test.h",
     "passphrase_collection_view_controller_test.mm",
+    "personal_data_manager_data_changed_observer.cc",
+    "personal_data_manager_data_changed_observer.h",
   ]
   deps = [
     ":settings",
     "//base",
+    "//components/autofill/core/browser",
     "//components/browser_sync",
     "//components/browser_sync:test_support",
     "//components/keyed_service/core",
@@ -379,5 +382,6 @@
     ":settings",
     "//ios/chrome/browser:browser_clean_skeleton",
     "//ios/chrome/browser/ui/actions",
+    "//ios/chrome/browser/ui/commands:commands_clean_skeleton",
   ]
 }
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
index 9cfbadc3..41ae1ea 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
@@ -14,6 +14,7 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
+#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -26,8 +27,7 @@
 class AutofillCollectionViewControllerTest
     : public CollectionViewControllerTest {
  protected:
-  void SetUp() override {
-    CollectionViewControllerTest::SetUp();
+  AutofillCollectionViewControllerTest() {
     TestChromeBrowserState::Builder test_cbs_builder;
     chrome_browser_state_ = test_cbs_builder.Build();
     // Profile import requires a PersonalDataManager which itself needs the
@@ -44,14 +44,17 @@
   void AddProfile(const std::string& origin,
                   const std::string& name,
                   const std::string& address) {
-    autofill::PersonalDataManager* personalDataManager =
+    autofill::PersonalDataManager* personal_data_manager =
         autofill::PersonalDataManagerFactory::GetForBrowserState(
             chrome_browser_state_.get());
-    autofill::AutofillProfile autofillProfile(base::GenerateGUID(), origin);
-    autofillProfile.SetRawInfo(autofill::NAME_FULL, base::ASCIIToUTF16(name));
-    autofillProfile.SetRawInfo(autofill::ADDRESS_HOME_LINE1,
-                               base::ASCIIToUTF16(address));
-    personalDataManager->SaveImportedProfile(autofillProfile);
+    PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+
+    autofill::AutofillProfile autofill_profile(base::GenerateGUID(), origin);
+    autofill_profile.SetRawInfo(autofill::NAME_FULL, base::ASCIIToUTF16(name));
+    autofill_profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1,
+                                base::ASCIIToUTF16(address));
+    personal_data_manager->SaveImportedProfile(autofill_profile);
+    observer.Wait();  // Wait for completion of the asynchronous operation.
   }
 
   web::TestWebThreadBundle thread_bundle_;
@@ -83,16 +86,20 @@
 
 // Adding a single credit card results in a credit card section.
 TEST_F(AutofillCollectionViewControllerTest, TestOneCreditCard) {
-  autofill::PersonalDataManager* personalDataManager =
+  autofill::PersonalDataManager* personal_data_manager =
       autofill::PersonalDataManagerFactory::GetForBrowserState(
           chrome_browser_state_.get());
-  autofill::CreditCard creditCard(base::GenerateGUID(),
-                                  "https://www.example.com/");
-  creditCard.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
-                        base::ASCIIToUTF16("Alan Smithee"));
-  creditCard.SetRawInfo(autofill::CREDIT_CARD_NUMBER,
-                        base::ASCIIToUTF16("378282246310005"));
-  personalDataManager->SaveImportedCreditCard(creditCard);
+  PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+
+  autofill::CreditCard credit_card(base::GenerateGUID(),
+                                   "https://www.example.com/");
+  credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
+                         base::ASCIIToUTF16("Alan Smithee"));
+  credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER,
+                         base::ASCIIToUTF16("378282246310005"));
+  personal_data_manager->SaveImportedCreditCard(credit_card);
+  observer.Wait();  // Wait for completion of the asynchronous operation.
+
   CreateController();
   // Expect two sections (header and credit card section).
   EXPECT_EQ(2, NumberOfSections());
@@ -117,6 +124,7 @@
       base::mac::ObjCCastStrict<AutofillCollectionViewController>(controller());
   // Put the collectionView in 'edit' mode.
   [view_controller editButtonPressed];
+
   // This is a bit of a shortcut, since actually clicking on the 'delete'
   // button would be tough.
   void (^delete_item_with_wait)(int, int) = ^(int i, int j) {
@@ -129,7 +137,16 @@
     });
   };
 
+  autofill::PersonalDataManager* personal_data_manager =
+      autofill::PersonalDataManagerFactory::GetForBrowserState(
+          chrome_browser_state_.get());
+  PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+
+  // This call cause a modification of the PersonalDataManager, so wait until
+  // the asynchronous task complete in addition to waiting for the UI update.
   delete_item_with_wait(1, 0);
+  observer.Wait();  // Wait for completion of the asynchronous operation.
+
   // Exit 'edit' mode.
   [view_controller editButtonPressed];
 
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm
index 4141ad4..fedc642 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm
@@ -17,6 +17,7 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
+#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/platform_test.h"
 
@@ -55,6 +56,7 @@
     personal_data_manager_ =
         autofill::PersonalDataManagerFactory::GetForBrowserState(
             chrome_browser_state_.get());
+    PersonalDataManagerDataChangedObserver observer(personal_data_manager_);
 
     std::string guid = base::GenerateGUID();
 
@@ -67,6 +69,7 @@
                                 base::UTF8ToUTF16(kTestAddressLine1));
 
     personal_data_manager_->SaveImportedProfile(autofill_profile);
+    observer.Wait();  // Wait for the completion of the asynchronous operation.
 
     autofill_profile_edit_controller_.reset(
         [[AutofillProfileEditCollectionViewController
diff --git a/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc
new file mode 100644
index 0000000..05253e236
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h"
+
+#include "components/autofill/core/browser/personal_data_manager.h"
+
+PersonalDataManagerDataChangedObserver::PersonalDataManagerDataChangedObserver(
+    autofill::PersonalDataManager* personal_data_manager)
+    : personal_data_manager_(personal_data_manager) {
+  personal_data_manager_->AddObserver(this);
+}
+
+PersonalDataManagerDataChangedObserver::
+    ~PersonalDataManagerDataChangedObserver() {
+  personal_data_manager_->RemoveObserver(this);
+}
+
+void PersonalDataManagerDataChangedObserver::Wait() {
+  // If a test is blocked in that method, it means that OnPersonalDataChanged()
+  // was never called which indicates a bug in either the expectation of the
+  // test (no asynchronous operation is executed on the PersonalDataManager
+  // passed in the constructor) or in the utilisation of the
+  // PersonalDataManagerDataChangedObserver (there is a race-condition between
+  // sending the asynchronous operation and the creation of the observer, it can
+  // be fixed by creating the observer before sending the operation).
+  run_loop_.Run();
+}
+
+void PersonalDataManagerDataChangedObserver::OnPersonalDataChanged() {
+  run_loop_.QuitWhenIdle();
+}
diff --git a/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h
new file mode 100644
index 0000000..5b4ae40
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+
+namespace autofill {
+class PersonalDataManager;
+}
+
+// Helper class to allow waiting until the asynchronous operation on the
+// autofill::PersonalDataManager completed. Need to be used like this:
+//
+//   autofill::PersonalDataManager* personal_data_manager = ...;
+//   PersonalDataManagerDataChangedObserver observer(personal_data_manager);
+//   personal_data_manager->...();  // Starts some asynchronous operation.
+//   observer.Wait();
+//
+class PersonalDataManagerDataChangedObserver
+    : public autofill::PersonalDataManagerObserver {
+ public:
+  PersonalDataManagerDataChangedObserver(
+      autofill::PersonalDataManager* personal_data_manager);
+  ~PersonalDataManagerDataChangedObserver() override;
+
+  // Blocks until |OnPersonalDataChanged| is invoked at the end of the
+  // asynchronous modification on the PersonalDataManager.
+  void Wait();
+
+  // autofill::PersonalDataManagerObserver implementation.
+  void OnPersonalDataChanged() override;
+
+ private:
+  autofill::PersonalDataManager* personal_data_manager_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerDataChangedObserver);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_
diff --git a/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm
index 445bcb6..b54ad4e 100644
--- a/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm
@@ -24,6 +24,7 @@
 #import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
+#include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_capabilities.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -76,6 +77,7 @@
   }
 
   web::TestWebThreadBundle thread_bundle_;
+  IOSChromeScopedTestingLocalState local_state_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   base::scoped_nsobject<NSString> initialValueForSpdyProxyEnabled_;
 };
diff --git a/ios/chrome/browser/ui/settings/settings_coordinator.h b/ios/chrome/browser/ui/settings/settings_coordinator.h
index e3a0089..6ff3a86 100644
--- a/ios/chrome/browser/ui/settings/settings_coordinator.h
+++ b/ios/chrome/browser/ui/settings/settings_coordinator.h
@@ -13,18 +13,13 @@
 
 #import "ios/chrome/browser/browser_coordinator.h"
 
-// Action delegate protocol for coordinators to handle changes to the
-// Settings UI.
-@protocol SettingsActionDelegate<NSObject>
-// Close the settings UI.
-- (void)closeSettings;
-@end
+@protocol SettingsCommands;
 
 // A coordinator for the Settings UI, which is usually presented modally
 // on top of whatever other UI is currently active.
 @interface SettingsCoordinator : BrowserCoordinator
 // Action delegate for this coordinator.
-@property(nonatomic, weak) id<SettingsActionDelegate> actionDelegate;
+@property(nonatomic, weak) id<SettingsCommands> settingsCommandHandler;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/settings_coordinator.mm b/ios/chrome/browser/ui/settings/settings_coordinator.mm
index 87e5d2a..87dcd87 100644
--- a/ios/chrome/browser/ui/settings/settings_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/settings_coordinator.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/settings/settings_coordinator.h"
 
 #import "ios/chrome/browser/browser_coordinator+internal.h"
+#import "ios/chrome/browser/ui/commands/settings_commands.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,7 +21,7 @@
 @end
 
 @implementation SettingsCoordinator
-@synthesize actionDelegate = _actionDelegate;
+@synthesize settingsCommandHandler = _settingsCommandHandler;
 @synthesize viewController = _viewController;
 
 #pragma mark - BrowserCoordinator
@@ -54,7 +55,7 @@
 }
 
 - (void)closeSettings {
-  [self.actionDelegate closeSettings];
+  [self.settingsCommandHandler closeSettings];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 3d4a3fba..04b7f2b 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -122,8 +122,7 @@
     historySideSwipeProvider_.reset(
         [[HistorySideSwipeProvider alloc] initWithTabModel:model_]);
 
-    if (!browserState->IsOffTheRecord() &&
-        reading_list::switches::IsReadingListEnabled()) {
+    if (reading_list::switches::IsReadingListEnabled()) {
       readingListSideSwipeProvider_.reset([[ReadingListSideSwipeProvider alloc]
           initWithReadingList:ReadingListModelFactory::GetForBrowserState(
                                   browserState)]);
diff --git a/ios/chrome/browser/ui/tab/tab_container_view_controller.mm b/ios/chrome/browser/ui/tab/tab_container_view_controller.mm
index 7a54d19..3f7c173 100644
--- a/ios/chrome/browser/ui/tab/tab_container_view_controller.mm
+++ b/ios/chrome/browser/ui/tab/tab_container_view_controller.mm
@@ -41,7 +41,7 @@
 
 // Called after a new toolbar view controller is set, but before
 // |-didMoveToParentViewController:| is called on that view controller.
-- (void)didAddContentViewController;
+- (void)didAddToolbarViewController;
 
 // Methods to populate the constraint properties.
 - (void)updateContentConstraintsWithToolbar;
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index e38eb26e..838c6d6b 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -15,6 +15,7 @@
     "//base",
     "//ios/chrome/browser:browser_clean_skeleton",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/commands:commands_clean_skeleton",
     "//ios/chrome/browser/ui/settings:settings_clean_skeleton",
     "//ios/chrome/browser/ui/tab",
     "//ios/web",
@@ -37,5 +38,6 @@
     "//base",
     "//ios/chrome/browser/ui/actions",
     "//ios/chrome/browser/ui/animators",
+    "//ios/chrome/browser/ui/commands:commands_clean_skeleton",
   ]
 }
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index 5a826f7c..fa34e39 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -13,6 +13,9 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/browser_coordinator+internal.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/commands/settings_commands.h"
+#import "ios/chrome/browser/ui/commands/tab_commands.h"
+#import "ios/chrome/browser/ui/commands/tab_grid_commands.h"
 #import "ios/chrome/browser/ui/settings/settings_coordinator.h"
 #import "ios/chrome/browser/ui/tab/tab_coordinator.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
@@ -26,8 +29,9 @@
 #endif
 
 @interface TabGridCoordinator ()<TabGridDataSource,
-                                 TabGridActionDelegate,
-                                 SettingsActionDelegate>
+                                 SettingsCommands,
+                                 TabCommands,
+                                 TabGridCommands>
 @property(nonatomic, strong) TabGridViewController* viewController;
 @property(nonatomic, weak) SettingsCoordinator* settingsCoordinator;
 @end
@@ -44,7 +48,9 @@
 - (void)start {
   self.viewController = [[TabGridViewController alloc] init];
   self.viewController.dataSource = self;
-  self.viewController.actionDelegate = self;
+  self.viewController.settingsCommandHandler = self;
+  self.viewController.tabCommandHandler = self;
+  self.viewController.tabGridCommandHandler = self;
 
   // |rootViewController| is nullable, so this is by design a no-op if it hasn't
   // been set. This may be true in a unit test, or if this coordinator is being
@@ -76,7 +82,7 @@
   return urlText;
 }
 
-#pragma mark - TabGridActionDelegate
+#pragma mark - TabCommands
 
 - (void)showTabAtIndexPath:(NSIndexPath*)indexPath {
   DCHECK(_placeholderWebState);
@@ -88,6 +94,8 @@
   [tabCoordinator start];
 }
 
+#pragma mark - TabGridCommands
+
 - (void)showTabGrid {
   // This object should only ever have at most one child.
   DCHECK_LE(self.children.count, 1UL);
@@ -96,18 +104,16 @@
   [self removeChildCoordinator:child];
 }
 
-#pragma mark - TabGridActionDelegate
+#pragma mark - SettingsCommands
 
 - (void)showSettings {
   SettingsCoordinator* settingsCoordinator = [[SettingsCoordinator alloc] init];
-  settingsCoordinator.actionDelegate = self;
+  settingsCoordinator.settingsCommandHandler = self;
   [self addOverlayCoordinator:settingsCoordinator];
   self.settingsCoordinator = settingsCoordinator;
   [settingsCoordinator start];
 }
 
-#pragma mark - SettingsActionDelegate
-
 - (void)closeSettings {
   [self.settingsCoordinator stop];
   [self.settingsCoordinator.parentCoordinator
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h
index a1043310..dddf35b 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h
@@ -13,6 +13,10 @@
 
 #import "ios/chrome/browser/ui/animators/zoom_transition_delegate.h"
 
+@protocol SettingsCommands;
+@protocol TabCommands;
+@protocol TabGridCommands;
+
 // The data source for tab grid UI.
 // Conceptually the tab grid represents a group of WebState objects (which
 // are ultimately the model-layer representation of a browser tab). The data
@@ -27,20 +31,16 @@
 
 @end
 
-@protocol TabGridActionDelegate<NSObject>
-- (void)showTabAtIndexPath:(NSIndexPath*)indexPath;
-- (void)showTabGrid;
-- (void)showSettings;
-@end
-
 // Controller for a scrolling view displaying square cells that represent
 // the user's open tabs.
 @interface TabGridViewController : UIViewController<ZoomTransitionDelegate>
 
 // Data source for the tabs to be displayed.
 @property(nonatomic, weak) id<TabGridDataSource> dataSource;
-@property(nonatomic, weak) id<TabGridActionDelegate> actionDelegate;
-
+// Command handlers.
+@property(nonatomic, weak) id<SettingsCommands> settingsCommandHandler;
+@property(nonatomic, weak) id<TabCommands> tabCommandHandler;
+@property(nonatomic, weak) id<TabGridCommands> tabGridCommandHandler;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_TAB_GRID_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 69684bc7..202dff0 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -12,6 +12,9 @@
 #import "ios/chrome/browser/ui/actions/settings_actions.h"
 #import "ios/chrome/browser/ui/actions/tab_grid_actions.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_tab_cell.h"
+#import "ios/chrome/browser/ui/commands/settings_commands.h"
+#import "ios/chrome/browser/ui/commands/tab_commands.h"
+#import "ios/chrome/browser/ui/commands/tab_grid_commands.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -33,7 +36,9 @@
 @implementation TabGridViewController
 
 @synthesize dataSource = _dataSource;
-@synthesize actionDelegate = _actionDelegate;
+@synthesize settingsCommandHandler = _settingsCommandHandler;
+@synthesize tabGridCommandHandler = _tabGridCommandHandler;
+@synthesize tabCommandHandler = _tabCommandHandler;
 @synthesize grid = _grid;
 
 - (void)viewDidLoad {
@@ -122,7 +127,7 @@
 
 - (void)collectionView:(UICollectionView*)collectionView
     didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
-  [self.actionDelegate showTabAtIndexPath:indexPath];
+  [self.tabCommandHandler showTabAtIndexPath:indexPath];
 }
 
 #pragma mark - ZoomTransitionDelegate methods
@@ -138,13 +143,13 @@
 #pragma mark - SettingsActions
 
 - (void)showSettings:(id)sender {
-  [self.actionDelegate showSettings];
+  [self.settingsCommandHandler showSettings];
 }
 
 #pragma mark - TabGridActions
 
 - (void)showTabGrid:(id)sender {
-  [self.actionDelegate showTabGrid];
+  [self.tabGridCommandHandler showTabGrid];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 360b1db6..98e3916 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -219,6 +219,7 @@
     "//ios/chrome/browser:browser_clean_skeleton",
     "//ios/chrome/browser/ui/actions",
     "//ios/chrome/browser/ui/animators",
+    "//ios/chrome/browser/ui/commands:commands_clean_skeleton",
     "//ios/chrome/browser/ui/tools",
     "//ios/web",
   ]
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index a172a84..b684843 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -10,6 +10,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/browser_coordinator+internal.h"
+#import "ios/chrome/browser/ui/commands/toolbar_commands.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_view_controller.h"
 #import "ios/chrome/browser/ui/tools/tools_coordinator.h"
 #include "ios/web/public/web_state/web_state.h"
@@ -18,7 +19,7 @@
 #error "This file requires ARC support."
 #endif
 
-@interface ToolbarCoordinator ()<ToolbarActionDelegate>
+@interface ToolbarCoordinator ()<ToolbarCommands>
 @property(nonatomic, weak) ToolsCoordinator* toolsMenuCoordinator;
 @property(nonatomic, strong) ToolbarViewController* viewController;
 @end
@@ -29,7 +30,7 @@
 
 - (void)start {
   self.viewController = [[ToolbarViewController alloc] init];
-  self.viewController.actionDelegate = self;
+  self.viewController.toolbarCommandHandler = self;
 
   [self.rootViewController presentViewController:self.viewController
                                         animated:YES
@@ -44,7 +45,7 @@
       setCurrentPageText:base::SysUTF8ToNSString(pageURL.spec())];
 }
 
-#pragma mark - ToolbarActionDelegate
+#pragma mark - ToolbarCommands
 
 - (void)showToolsMenu {
   ToolsCoordinator* toolsCoordinator = [[ToolsCoordinator alloc] init];
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h
index 4a181299..db348c7 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h
+++ b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h
@@ -13,16 +13,7 @@
 
 #import "ios/chrome/browser/ui/animators/zoom_transition_delegate.h"
 
-// Action delegate for coordinators that handle the toolbar UI.
-@protocol ToolbarActionDelegate<NSObject>
-
-// Shows the tools menu.
-- (void)showToolsMenu;
-
-// Closes the tools menu.
-- (void)closeToolsMenu;
-
-@end
+@protocol ToolbarCommands;
 
 // View controller for a toolbar, which will show a horizontal row of
 // controls and/or labels.
@@ -31,8 +22,8 @@
 // height for it.
 @interface ToolbarViewController : UIViewController<ZoomTransitionDelegate>
 
-// The action delegate for this view controller.:wq
-@property(nonatomic, weak) id<ToolbarActionDelegate> actionDelegate;
+// The action delegate for this view controller.
+@property(nonatomic, weak) id<ToolbarCommands> toolbarCommandHandler;
 
 // Sets the text for a label appearing in the center of the toolbar.
 - (void)setCurrentPageText:(NSString*)text;
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm
index 0c7cecc9..af7990e 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm
@@ -10,6 +10,7 @@
 
 #import "ios/chrome/browser/ui/actions/tab_grid_actions.h"
 #import "ios/chrome/browser/ui/actions/tools_menu_actions.h"
+#import "ios/chrome/browser/ui/commands/toolbar_commands.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -21,7 +22,7 @@
 @end
 
 @implementation ToolbarViewController
-@synthesize actionDelegate = _actionDelegate;
+@synthesize toolbarCommandHandler = _toolbarCommandHandler;
 @synthesize omnibox = _omnibox;
 @synthesize toolsMenu = _toolsMenu;
 
@@ -86,11 +87,11 @@
 #pragma mark - ToolsMenuActions
 
 - (void)showToolsMenu:(id)sender {
-  [self.actionDelegate showToolsMenu];
+  [self.toolbarCommandHandler showToolsMenu];
 }
 
 - (void)closeToolsMenu:(id)sender {
-  [self.actionDelegate closeToolsMenu];
+  [self.toolbarCommandHandler closeToolsMenu];
 }
 
 @end
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 77b7009..8f457ab 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -33,6 +33,7 @@
 
   deps = [
     "//base",
+    "//base/test:test_support",
     "//components/content_settings/core/common",
     "//components/network_time",
     "//components/prefs",
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 9c01844..c32d84b 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -36,10 +36,6 @@
 // Returns matcher for webview not containing |text|.
 id<GREYMatcher> webViewNotContainingText(std::string text);
 
-// Matcher for WKWebView containing a blocked |image_id|.  When blocked, the
-// image element will be smaller than actual image.
-id<GREYMatcher> webViewContainingBlockedImage(std::string image_id);
-
 // Returns matcher for a StaticHtmlViewController containing |text|.
 id<GREYMatcher> staticHtmlViewContainingText(NSString* text);
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index eea05e7b..02ddd256 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -126,11 +126,6 @@
   return web::webViewNotContainingText(std::move(text), GetCurrentWebState());
 }
 
-id<GREYMatcher> webViewContainingBlockedImage(std::string image_id) {
-  return web::webViewContainingBlockedImage(std::move(image_id),
-                                            GetCurrentWebState());
-}
-
 id<GREYMatcher> staticHtmlViewContainingText(NSString* text) {
   // The WKWebView in a static HTML view isn't part of a webState, but it
   // does have the StaticHtmlViewController as its navigation delegate.
diff --git a/ios/chrome/test/ios_chrome_unit_test_suite.h b/ios/chrome/test/ios_chrome_unit_test_suite.h
index 86e8314d..faf545d 100644
--- a/ios/chrome/test/ios_chrome_unit_test_suite.h
+++ b/ios/chrome/test/ios_chrome_unit_test_suite.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/single_thread_task_runner.h"
 #include "ios/web/public/test/web_test_suite.h"
 
 // Test suite for unit tests.
@@ -19,6 +20,8 @@
   void Initialize() override;
 
  private:
+  scoped_refptr<base::SingleThreadTaskRunner> action_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(IOSChromeUnitTestSuite);
 };
 
diff --git a/ios/chrome/test/ios_chrome_unit_test_suite.mm b/ios/chrome/test/ios_chrome_unit_test_suite.mm
index 43b6b2b2b..0797c7d 100644
--- a/ios/chrome/test/ios_chrome_unit_test_suite.mm
+++ b/ios/chrome/test/ios_chrome_unit_test_suite.mm
@@ -5,7 +5,9 @@
 #include "ios/chrome/test/ios_chrome_unit_test_suite.h"
 
 #include "base/macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/path_service.h"
+#include "base/test/test_simple_task_runner.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
 #include "ios/chrome/browser/chrome_paths.h"
@@ -55,7 +57,8 @@
 }  // namespace
 
 IOSChromeUnitTestSuite::IOSChromeUnitTestSuite(int argc, char** argv)
-    : web::WebTestSuite(argc, argv) {}
+    : web::WebTestSuite(argc, argv),
+      action_task_runner_(new base::TestSimpleTaskRunner) {}
 
 IOSChromeUnitTestSuite::~IOSChromeUnitTestSuite() {}
 
@@ -74,6 +77,10 @@
   // test is run so that the dependencies are correctly resolved.
   EnsureBrowserStateKeyedServiceFactoriesBuilt();
 
+  // Register a SingleThreadTaskRunner for base::RecordAction as overridding
+  // it in individual tests is unsafe (as there is no way to unregister).
+  base::SetRecordActionTaskRunner(action_task_runner_);
+
   ios::RegisterPathProvider();
   ui::RegisterPathProvider();
   url::AddStandardScheme(kChromeUIScheme, url::SCHEME_WITHOUT_PORT);
diff --git a/ios/showcase/common/BUILD.gn b/ios/showcase/common/BUILD.gn
index 3a469136..8a5dcd14 100644
--- a/ios/showcase/common/BUILD.gn
+++ b/ios/showcase/common/BUILD.gn
@@ -5,7 +5,12 @@
 source_set("common") {
   sources = [
     "coordinator.h",
+    "protocol_alerter.h",
+    "protocol_alerter.mm",
   ]
-  libs = [ "Foundation.framework" ]
+  deps = [
+    "//base",
+  ]
+  libs = [ "UIKit.framework" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/showcase/common/protocol_alerter.h b/ios/showcase/common/protocol_alerter.h
new file mode 100644
index 0000000..02ad7645
--- /dev/null
+++ b/ios/showcase/common/protocol_alerter.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_
+#define IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_
+
+#import <UIKit/UIKit.h>
+
+// A protocol alerter is a stub object for testing UI components. It can
+// be initialized to conform to one or more protocols. When a protocol method
+// is called on it, it will log the call to the console. If the alerter has
+// has |baseViewController| set, it will display a UIAlert instead of logging.
+@interface ProtocolAlerter : NSProxy
+
+// Create a new alerter that responds to all of the selectors in all of the
+// protocols in |protocols|.
+- (instancetype)initWithProtocols:(NSArray<Protocol*>*)protocols;
+
+// The view controller (if any)_ that will be used to present alerts.
+@property(nonatomic, weak) UIViewController* baseViewController;
+
+@end
+
+#endif  // IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_
diff --git a/ios/showcase/common/protocol_alerter.mm b/ios/showcase/common/protocol_alerter.mm
new file mode 100644
index 0000000..888f957
--- /dev/null
+++ b/ios/showcase/common/protocol_alerter.mm
@@ -0,0 +1,201 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/showcase/common/protocol_alerter.h"
+
+#import <objc/runtime.h>
+
+#import "base/logging.h"
+#import "base/strings/sys_string_conversions.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Opaque value to use as an associated object key.
+char kAssociatedProtocolNameKey;
+}
+
+@interface NSInvocation (Description)
+// Returns a string description of the invocation consisting of the selector
+// name interspersed with argument values.
+- (NSString*)crsc_description;
+@end
+
+@interface ProtocolAlerter () {
+  NSSet<Protocol*>* _protocols;
+}
+@end
+
+@implementation ProtocolAlerter
+
+@synthesize baseViewController = _baseViewController;
+
+- (instancetype)initWithProtocols:(NSArray<Protocol*>*)protocols {
+  if (!protocols)
+    return nil;
+  // NSProxy isn't a subclass of NSObject, and has no superclass, so
+  // there's no [super init] to call.
+  _protocols = [[NSSet<Protocol*> alloc] initWithArray:protocols];
+  return self;
+}
+
+#pragma mark - NSProxy
+
+- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
+  for (Protocol* protocol in _protocols) {
+    for (NSNumber* required in @[ @(YES), @(NO) ]) {
+      struct objc_method_description method =
+          protocol_getMethodDescription(protocol,
+                                        sel,
+                                        required.boolValue,
+                                        YES /* an instance method */);
+      if (method.name != NULL) {
+        NSMethodSignature* signature =
+            [NSMethodSignature signatureWithObjCTypes:method.types];
+        // Tag the method signature with the protocol name.
+        objc_setAssociatedObject(signature,
+                                 &kAssociatedProtocolNameKey,
+                                 NSStringFromProtocol(protocol),
+                                 OBJC_ASSOCIATION_COPY_NONATOMIC);
+        return signature;
+      }
+    }
+  }
+  return nil;
+}
+
+- (void)forwardInvocation:(NSInvocation*)invocation {
+  // Instead of actually doing anything the protocol method would normally
+  // do, instead just generate a title and description and display an alert or
+  // log a message.
+  NSString* protocolName = objc_getAssociatedObject(
+      [self methodSignatureForSelector:invocation.selector],
+      &kAssociatedProtocolNameKey);
+  NSString* description = [invocation crsc_description];
+  if (self.baseViewController) {
+    [self showAlertWithTitle:protocolName message:description];
+  } else {
+    VLOG(0) << "Alerter -- protocol:"
+            << base::SysNSStringToUTF8(protocolName);
+    VLOG(0) << "Alerter -- invocation:"
+            << base::SysNSStringToUTF8(description);
+  }
+}
+
+#pragma mark - NSObject
+
+- (BOOL)conformsToProtocol:(Protocol*)aProtocol {
+  for (Protocol* protocol in _protocols) {
+    // Handle protocols that conform to other protocols.
+    if (protocol_conformsToProtocol(protocol, aProtocol))
+      return YES;
+  }
+  return NO;
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector {
+  return [self methodSignatureForSelector:aSelector] != nil;
+}
+
+#pragma mark - Private
+
+// Helper to show simple alert.
+- (void)showAlertWithTitle:(NSString*)title message:(NSString*)message {
+  UIAlertController* alertController =
+      [UIAlertController alertControllerWithTitle:title
+                                          message:message
+                                   preferredStyle:UIAlertControllerStyleAlert];
+  UIAlertAction* action =
+      [UIAlertAction actionWithTitle:@"Done"
+                               style:UIAlertActionStyleCancel
+                             handler:nil];
+  [alertController addAction:action];
+  [self.baseViewController presentViewController:alertController
+                                        animated:YES
+                                      completion:nil];
+}
+
+@end
+
+@implementation NSInvocation (Description)
+
+- (NSString*)crsc_description {
+  NSInteger arguments = self.methodSignature.numberOfArguments;
+  NSString* selector = NSStringFromSelector(self.selector);
+
+  // NSInvocation's first two arguments are |self| and |_cmd|; if they are the
+  // only ones, then the invocation has no actual arguments.
+  if (arguments == 2)
+    return selector;
+
+  // Get the parts of the selector name by splitting on /:/, and dropping the
+  // last (empty) part.
+  NSArray* keywords = [[selector componentsSeparatedByString:@":"]
+      subarrayWithRange:NSMakeRange(0, arguments - 2)];
+  NSMutableString* description = [[NSMutableString alloc] init];
+  NSInteger argumentIndex = 2;
+  for (NSString* keyword in keywords) {
+    // Insert a space before each keyword after the first one.
+    if (description.length)
+      [description appendString:@" "];
+    [description appendString:keyword];
+    [description appendString:@":"];
+    [description appendString:[self argumentDescriptionAtIndex:argumentIndex]];
+    argumentIndex++;
+  }
+
+  return description;
+}
+
+// Return a string describing the argument value at |index|.
+// (|index| is in NSInvocation's argument array).
+- (NSString*)argumentDescriptionAtIndex:(NSInteger)index {
+  const char* type = [self.methodSignature getArgumentTypeAtIndex:index];
+
+  switch (*type) {
+    case '@':
+      return [self objectDescriptionAtIndex:index];
+    // Add cases as needed here.
+    default:
+      return [NSString stringWithFormat:@"<Unknown Type:%c>", *type];
+  }
+}
+
+// Return a string describing an argument at |index| that's known to an
+// objective-C object.
+- (NSString*)objectDescriptionAtIndex:(NSInteger)index {
+  id object;
+
+  [self getArgument:&object atIndex:index];
+  if (!object)
+    return @"nil";
+
+  NSString* description = [object description];
+  NSString* className = NSStringFromClass([object class]);
+  if (!description) {
+    return
+        [NSString stringWithFormat:@"<%@ object, no description>", className];
+  }
+
+  // Wrap strings in @" ... ".
+  if ([object isKindOfClass:[NSString class]])
+    return [NSString stringWithFormat:@"@\"%@\"", description];
+
+  // Remove the address of objects from their descriptions, so (for example):
+  //   <NSObject: 0xc00lf0ccac1a>
+  // becomes just:
+  //   <NSObject>
+  NSRange range = NSMakeRange(0, description.length);
+  NSString* classPlusAddress =
+      [className stringByAppendingString:@": 0x[0-9a-c]+"];
+  return [description
+      stringByReplacingOccurrencesOfString:classPlusAddress
+                                withString:className
+                                   options:NSRegularExpressionSearch
+                                     range:range];
+}
+
+@end
diff --git a/ios/showcase/tab_grid/BUILD.gn b/ios/showcase/tab_grid/BUILD.gn
index 8f0eb14..fb4d36d 100644
--- a/ios/showcase/tab_grid/BUILD.gn
+++ b/ios/showcase/tab_grid/BUILD.gn
@@ -9,6 +9,7 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/browser/ui/commands:commands_clean_skeleton",
     "//ios/chrome/browser/ui/tab_grid:tab_grid_ui",
     "//ios/showcase/common",
   ]
diff --git a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
index 59cb68b..453052a 100644
--- a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
+++ b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
@@ -5,25 +5,35 @@
 #import "ios/showcase/tab_grid/sc_tab_grid_coordinator.h"
 
 #import "base/format_macros.h"
+#import "ios/chrome/browser/ui/commands/tab_commands.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
+#import "ios/showcase/common/protocol_alerter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface SCTabGridCoordinator ()<TabGridDataSource, TabGridActionDelegate>
+@interface SCTabGridCoordinator ()<TabGridDataSource>
 @property(nonatomic, strong) TabGridViewController* viewController;
+@property(nonatomic, strong) ProtocolAlerter* alerter;
 @end
 
 @implementation SCTabGridCoordinator
 @synthesize baseViewController = _baseViewController;
 @synthesize viewController = _viewController;
+@synthesize alerter = _alerter;
 
 - (void)start {
+  self.alerter =
+      [[ProtocolAlerter alloc] initWithProtocols:@[ @protocol(TabCommands) ]];
+  self.alerter.baseViewController = self.baseViewController;
+
   self.viewController = [[TabGridViewController alloc] init];
   self.viewController.title = @"Tab Grid";
   self.viewController.dataSource = self;
-  self.viewController.actionDelegate = self;
+  self.viewController.tabCommandHandler =
+      static_cast<id<TabCommands>>(self.alerter);
+
   [self.baseViewController setHidesBarsOnSwipe:YES];
   [self.baseViewController pushViewController:self.viewController animated:YES];
 }
@@ -38,44 +48,4 @@
   return [NSString stringWithFormat:@"Tab %" PRIdNS, index];
 }
 
-#pragma mark - TabGridActionDelegate
-
-- (void)showTabAtIndexPath:(NSIndexPath*)indexPath {
-  [self
-      showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate))
-                 message:[NSString
-                             stringWithFormat:@"showTabAtIndexPath:%" PRIdNS,
-                                              indexPath.item]];
-}
-
-- (void)showTabGrid {
-  [self
-      showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate))
-                 message:NSStringFromSelector(_cmd)];
-}
-
-- (void)showSettings {
-  [self
-      showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate))
-                 message:NSStringFromSelector(_cmd)];
-}
-
-#pragma mark - Private
-
-// Helper to show simple alert.
-- (void)showAlertWithTitle:(NSString*)title message:(NSString*)message {
-  UIAlertController* alertController =
-      [UIAlertController alertControllerWithTitle:title
-                                          message:message
-                                   preferredStyle:UIAlertControllerStyleAlert];
-  UIAlertAction* action =
-      [UIAlertAction actionWithTitle:@"Done"
-                               style:UIAlertActionStyleCancel
-                             handler:nil];
-  [alertController addAction:action];
-  [self.baseViewController presentViewController:alertController
-                                        animated:YES
-                                      completion:nil];
-}
-
 @end
diff --git a/ios/showcase/tab_grid/sc_tab_grid_egtest.mm b/ios/showcase/tab_grid/sc_tab_grid_egtest.mm
index 3521cbdb..68a4ad0a 100644
--- a/ios/showcase/tab_grid/sc_tab_grid_egtest.mm
+++ b/ios/showcase/tab_grid/sc_tab_grid_egtest.mm
@@ -22,7 +22,7 @@
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:grey_text(@"Tab 0")]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:grey_text(@"TabGridActionDelegate")]
+  [[EarlGrey selectElementWithMatcher:grey_text(@"TabCommands")]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:grey_text(@"Done")]
       performAction:grey_tap()];
diff --git a/ios/web/public/web_state/ui/crw_native_content.h b/ios/web/public/web_state/ui/crw_native_content.h
index 994ba6d..b3f336fe1 100644
--- a/ios/web/public/web_state/ui/crw_native_content.h
+++ b/ios/web/public/web_state/ui/crw_native_content.h
@@ -85,6 +85,9 @@
 // Called when a snapshot of the content will be taken.
 - (void)willUpdateSnapshot;
 
+// Notifies the CRWNativeContent that it will be removed from superview.
+- (void)willBeDismissed;
+
 // The URL that will be displayed to the user when presenting this native
 // content.
 - (GURL)virtualURL;
diff --git a/ios/web/public/web_state/web_state_observer.h b/ios/web/public/web_state/web_state_observer.h
index d0b76cd..ca05867 100644
--- a/ios/web/public/web_state/web_state_observer.h
+++ b/ios/web/public/web_state/web_state_observer.h
@@ -29,10 +29,6 @@
 // load events from WebState.
 class WebStateObserver {
  public:
-  // Key code associated to form events for which the key code is missing or
-  // irrelevant.
-  static int kInvalidFormKeyCode;
-
   // Returns the web state associated with this observer.
   WebState* web_state() const { return web_state_; }
 
@@ -77,12 +73,10 @@
 
   // Called when the user is typing on a form field, with |error| indicating if
   // there is any error when parsing the form field information.
-  // |key_code| may be kInvalidFormKeyCode if there is no key code.
   virtual void FormActivityRegistered(const std::string& form_name,
                                       const std::string& field_name,
                                       const std::string& type,
                                       const std::string& value,
-                                      int key_code,
                                       bool input_missing) {}
 
   // Invoked when new favicon URL candidates are received.
diff --git a/ios/web/public/web_state/web_state_observer_bridge.h b/ios/web/public/web_state/web_state_observer_bridge.h
index 2a689f4..a0ba092 100644
--- a/ios/web/public/web_state/web_state_observer_bridge.h
+++ b/ios/web/public/web_state/web_state_observer_bridge.h
@@ -53,7 +53,6 @@
                                fieldName:(const std::string&)fieldName
                                     type:(const std::string&)type
                                    value:(const std::string&)value
-                                 keyCode:(int)keyCode
                             inputMissing:(BOOL)inputMissing;
 
 // Invoked by WebStateObserverBridge::FaviconUrlUpdated.
@@ -102,7 +101,6 @@
                               const std::string& field_name,
                               const std::string& type,
                               const std::string& value,
-                              int key_code,
                               bool input_missing) override;
   void FaviconUrlUpdated(const std::vector<FaviconURL>& candidates) override;
   void WebStateDestroyed() override;
diff --git a/ios/web/test/data/mojo_test.js b/ios/web/test/data/mojo_test.js
index 7b3a9e4..6c48cca 100644
--- a/ios/web/test/data/mojo_test.js
+++ b/ios/web/test/data/mojo_test.js
@@ -13,10 +13,9 @@
   return new Promise(function(resolve, reject) {
     define([
       'mojo/public/js/bindings',
-      'mojo/public/js/connection',
       'ios/web/test/mojo_test.mojom',
       'content/public/renderer/frame_interfaces',
-    ], function(bindings, connection, mojom, frameInterfaces) {
+    ], function(bindings, mojom, frameInterfaces) {
       var pageImpl, browserProxy;
 
       /** @constructor */
@@ -35,9 +34,8 @@
         },
       };
 
-      browserProxy = connection.bindHandleToProxy(
-          frameInterfaces.getInterface(mojom.TestUIHandlerMojo.name),
-          mojom.TestUIHandlerMojo);
+      browserProxy = new mojom.TestUIHandlerMojoPtr(
+          frameInterfaces.getInterface(mojom.TestUIHandlerMojo.name));
       pageImpl = new TestPageImpl();
 
       browserProxy.setClientPage(pageImpl.binding.createInterfacePtrAndBind());
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js
index 6ab7caca..2d6b86c 100644
--- a/ios/web/web_state/js/resources/core.js
+++ b/ios/web/web_state/js/resources/core.js
@@ -293,8 +293,6 @@
         'type': evt.type,
         'value': value
       };
-      if (evt.keyCode)
-        msg.keyCode = evt.keyCode;
       invokeOnHost_(msg);
     };
 
@@ -648,7 +646,6 @@
 
     var form = evt.target;
     var targetsFrame = form.target && hasFrame_(window, form.target);
-    // TODO(stuartmorgan): Handle external targets. crbug.com/233543
 
     var action = form.getAttribute('action');
     // Default action is to re-submit to same page.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 9862aab..f2e9226 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2903,7 +2903,6 @@
   std::string fieldName;
   std::string type;
   std::string value;
-  int keyCode = web::WebStateObserver::kInvalidFormKeyCode;
   bool inputMissing = false;
   if (!message->GetString("formName", &formName) ||
       !message->GetString("fieldName", &fieldName) ||
@@ -2912,14 +2911,8 @@
     inputMissing = true;
   }
 
-  double keyCodeAsDouble = 0;
-  if (!message->GetDouble("keyCode", &keyCodeAsDouble) || keyCodeAsDouble < 0) {
-    keyCode = web::WebStateObserver::kInvalidFormKeyCode;
-  } else {
-    keyCode = static_cast<int>(keyCodeAsDouble);
-  }
   _webStateImpl->OnFormActivityRegistered(formName, fieldName, type, value,
-                                          keyCode, inputMissing);
+                                          inputMissing);
   return YES;
 }
 
@@ -5741,7 +5734,6 @@
 }
 
 - (void)removeObserver:(id<CRWWebControllerObserver>)observer {
-  // TODO(jimblackler): make _observers use NSMapTable. crbug.com/367992
   DCHECK([_observers containsObject:observer]);
   [_observers removeObject:observer];
   // Remove the associated WebControllerObserverBridge.
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm
index 25e31bde..87e1363 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -172,6 +172,9 @@
 - (void)setNativeController:(id<CRWNativeContent>)nativeController {
   if (![_nativeController isEqual:nativeController]) {
     base::WeakNSProtocol<id> oldController(_nativeController);
+    if ([oldController respondsToSelector:@selector(willBeDismissed)]) {
+      [oldController willBeDismissed];
+    }
     [[oldController view] removeFromSuperview];
     _nativeController.reset([nativeController retain]);
     // TODO(crbug.com/503297): Re-enable this DCHECK once native controller
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 4f6fd35..69c625ca 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -27,6 +27,7 @@
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
+#include "ios/web/public/web_state/web_state_observer.h"
 #import "ios/web/test/web_test_with_web_controller.h"
 #import "ios/web/test/wk_web_view_crash_utils.h"
 #include "ios/web/web_state/blocked_popup_info.h"
@@ -771,6 +772,41 @@
               web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
 }
 
+// Real WKWebView is required for CRWWebControllerFormActivityTest.
+typedef web::WebTestWithWebController CRWWebControllerFormActivityTest;
+
+// Tests that keyup event correctly delivered to WebStateObserver.
+TEST_F(CRWWebControllerFormActivityTest, KeyUpEvent) {
+  // Observes and verifies FormActivityRegistered call.
+  class FormActivityObserver : public web::WebStateObserver {
+   public:
+    explicit FormActivityObserver(web::WebState* web_state)
+        : web::WebStateObserver(web_state) {}
+    bool form_activity_registered() const { return form_activity_registered_; }
+    // WebStateObserver overrides:
+    void FormActivityRegistered(const std::string& form_name,
+                                const std::string& field_name,
+                                const std::string& type,
+                                const std::string& value,
+                                bool input_missing) override {
+      EXPECT_EQ("keyup", type);
+      EXPECT_FALSE(input_missing);
+      form_activity_registered_ = true;
+    }
+
+   private:
+    bool form_activity_registered_ = false;
+  };
+  FormActivityObserver form_activity_observer(web_state());
+  FormActivityObserver& form_activity_observer_ref(form_activity_observer);
+
+  LoadHtml(@"<p></p>");
+  ExecuteJavaScript(@"document.dispatchEvent(new KeyboardEvent('keyup'));");
+  base::test::ios::WaitUntilCondition(^{
+    return form_activity_observer_ref.form_activity_registered();
+  });
+}
+
 // Real WKWebView is required for CRWWebControllerJSExecutionTest.
 typedef web::WebTestWithWebController CRWWebControllerJSExecutionTest;
 
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index e511ffd..cb86de42 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -114,7 +114,6 @@
                                 const std::string& field_name,
                                 const std::string& type,
                                 const std::string& value,
-                                int key_code,
                                 bool input_missing);
 
   // Called when new FaviconURL candidates are received.
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 42a89257..46897fa 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -239,11 +239,10 @@
                                             const std::string& field_name,
                                             const std::string& type,
                                             const std::string& value,
-                                            int key_code,
                                             bool input_missing) {
   for (auto& observer : observers_) {
     observer.FormActivityRegistered(form_name, field_name, type, value,
-                                    key_code, input_missing);
+                                    input_missing);
   }
 }
 
diff --git a/ios/web/web_state/web_state_observer.mm b/ios/web/web_state/web_state_observer.mm
index dcb0204..6c250e9 100644
--- a/ios/web/web_state/web_state_observer.mm
+++ b/ios/web/web_state/web_state_observer.mm
@@ -9,8 +9,6 @@
 
 namespace web {
 
-int WebStateObserver::kInvalidFormKeyCode = -1;
-
 WebStateObserver::WebStateObserver(WebState* web_state) : web_state_(nullptr) {
   Observe(web_state);
 }
diff --git a/ios/web/web_state/web_state_observer_bridge.mm b/ios/web/web_state/web_state_observer_bridge.mm
index 292470c..4ff34c2d 100644
--- a/ios/web/web_state/web_state_observer_bridge.mm
+++ b/ios/web/web_state/web_state_observer_bridge.mm
@@ -85,14 +85,12 @@
     const std::string& field_name,
     const std::string& type,
     const std::string& value,
-    int key_code,
     bool input_missing) {
   SEL selector = @selector(webState:
       didRegisterFormActivityWithFormNamed:
                                  fieldName:
                                       type:
                                      value:
-                                   keyCode:
                               inputMissing:);
   if ([observer_ respondsToSelector:selector]) {
     [observer_ webState:web_state()
@@ -100,7 +98,6 @@
                                    fieldName:field_name
                                         type:type
                                        value:value
-                                     keyCode:key_code
                                 inputMissing:input_missing];
   }
 }
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 2111b9e..96bdca3 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -365,6 +365,7 @@
       "//base",
       "//base/test:test_support",
       "//media",
+      "//media/base:test_support",
       "//testing/gtest",
       "//ui/base",
       "//ui/display/manager",
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index 1d6919b..cd85fbc 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -1154,11 +1154,14 @@
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&DXVAVideoDecodeAccelerator::NotifyResetDone, weak_ptr_));
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped,
+                 weak_ptr_, std::move(pending_input_buffers_)));
+  pending_input_buffers_.clear();
 
   StartDecoderThread();
   SetState(kNormal);
-
-  NotifyInputBuffersDropped();
 }
 
 void DXVAVideoDecodeAccelerator::Destroy() {
@@ -2064,19 +2067,18 @@
   }
 }
 
-void DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped() {
+void DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped(
+    const PendingInputs& pending_buffers) {
   DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
   if (!client_)
     return;
 
-  for (PendingInputs::iterator it = pending_input_buffers_.begin();
-       it != pending_input_buffers_.end(); ++it) {
+  for (const auto& buffer : pending_buffers) {
     LONGLONG input_buffer_id = 0;
-    RETURN_ON_HR_FAILURE((*it)->GetSampleTime(&input_buffer_id),
+    RETURN_ON_HR_FAILURE(buffer->GetSampleTime(&input_buffer_id),
                          "Failed to get buffer id associated with sample", );
     client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
   }
-  pending_input_buffers_.clear();
 }
 
 void DXVAVideoDecodeAccelerator::DecodePendingInputBuffers() {
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h
index a4df35c..9706516 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -147,6 +147,7 @@
   friend class PbufferPictureBuffer;
   typedef void* EGLConfig;
   typedef void* EGLSurface;
+  typedef std::list<base::win::ScopedComPtr<IMFSample>> PendingInputs;
 
   // Returns the minimum resolution for the |profile| passed in.
   static std::pair<int, int> GetMinResolution(const VideoCodecProfile profile);
@@ -244,7 +245,7 @@
 
   // Sends pending input buffer processed acks to the client if we don't have
   // output samples waiting to be processed.
-  void NotifyInputBuffersDropped();
+  void NotifyInputBuffersDropped(const PendingInputs& input_buffers);
 
   // Decodes pending input buffers.
   void DecodePendingInputBuffers();
@@ -468,7 +469,6 @@
   uint32_t output_array_size_ = 0;
 
   // List of input samples waiting to be processed.
-  typedef std::list<base::win::ScopedComPtr<IMFSample>> PendingInputs;
   PendingInputs pending_input_buffers_;
 
   // Callback to get current GLContext.
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index ed295a1..9611b14 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -56,6 +56,7 @@
 #include "build/build_config.h"
 #include "gpu/command_buffer/service/gpu_preferences.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "media/base/test_data_util.h"
 #include "media/filters/h264_parser.h"
 #include "media/gpu/fake_video_decode_accelerator.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
@@ -137,6 +138,10 @@
 // requesting the VDA itself to allocate buffers.
 bool g_test_import = false;
 
+// This is the location of the test files. If empty, they're in the current
+// working directory.
+base::FilePath g_test_file_path;
+
 // Environment to store rendering thread.
 class VideoDecodeAcceleratorTestEnvironment;
 VideoDecodeAcceleratorTestEnvironment* g_env;
@@ -188,13 +193,19 @@
 const gfx::Size kThumbnailSize(160, 120);
 const int kMD5StringLength = 32;
 
+base::FilePath GetTestDataFile(const base::FilePath& input_file) {
+  if (input_file.IsAbsolute())
+    return input_file;
+  return base::MakeAbsoluteFilePath(g_test_file_path.Append(input_file));
+}
+
 // Read in golden MD5s for the thumbnailed rendering of this video
 void ReadGoldenThumbnailMD5s(const TestVideoFile* video_file,
                              std::vector<std::string>* md5_strings) {
   base::FilePath filepath(video_file->file_name);
   filepath = filepath.AddExtension(FILE_PATH_LITERAL(".md5"));
   std::string all_md5s;
-  base::ReadFileToString(base::MakeAbsoluteFilePath(filepath), &all_md5s);
+  base::ReadFileToString(GetTestDataFile(filepath), &all_md5s);
   *md5_strings = base::SplitString(all_md5s, "\n", base::TRIM_WHITESPACE,
                                    base::SPLIT_WANT_ALL);
   // Check these are legitimate MD5s.
@@ -1187,7 +1198,7 @@
 
     // Read in the video data.
     base::FilePath filepath(video_file->file_name);
-    LOG_ASSERT(base::ReadFileToString(base::MakeAbsoluteFilePath(filepath),
+    LOG_ASSERT(base::ReadFileToString(GetTestDataFile(filepath),
                                       &video_file->data_str))
         << "test_video_file: " << filepath.MaybeAsASCII();
 
@@ -1483,8 +1494,9 @@
       base::FilePath filepath(test_video_files_[0]->file_name);
       filepath = filepath.AddExtension(FILE_PATH_LITERAL(".bad_thumbnails"));
       filepath = filepath.AddExtension(FILE_PATH_LITERAL(".png"));
-      int num_bytes = base::WriteFile(
-          filepath, reinterpret_cast<char*>(&png[0]), png.size());
+      int num_bytes =
+          base::WriteFile(GetTestDataFile(filepath),
+                          reinterpret_cast<char*>(&png[0]), png.size());
       ASSERT_EQ(num_bytes, static_cast<int>(png.size()));
     }
     ASSERT_NE(match, golden_md5s.end());
@@ -1819,6 +1831,10 @@
       media::g_test_import = true;
       continue;
     }
+    if (it->first == "use-test-data-path") {
+      media::g_test_file_path = media::GetTestDataFilePath("");
+      continue;
+    }
   }
 
   base::ShadowingAtExitManager at_exit_manager;
diff --git a/media/remoting/remote_renderer_impl.cc b/media/remoting/remote_renderer_impl.cc
index b87b7f6..b814b00cb 100644
--- a/media/remoting/remote_renderer_impl.cc
+++ b/media/remoting/remote_renderer_impl.cc
@@ -35,14 +35,14 @@
       rpc_broker_(remoting_renderer_controller_->GetRpcBroker()),
       rpc_handle_(rpc_broker_->GetUniqueHandle()),
       remote_renderer_handle_(remoting::kInvalidHandle),
-      interstitial_ui_(video_renderer_sink,
-                       remoting_renderer_controller->pipeline_metadata()),
+      video_renderer_sink_(video_renderer_sink),
       weak_factory_(this) {
   VLOG(2) << __func__;
   // The constructor is running on the main thread.
-  DCHECK(remoting_renderer_controller);
-
-  UpdateInterstitial();
+  DCHECK(remoting_renderer_controller_);
+  remoting_renderer_controller_->SetShowInterstitialCallback(
+      base::Bind(&RemoteRendererImpl::RequestUpdateInterstitialOnMainThread,
+                 media_task_runner_, weak_factory_.GetWeakPtr()));
 
   const remoting::RpcBroker::ReceiveMessageCallback receive_callback =
       base::Bind(&RemoteRendererImpl::OnMessageReceivedOnMainThread,
@@ -53,6 +53,17 @@
 RemoteRendererImpl::~RemoteRendererImpl() {
   VLOG(2) << __func__;
   DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  UpdateInterstitial(interstitial_background_, canvas_size_,
+                     RemotingInterstitialType::BETWEEN_SESSIONS);
+
+  // Post task on main thread to unset the interstial callback.
+  main_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&RemotingRendererController::SetShowInterstitialCallback,
+                 remoting_renderer_controller_,
+                 RemotingRendererController::ShowInterstitialCallback()));
+
   // Post task on main thread to unregister message receiver.
   main_task_runner_->PostTask(
       FROM_HERE,
@@ -597,10 +608,6 @@
 
   VLOG(2) << __func__ << " with PipelineStatus error=" << error;
 
-  main_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&RemoteRendererImpl::UpdateInterstitial,
-                            weak_factory_.GetWeakPtr()));
-
   const State old_state = state_;
   state_ = STATE_ERROR;
 
@@ -618,12 +625,29 @@
   client_->OnError(error);
 }
 
-void RemoteRendererImpl::UpdateInterstitial() {
-  DCHECK(main_task_runner_->BelongsToCurrentThread());
+// static
+void RemoteRendererImpl::RequestUpdateInterstitialOnMainThread(
+    scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+    base::WeakPtr<RemoteRendererImpl> remote_renderer_impl,
+    const SkBitmap& background_image,
+    const gfx::Size& canvas_size,
+    RemotingInterstitialType interstitial_type) {
+  media_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(&RemoteRendererImpl::UpdateInterstitial, remote_renderer_impl,
+                 background_image, canvas_size, interstitial_type));
+}
 
-  interstitial_ui_.ShowInterstitial(
-      remoting_renderer_controller_->remoting_source()->state() ==
-      RemotingSessionState::SESSION_STARTED);
+void RemoteRendererImpl::UpdateInterstitial(
+    const SkBitmap& background_image,
+    const gfx::Size& canvas_size,
+    RemotingInterstitialType interstitial_type) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  if (!background_image.drawsNothing())
+    interstitial_background_ = background_image;
+  canvas_size_ = canvas_size;
+  PaintRemotingInterstitial(interstitial_background_, canvas_size_,
+                            interstitial_type, video_renderer_sink_);
 }
 
 }  // namespace media
diff --git a/media/remoting/remote_renderer_impl.h b/media/remoting/remote_renderer_impl.h
index b557c3d..38947f9 100644
--- a/media/remoting/remote_renderer_impl.h
+++ b/media/remoting/remote_renderer_impl.h
@@ -67,6 +67,15 @@
       base::WeakPtr<RemoteRendererImpl> self,
       std::unique_ptr<remoting::pb::RpcMessage> message);
 
+  // Callback when remoting interstitial needs to be updated. Will post task to
+  // media thread to avoid threading race condition.
+  static void RequestUpdateInterstitialOnMainThread(
+      scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+      base::WeakPtr<RemoteRendererImpl> remote_renderer_impl,
+      const SkBitmap& background_image,
+      const gfx::Size& canvas_size,
+      RemotingInterstitialType interstitial_type);
+
  public:
   // media::Renderer implementation.
   void Initialize(DemuxerStreamProvider* demuxer_stream_provider,
@@ -121,12 +130,16 @@
   void OnStatisticsUpdate(std::unique_ptr<remoting::pb::RpcMessage> message);
   void OnDurationChange(std::unique_ptr<remoting::pb::RpcMessage> message);
 
+  // Called to update the remoting interstitial. Draw remoting interstitial on
+  // |interstitial_background_| if |background_image| is empty. Update
+  // |interstitial_background_| if |background_image| is not empty.
+  void UpdateInterstitial(const SkBitmap& background_image,
+                          const gfx::Size& canvas_size,
+                          RemotingInterstitialType interstitial_type);
+
   // Shut down remoting session.
   void OnFatalError(PipelineStatus status);
 
-  // Show interstial accordingly.
-  void UpdateInterstitial();
-
   State state_;
   const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
@@ -160,7 +173,12 @@
   CdmAttachedCB cdm_attached_cb_;
   base::Closure flush_cb_;
 
-  RemotingInterstitialUI interstitial_ui_;
+  VideoRendererSink* const video_renderer_sink_;  // Outlives this class.
+  // The background image for remoting interstitial. When |this| is destructed,
+  // |interstitial_background_| will be paint to clear the cast messages on
+  // the interstitial.
+  SkBitmap interstitial_background_;
+  gfx::Size canvas_size_;
 
   base::WeakPtrFactory<RemoteRendererImpl> weak_factory_;
 
diff --git a/media/remoting/remoting_interstitial_ui.cc b/media/remoting/remoting_interstitial_ui.cc
index 9e3f01d..fc0e28c33 100644
--- a/media/remoting/remoting_interstitial_ui.cc
+++ b/media/remoting/remoting_interstitial_ui.cc
@@ -8,6 +8,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_renderer_sink.h"
 #include "media/base/video_util.h"
+#include "skia/ext/image_operations.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
@@ -21,45 +22,31 @@
 
 namespace {
 
-gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) {
-  if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270)
-    return gfx::Size(natural_size.height(), natural_size.width());
-  return natural_size;
+SkBitmap ResizeImage(const SkBitmap& image, const gfx::Size& scaled_size) {
+  DCHECK(!scaled_size.IsEmpty());
+
+  if (image.width() == scaled_size.width() &&
+      image.height() == scaled_size.height())
+    return image;
+
+  return skia::ImageOperations::Resize(
+      image, skia::ImageOperations::RESIZE_BEST, scaled_size.width(),
+      scaled_size.height());
 }
 
-}  // namespace
-
-RemotingInterstitialUI::RemotingInterstitialUI(
-    VideoRendererSink* video_renderer_sink,
-    const PipelineMetadata& pipeline_metadata)
-    : video_renderer_sink_(video_renderer_sink),
-      pipeline_metadata_(pipeline_metadata) {
-  DCHECK(pipeline_metadata_.has_video);
-  pipeline_metadata_.natural_size = GetRotatedVideoSize(
-      pipeline_metadata_.video_rotation, pipeline_metadata_.natural_size);
-}
-
-RemotingInterstitialUI::~RemotingInterstitialUI() {}
-
-scoped_refptr<VideoFrame> RemotingInterstitialUI::GetInterstitial(
-    const SkBitmap& background_image,
-    bool is_remoting_successful) {
-  const gfx::Size canvas_size =
-      gfx::Size(background_image.width(), background_image.height());
-  DCHECK(canvas_size == pipeline_metadata_.natural_size);
-
-  color_utils::HSL shift = {-1, 0, 0.2};  // Make monochromatic.
-  SkBitmap modified_bitmap =
-      SkBitmapOperations::CreateHSLShiftedBitmap(background_image, shift);
-
-  SkCanvas canvas(modified_bitmap);
+void RenderCastMessage(const gfx::Size& canvas_size,
+                       RemotingInterstitialType type,
+                       SkCanvas* canvas) {
+  DCHECK(canvas);
+  if (type == RemotingInterstitialType::BETWEEN_SESSIONS)
+    return;
 
   // Blur the background image.
   SkScalar sigma = SkDoubleToScalar(10);
   SkPaint paint_blur;
   paint_blur.setImageFilter(SkBlurImageFilter::Make(sigma, sigma, nullptr));
-  canvas.saveLayer(0, &paint_blur);
-  canvas.restore();
+  canvas->saveLayer(0, &paint_blur);
+  canvas->restore();
 
   // Create SkPaint for text and icon bitmap.
   // After |paint| draws, the new canvas should look like this:
@@ -84,59 +71,84 @@
 
   // Draw the appropriate text.
   const std::string remote_playback_message =
-      is_remoting_successful
-          ? GetLocalizedStringUTF8(MEDIA_REMOTING_CASTING_VIDEO_TEXT)
-          : GetLocalizedStringUTF8(MEDIA_REMOTING_CAST_ERROR_TEXT);
+      (type == RemotingInterstitialType::IN_SESSION
+           ? GetLocalizedStringUTF8(MEDIA_REMOTING_CASTING_VIDEO_TEXT)
+           : GetLocalizedStringUTF8(MEDIA_REMOTING_CAST_ERROR_TEXT));
   size_t display_text_width = paint.measureText(remote_playback_message.data(),
                                                 remote_playback_message.size());
   SkScalar sk_text_offset_x = (canvas_size.width() - display_text_width) / 2.0;
   SkScalar sk_text_offset_y = (canvas_size.height() / 2.0) + text_size;
-  canvas.drawText(remote_playback_message.data(),
-                  remote_playback_message.size(), sk_text_offset_x,
-                  sk_text_offset_y, paint);
+  canvas->drawText(remote_playback_message.data(),
+                   remote_playback_message.size(), sk_text_offset_x,
+                   sk_text_offset_y, paint);
 
   // Draw the appropriate Cast icon.
   gfx::VectorIconId current_icon =
-      is_remoting_successful ? gfx::VectorIconId::MEDIA_ROUTER_ACTIVE
-                             : gfx::VectorIconId::MEDIA_ROUTER_WARNING;
+      (type == RemotingInterstitialType::IN_SESSION
+           ? gfx::VectorIconId::MEDIA_ROUTER_ACTIVE
+           : gfx::VectorIconId::MEDIA_ROUTER_WARNING);
   gfx::ImageSkia icon_image =
       gfx::CreateVectorIcon(current_icon, 65, SK_ColorLTGRAY);
   const SkBitmap* icon_bitmap = icon_image.bitmap();
   SkScalar sk_image_offset_x = (canvas_size.width() - icon_image.width()) / 2.0;
   SkScalar sk_image_offset_y =
       (canvas_size.height() / 2.0) - icon_image.height();
-  canvas.drawBitmap(*icon_bitmap, sk_image_offset_x, sk_image_offset_y, &paint);
+  canvas->drawBitmap(*icon_bitmap, sk_image_offset_x, sk_image_offset_y,
+                     &paint);
+}
+
+scoped_refptr<VideoFrame> RenderInterstitialFrame(
+    const SkBitmap& image,
+    const gfx::Size& canvas_size,
+    RemotingInterstitialType type) {
+  SkBitmap canvas_bitmap;
+  canvas_bitmap.allocN32Pixels(canvas_size.width(), canvas_size.height());
+  canvas_bitmap.eraseColor(SK_ColorBLACK);
+  SkCanvas canvas(canvas_bitmap);
+
+  // Draw background image on the canvas.
+  if (!image.drawsNothing()) {
+    gfx::Rect centered_rect = ComputeLetterboxRegion(
+        gfx::Rect(canvas_size), gfx::Size(image.width(), image.height()));
+    SkBitmap processed_image = ResizeImage(image, centered_rect.size());
+    if (type != RemotingInterstitialType::BETWEEN_SESSIONS) {
+      color_utils::HSL shift = {-1, 0, 0.2};  // Make monochromatic.
+      processed_image =
+          SkBitmapOperations::CreateHSLShiftedBitmap(processed_image, shift);
+    }
+    canvas.writePixels(processed_image, centered_rect.x(), centered_rect.y());
+  }
+
+  RenderCastMessage(canvas_size, type, &canvas);
 
   // Create a new VideoFrame, copy the bitmap, then return it.
   scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
       media::PIXEL_FORMAT_I420, canvas_size, gfx::Rect(canvas_size),
       canvas_size, base::TimeDelta());
-  modified_bitmap.lockPixels();
+  canvas_bitmap.lockPixels();
   media::CopyRGBToVideoFrame(
-      reinterpret_cast<uint8_t*>(modified_bitmap.getPixels()),
-      modified_bitmap.rowBytes(),
+      reinterpret_cast<uint8_t*>(canvas_bitmap.getPixels()),
+      canvas_bitmap.rowBytes(),
       gfx::Rect(canvas_size.width(), canvas_size.height()), video_frame.get());
-  modified_bitmap.unlockPixels();
+  canvas_bitmap.unlockPixels();
   return video_frame;
 }
 
-void RemotingInterstitialUI::ShowInterstitial(bool is_remoting_successful) {
-  if (!video_renderer_sink_)
-    return;
+}  // namespace
 
-  // TODO(xjz): Provide poster image, if available, rather than a blank, black
-  // image.
-  SkBitmap background_image;
-  const gfx::Size size = pipeline_metadata_.natural_size;
-  background_image.allocN32Pixels(size.width(), size.height());
-  background_image.eraseColor(SK_ColorBLACK);
+void PaintRemotingInterstitial(const SkBitmap& image,
+                               const gfx::Size& canvas_size,
+                               RemotingInterstitialType interstitial_type,
+                               VideoRendererSink* video_renderer_sink) {
+  if (canvas_size.IsEmpty())
+    return;
 
   const scoped_refptr<VideoFrame> interstitial =
-      GetInterstitial(background_image, is_remoting_successful);
+      RenderInterstitialFrame(image, canvas_size, interstitial_type);
+
   if (!interstitial)
     return;
-
-  video_renderer_sink_->PaintSingleFrame(interstitial);
+  video_renderer_sink->PaintSingleFrame(interstitial);
 }
 
 }  // namespace media
diff --git a/media/remoting/remoting_interstitial_ui.h b/media/remoting/remoting_interstitial_ui.h
index 7807628..51c0a730 100644
--- a/media/remoting/remoting_interstitial_ui.h
+++ b/media/remoting/remoting_interstitial_ui.h
@@ -5,36 +5,33 @@
 #ifndef MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_
 #define MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_
 
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "media/base/pipeline_metadata.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
+namespace gfx {
+class Size;
+}
+
 namespace media {
 
-class VideoFrame;
 class VideoRendererSink;
 
-class RemotingInterstitialUI {
- public:
-  RemotingInterstitialUI(VideoRendererSink* video_renderer_sink,
-                         const PipelineMetadata& pipeline_metadata);
-  ~RemotingInterstitialUI();
-
-  void ShowInterstitial(bool is_remoting_successful);
-
- private:
-  // Gets an 'interstitial' VideoFrame to paint on the media player when the
-  // video is being played remotely.
-  scoped_refptr<VideoFrame> GetInterstitial(const SkBitmap& background_image,
-                                            bool is_remoting_successful);
-
-  VideoRendererSink* const video_renderer_sink_;  // Outlives this class.
-  PipelineMetadata pipeline_metadata_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemotingInterstitialUI);
+enum RemotingInterstitialType {
+  BETWEEN_SESSIONS,             // Show background image only.
+  IN_SESSION,                   // Show MEDIA_REMOTING_CASTING_VIDEO_TEXT.
+  ENCRYPTED_MEDIA_FATAL_ERROR,  // Show MEDIA_REMOTING_CAST_ERROR_TEXT.
 };
 
+// Paint an interstitial frame and send it to the given VideoRendererSink.
+// |background_image| will be scaled to fit in |canvas_size|, but keep its
+// aspect ratio. When has different aspect ratio with |canvas_size|, scaled
+// |background_image| will be centered in the canvas. When |background_image|
+// is empty, interstitial will be drawn on a blank and black background. When
+// |interstial_type| is BETWEEN_SESSIONS, show background image only.
+void PaintRemotingInterstitial(const SkBitmap& background_image,
+                               const gfx::Size& canvas_size,
+                               RemotingInterstitialType interstitial_type,
+                               VideoRendererSink* video_renderer_sink);
+
 }  // namespace media
 
 #endif  // MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_
diff --git a/media/remoting/remoting_renderer_controller.cc b/media/remoting/remoting_renderer_controller.cc
index c58c17a..0464d9ed 100644
--- a/media/remoting/remoting_renderer_controller.cc
+++ b/media/remoting/remoting_renderer_controller.cc
@@ -7,10 +7,21 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_checker.h"
+#include "media/base/video_util.h"
 #include "media/remoting/remoting_cdm_context.h"
 
 namespace media {
 
+namespace {
+
+gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) {
+  if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270)
+    return gfx::Size(natural_size.height(), natural_size.width());
+  return natural_size;
+}
+
+}  // namespace
+
 RemotingRendererController::RemotingRendererController(
     scoped_refptr<RemotingSourceImpl> remoting_source)
     : remoting_source_(remoting_source), weak_factory_(this) {
@@ -46,6 +57,7 @@
   if (!sink_available_changed_cb_.is_null())
     sink_available_changed_cb_.Run(IsRemoteSinkAvailable());
 
+  UpdateInterstitial();
   UpdateAndMaybeSwitch();
 }
 
@@ -146,17 +158,22 @@
     const PipelineMetadata& metadata) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  const gfx::Size old_size = pipeline_metadata_.natural_size;
   pipeline_metadata_ = metadata;
 
   is_encrypted_ = false;
   if (has_video()) {
-    video_decoder_config_ = metadata.video_decoder_config;
-    is_encrypted_ |= video_decoder_config_.is_encrypted();
+    is_encrypted_ |= metadata.video_decoder_config.is_encrypted();
+    pipeline_metadata_.natural_size = GetRotatedVideoSize(
+        pipeline_metadata_.video_rotation, pipeline_metadata_.natural_size);
   }
   if (has_audio()) {
-    audio_decoder_config_ = metadata.audio_decoder_config;
-    is_encrypted_ |= audio_decoder_config_.is_encrypted();
+    is_encrypted_ |= metadata.audio_decoder_config.is_encrypted();
   }
+
+  if (pipeline_metadata_.natural_size != old_size)
+    UpdateInterstitial();
+
   UpdateAndMaybeSwitch();
 }
 
@@ -164,13 +181,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(has_video());
 
-  switch (video_decoder_config_.codec()) {
+  switch (pipeline_metadata_.video_decoder_config.codec()) {
     case VideoCodec::kCodecH264:
     case VideoCodec::kCodecVP8:
       return true;
     default:
       VLOG(2) << "Remoting does not support video codec: "
-              << video_decoder_config_.codec();
+              << pipeline_metadata_.video_decoder_config.codec();
       return false;
   }
 }
@@ -179,7 +196,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(has_audio());
 
-  switch (audio_decoder_config_.codec()) {
+  switch (pipeline_metadata_.audio_decoder_config.codec()) {
     case AudioCodec::kCodecAAC:
     case AudioCodec::kCodecMP3:
     case AudioCodec::kCodecPCM:
@@ -199,7 +216,7 @@
       return true;
     default:
       VLOG(2) << "Remoting does not support audio codec: "
-              << audio_decoder_config_.codec();
+              << pipeline_metadata_.audio_decoder_config.codec();
       return false;
   }
 }
@@ -309,4 +326,36 @@
   }
 }
 
+void RemotingRendererController::SetShowInterstitialCallback(
+    const ShowInterstitialCallback& cb) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  show_interstitial_cb_ = cb;
+  UpdateInterstitial();
+}
+
+void RemotingRendererController::UpdateInterstitial() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (show_interstitial_cb_.is_null() ||
+      pipeline_metadata_.natural_size.IsEmpty())
+    return;
+
+  RemotingInterstitialType type = RemotingInterstitialType::BETWEEN_SESSIONS;
+  switch (remoting_source_->state()) {
+    case SESSION_STARTED:
+      type = RemotingInterstitialType::IN_SESSION;
+      break;
+    case SESSION_PERMANENTLY_STOPPED:
+      type = RemotingInterstitialType::ENCRYPTED_MEDIA_FATAL_ERROR;
+      break;
+    case SESSION_UNAVAILABLE:
+    case SESSION_CAN_START:
+    case SESSION_STARTING:
+    case SESSION_STOPPING:
+      break;
+  }
+
+  // TODO(xjz): Download poster image when available.
+  show_interstitial_cb_.Run(SkBitmap(), pipeline_metadata_.natural_size, type);
+}
+
 }  // namespace media
diff --git a/media/remoting/remoting_renderer_controller.h b/media/remoting/remoting_renderer_controller.h
index c3a35bd..797e44f 100644
--- a/media/remoting/remoting_renderer_controller.h
+++ b/media/remoting/remoting_renderer_controller.h
@@ -8,6 +8,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "media/base/media_observer.h"
+#include "media/remoting/remoting_interstitial_ui.h"
 #include "media/remoting/remoting_source_impl.h"
 
 namespace media {
@@ -46,6 +47,12 @@
   void SetRemoteSinkAvailableChangedCallback(
       const base::Callback<void(bool)>& cb);
 
+  using ShowInterstitialCallback = base::Callback<
+      void(const SkBitmap&, const gfx::Size&, RemotingInterstitialType type)>;
+  // Called by RemoteRendererImpl constructor to set the callback to draw and
+  // show remoting interstial.
+  void SetShowInterstitialCallback(const ShowInterstitialCallback& cb);
+
   base::WeakPtr<RemotingRendererController> GetWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
@@ -70,11 +77,6 @@
 
   base::WeakPtr<remoting::RpcBroker> GetRpcBroker() const;
 
-  PipelineMetadata pipeline_metadata() const {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    return pipeline_metadata_;
-  }
-
  private:
   bool has_audio() const {
     return pipeline_metadata_.has_audio &&
@@ -97,6 +99,17 @@
   // necessary.
   void UpdateAndMaybeSwitch();
 
+  // Called when any of the following happens:
+  // 1. SetShowInterstitialCallback() is called (RemoteRendererImpl is
+  //    constructed);
+  // 2. The remoting session is shut down (to update the status message in the
+  //    interstitial).
+  // 3. The size of the canvas is changed (to update the background image and
+  //    the position of the status message).
+  // TODO(xjz): Call this when poster url is set/changed. Download poster image
+  // when available, and draw interstitial on it.
+  void UpdateInterstitial();
+
   // Indicates whether this media element or its ancestor is in full screen.
   bool is_fullscreen_ = false;
 
@@ -106,10 +119,6 @@
   // Indicates whether audio or video is encrypted.
   bool is_encrypted_ = false;
 
-  // Current audio/video config.
-  VideoDecoderConfig video_decoder_config_;
-  AudioDecoderConfig audio_decoder_config_;
-
   // Indicates whether remote playback is currently disabled. This starts out as
   // true, and should be updated at least once via a call to
   // OnRemotePlaybackDisabled() at some point in the future. A web page
@@ -141,6 +150,11 @@
 
   PipelineMetadata pipeline_metadata_;
 
+  // The callback to show remoting interstitial. It is set when entering the
+  // remoting mode (RemotingRendererImpl is constructed) by calling
+  // SetShowInterstitialCallback(), and is reset when leaving the remoting mode.
+  ShowInterstitialCallback show_interstitial_cb_;
+
   base::WeakPtrFactory<RemotingRendererController> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RemotingRendererController);
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 758d96c..5cf70e5 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -5,6 +5,7 @@
 #include "mojo/edk/embedder/embedder.h"
 
 #include <stdint.h>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/location.h"
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index f37d42ef..cfcb777 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -45,8 +45,8 @@
 // This is an unnecessarily large limit that is relatively easy to enforce.
 const uint32_t kMaxHandlesPerMessage = 1024 * 1024;
 
-// TODO: Maybe we could negotiate a debugging pipe ID for cross-process pipes
-// too; for now we just use a constant. This only affects bootstrap pipes.
+// TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process
+// pipes too; for now we just use a constant. This only affects bootstrap pipes.
 const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL;
 
 void CallWatchCallback(MojoWatchCallback callback,
@@ -815,12 +815,12 @@
   create_options.struct_size = sizeof(MojoCreateDataPipeOptions);
   create_options.flags = options ? options->flags : 0;
   create_options.element_num_bytes = options ? options->element_num_bytes : 1;
-  // TODO: Use Configuration to get default data pipe capacity.
+  // TODO(rockot): Use Configuration to get default data pipe capacity.
   create_options.capacity_num_bytes =
       options && options->capacity_num_bytes ? options->capacity_num_bytes
                                              : 64 * 1024;
 
-  // TODO: Broker through the parent when necessary.
+  // TODO(rockot): Broker through the parent when necessary.
   scoped_refptr<PlatformSharedBuffer> ring_buffer =
       GetNodeController()->CreateSharedBuffer(
           create_options.capacity_num_bytes);
@@ -1126,7 +1126,7 @@
                                   const MojoHandleSignals* signals,
                                   uint32_t num_handles,
                                   MojoDeadline deadline,
-                                  uint32_t *result_index,
+                                  uint32_t* result_index,
                                   HandleSignalsState* signals_states) {
   CHECK(handles);
   CHECK(signals);
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index e32e2d2..21b44b1 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -6,6 +6,7 @@
 #define MOJO_EDK_SYSTEM_CORE_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/callback.h"
@@ -39,7 +40,7 @@
 // are thread-safe.
 class MOJO_SYSTEM_IMPL_EXPORT Core {
  public:
-  explicit Core();
+  Core();
   virtual ~Core();
 
   // Called exactly once, shortly after construction, and before any other
@@ -278,7 +279,7 @@
                               const MojoHandleSignals* signals,
                               uint32_t num_handles,
                               MojoDeadline deadline,
-                              uint32_t *result_index,
+                              uint32_t* result_index,
                               HandleSignalsState* signals_states);
 
   // Used to pass ownership of our NodeController over to the IO thread in the
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index aa19c606..11bb405 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -443,7 +443,8 @@
     }
 
     char message[128];
-    sprintf(message, "hello %d", static_cast<int>(pipe_count));
+    snprintf(message, sizeof(message), "hello %d",
+             static_cast<int>(pipe_count));
     ASSERT_EQ(MOJO_RESULT_OK,
               MojoWriteMessage(h, message,
                                static_cast<uint32_t>(strlen(message)),
@@ -465,9 +466,9 @@
 #if !defined(OS_ANDROID)
 INSTANTIATE_TEST_CASE_P(PipeCount,
                         MultiprocessMessagePipeTestWithPipeCount,
-                        // TODO: Re-enable the 140-pipe case when ChannelPosix
-                        // has support for sending lots of handles.
-                        testing::Values(1u, 128u/*, 140u*/));
+                        // TODO(rockot): Re-enable the 140-pipe case when
+                        // ChannelPosix has support for sending lots of handles.
+                        testing::Values(1u, 128u /*, 140u*/));
 #endif
 
 DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
@@ -997,7 +998,7 @@
     }
   }
 
-  for (auto& pipe: named_pipes)
+  for (auto& pipe : named_pipes)
     CloseHandle(pipe.second);
 
   return 0;
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc
index cd0941a5..43a99db 100644
--- a/net/quic/chromium/quic_test_packet_maker.cc
+++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -641,7 +641,7 @@
     bool should_include_version,
     QuicStreamOffset* offset) {
   SpdySettingsIR settings_frame;
-  settings_frame.AddSetting(id, false, false, value);
+  settings_frame.AddSetting(id, value);
   SpdySerializedFrame spdy_frame(
       spdy_request_framer_.SerializeFrame(settings_frame));
   InitializeHeader(packet_number, should_include_version);
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 6c162fd5..0ea432a 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -168,7 +168,7 @@
     CloseConnection("SPDY RST_STREAM frame received.");
   }
 
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {
+  void OnSetting(SpdySettingsIds id, uint32_t value) override {
     if (!FLAGS_quic_respect_http2_settings_frame) {
       CloseConnection("SPDY SETTINGS frame received.");
       return;
@@ -590,7 +590,7 @@
 
 size_t QuicHeadersStream::SendMaxHeaderListSize(size_t value) {
   SpdySettingsIR settings_frame;
-  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false, value);
+  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value);
 
   SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame));
   WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, nullptr);
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index 2feb739..73ca0a93 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -80,8 +80,7 @@
   MOCK_METHOD2(OnRstStream,
                void(SpdyStreamId stream_id, SpdyRstStreamStatus status));
   MOCK_METHOD1(OnSettings, void(bool clear_persisted));
-  MOCK_METHOD3(OnSetting,
-               void(SpdySettingsIds id, uint8_t flags, uint32_t value));
+  MOCK_METHOD2(OnSetting, void(SpdySettingsIds id, uint32_t value));
   MOCK_METHOD0(OnSettingsAck, void());
   MOCK_METHOD0(OnSettingsEnd, void());
   MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
@@ -539,7 +538,7 @@
   session_.OnConfigNegotiated();
   SpdySettingsIR data;
   // Respect supported settings frames SETTINGS_ENABLE_PUSH.
-  data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 0);
+  data.AddSetting(SETTINGS_ENABLE_PUSH, 0);
   SpdySerializedFrame frame(framer_->SerializeFrame(data));
   stream_frame_.data_buffer = frame.data();
   stream_frame_.data_length = frame.size();
@@ -734,7 +733,7 @@
 TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) {
   FLAGS_quic_respect_http2_settings_frame = false;
   SpdySettingsIR data;
-  data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, 0);
+  data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 0);
   SpdySerializedFrame frame(framer_->SerializeFrame(data));
   EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
                                             "SPDY SETTINGS frame received.", _))
@@ -752,8 +751,8 @@
   SpdySettingsIR data;
   // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE,
   // SETTINGS_MAX_HEADER_LIST_SIZE.
-  data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, kTestHeaderTableSize);
-  data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, true, true, 2000);
+  data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, kTestHeaderTableSize);
+  data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, 2000);
   SpdySerializedFrame frame(framer_->SerializeFrame(data));
   stream_frame_.data_buffer = frame.data();
   stream_frame_.data_length = frame.size();
@@ -770,10 +769,10 @@
   // Does not support SETTINGS_MAX_CONCURRENT_STREAMS,
   // SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and
   // SETTINGS_MAX_FRAME_SIZE.
-  data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, true, true, 100);
-  data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 100);
-  data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 1);
-  data.AddSetting(SETTINGS_MAX_FRAME_SIZE, true, true, 1250);
+  data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100);
+  data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 100);
+  data.AddSetting(SETTINGS_ENABLE_PUSH, 1);
+  data.AddSetting(SETTINGS_MAX_FRAME_SIZE, 1250);
   SpdySerializedFrame frame(framer_->SerializeFrame(data));
   EXPECT_CALL(
       *connection_,
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index a1479c0b..c8fa123e 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -130,10 +130,8 @@
   visitor_->OnSettings();
 }
 
-void BufferedSpdyFramer::OnSetting(SpdySettingsIds id,
-                                   uint8_t flags,
-                                   uint32_t value) {
-  visitor_->OnSetting(id, flags, value);
+void BufferedSpdyFramer::OnSetting(SpdySettingsIds id, uint32_t value) {
+  visitor_->OnSetting(id, value);
 }
 
 void BufferedSpdyFramer::OnSettingsAck() {
@@ -251,14 +249,9 @@
 SpdySerializedFrame* BufferedSpdyFramer::CreateSettings(
     const SettingsMap& values) const {
   SpdySettingsIR settings_ir;
-  for (SettingsMap::const_iterator it = values.begin();
-       it != values.end();
+  for (SettingsMap::const_iterator it = values.begin(); it != values.end();
        ++it) {
-    settings_ir.AddSetting(
-        it->first,
-        (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
-        (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
-        it->second.second);
+    settings_ir.AddSetting(it->first, it->second);
   }
   return new SpdySerializedFrame(spdy_framer_.SerializeSettings(settings_ir));
 }
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h
index f6cb978..8f5b1ec 100644
--- a/net/spdy/buffered_spdy_framer.h
+++ b/net/spdy/buffered_spdy_framer.h
@@ -68,7 +68,7 @@
 
   // Called when an individual setting within a SETTINGS frame has been parsed
   // and validated.
-  virtual void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) = 0;
+  virtual void OnSetting(SpdySettingsIds id, uint32_t value) = 0;
 
   // Called when a SETTINGS frame is received with the ACK flag set.
   virtual void OnSettingsAck() {}
@@ -151,7 +151,7 @@
       SpdyStreamId stream_id) override;
   void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override;
   void OnSettings(bool clear_persisted) override;
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
+  void OnSetting(SpdySettingsIds id, uint32_t value) override;
   void OnSettingsAck() override;
   void OnSettingsEnd() override;
   void OnPing(SpdyPingId unique_id, bool is_ack) override;
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index 82a6637..a5bf1c1f 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "net/spdy/buffered_spdy_framer.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/logging.h"
@@ -16,7 +17,7 @@
 
 class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
  public:
-  explicit TestBufferedSpdyVisitor()
+  TestBufferedSpdyVisitor()
       : buffered_spdy_framer_(),
         error_count_(0),
         setting_count_(0),
@@ -74,7 +75,7 @@
 
   void OnSettings() override {}
 
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {
+  void OnSetting(SpdySettingsIds id, uint32_t value) override {
     setting_count_++;
   }
 
@@ -181,8 +182,8 @@
 TEST_F(BufferedSpdyFramerTest, OnSetting) {
   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
-  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2);
-  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3);
+  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 2);
+  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 3);
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   TestBufferedSpdyVisitor visitor;
 
diff --git a/net/spdy/hpack/hpack_encoder.cc b/net/spdy/hpack/hpack_encoder.cc
index f186b6a4..0ec5e8f 100644
--- a/net/spdy/hpack/hpack_encoder.cc
+++ b/net/spdy/hpack/hpack_encoder.cc
@@ -157,17 +157,22 @@
 }
 
 void HpackEncoder::EmitIndex(const HpackEntry* entry) {
+  DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry);
   output_stream_.AppendPrefix(kIndexedOpcode);
   output_stream_.AppendUint32(header_table_.IndexOf(entry));
 }
 
 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
+  DVLOG(2) << "Emitting indexed literal: (" << representation.first << ", "
+           << representation.second << ")";
   output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
   EmitLiteral(representation);
   header_table_.TryAddEntry(representation.first, representation.second);
 }
 
 void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) {
+  DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", "
+           << representation.second << ")";
   output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
   output_stream_.AppendUint32(0);
   EmitString(representation.first);
@@ -189,10 +194,12 @@
   size_t encoded_size =
       enable_compression_ ? huffman_table_.EncodedSize(str) : str.size();
   if (encoded_size < str.size()) {
+    DVLOG(2) << "Emitted Huffman-encoded string of length " << encoded_size;
     output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
     output_stream_.AppendUint32(encoded_size);
     huffman_table_.EncodeString(str, &output_stream_);
   } else {
+    DVLOG(2) << "Emitted literal string of length " << str.size();
     output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
     output_stream_.AppendUint32(str.size());
     output_stream_.AppendBytes(str);
diff --git a/net/spdy/hpack/hpack_entry.cc b/net/spdy/hpack/hpack_entry.cc
index 50bb718..9a47079 100644
--- a/net/spdy/hpack/hpack_entry.cc
+++ b/net/spdy/hpack/hpack_entry.cc
@@ -37,8 +37,8 @@
   } else {
     name_ = other.name_;
     value_ = other.value_;
-    name_ref_.set(name_.data(), name_.size());
-    value_ref_.set(value_.data(), value_.size());
+    name_ref_ = StringPiece(name_.data(), name_.size());
+    value_ref_ = StringPiece(value_.data(), value_.size());
   }
 }
 
@@ -52,8 +52,8 @@
   }
   name_ = other.name_;
   value_ = other.value_;
-  name_ref_.set(name_.data(), name_.size());
-  value_ref_.set(value_.data(), value_.size());
+  name_ref_ = StringPiece(name_.data(), name_.size());
+  value_ref_ = StringPiece(value_.data(), value_.size());
   return *this;
 }
 
diff --git a/net/spdy/http2_frame_decoder_adapter.cc b/net/spdy/http2_frame_decoder_adapter.cc
index 756a1ee..5153a9d 100644
--- a/net/spdy/http2_frame_decoder_adapter.cc
+++ b/net/spdy/http2_frame_decoder_adapter.cc
@@ -380,7 +380,7 @@
       DVLOG(1) << "Ignoring invalid setting id: " << setting_fields;
       return;
     }
-    visitor()->OnSetting(setting_id, 0, setting_fields.value);
+    visitor()->OnSetting(setting_id, setting_fields.value);
   }
 
   void OnSettingsEnd() override {
diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h
index 7f33c1dd1..4f48ed0 100644
--- a/net/spdy/mock_spdy_framer_visitor.h
+++ b/net/spdy/mock_spdy_framer_visitor.h
@@ -37,8 +37,7 @@
   MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
                                  SpdyRstStreamStatus status));
   MOCK_METHOD1(OnSettings, void(bool clear_persisted));
-  MOCK_METHOD3(OnSetting,
-               void(SpdySettingsIds id, uint8_t flags, uint32_t value));
+  MOCK_METHOD2(OnSetting, void(SpdySettingsIds id, uint32_t value));
   MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
   MOCK_METHOD0(OnSettingsEnd, void());
   MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
diff --git a/net/spdy/spdy_deframer_visitor.cc b/net/spdy/spdy_deframer_visitor.cc
index 40ff7f52..15ff32c 100644
--- a/net/spdy/spdy_deframer_visitor.cc
+++ b/net/spdy/spdy_deframer_visitor.cc
@@ -169,7 +169,7 @@
                      bool end) override;
   void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override;
   bool OnRstStreamFrameData(const char* rst_stream_data, size_t len) override;
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
+  void OnSetting(SpdySettingsIds id, uint32_t value) override;
   void OnSettings(bool clear_persisted) override;
   void OnSettingsAck() override;
   void OnSettingsEnd() override;
@@ -636,16 +636,13 @@
 
 // Called for an individual setting. There is no negotiation, the sender is
 // stating the value that the sender is using.
-void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id,
-                                     uint8_t flags,
-                                     uint32_t value) {
-  DVLOG(1) << "OnSetting id: " << id << std::hex << "   flags: " << flags
-           << "    value: " << value;
+void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id, uint32_t value) {
+  DVLOG(1) << "OnSetting id: " << id << std::hex << "    value: " << value;
   CHECK_EQ(frame_type_, SETTINGS) << "   frame_type_="
                                   << Http2FrameTypeToString(frame_type_);
   CHECK(settings_);
   settings_->push_back(std::make_pair(id, value));
-  settings_ir_->AddSetting(id, true, true, value);
+  settings_ir_->AddSetting(id, value);
 }
 
 // Called at the start of a SETTINGS frame with setting entries, but not the
diff --git a/net/spdy/spdy_deframer_visitor_test.cc b/net/spdy/spdy_deframer_visitor_test.cc
index 4652049..f2dc4c74 100644
--- a/net/spdy/spdy_deframer_visitor_test.cc
+++ b/net/spdy/spdy_deframer_visitor_test.cc
@@ -228,7 +228,7 @@
   ASSERT_NE(cf0.frame_ir, nullptr);
 
   SpdySettingsIR expected_ir;
-  expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 255);
+  expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 255);
   EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
 
   SettingVector expected_settings;
@@ -241,7 +241,7 @@
   expected_settings.push_back({SETTINGS_INITIAL_WINDOW_SIZE, 65536});
   EXPECT_FALSE(cf0.VerifyHasSettings(expected_settings));
 
-  expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 65536);
+  expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 65536);
   EXPECT_FALSE(cf0.VerifyHasFrame(expected_ir));
 
   SpdySettingsIR unexpected_ir;
diff --git a/net/spdy/spdy_frame_reader.cc b/net/spdy/spdy_frame_reader.cc
index 4727caa..7e9ded5 100644
--- a/net/spdy/spdy_frame_reader.cc
+++ b/net/spdy/spdy_frame_reader.cc
@@ -10,6 +10,8 @@
 
 namespace net {
 
+using base::StringPiece;
+
 SpdyFrameReader::SpdyFrameReader(const char* data, const size_t len)
     : data_(data),
       len_(len),
@@ -115,7 +117,7 @@
   return true;
 }
 
-bool SpdyFrameReader::ReadStringPiece16(base::StringPiece* result) {
+bool SpdyFrameReader::ReadStringPiece16(StringPiece* result) {
   // Read resultant length.
   uint16_t result_len;
   if (!ReadUInt16(&result_len)) {
@@ -130,7 +132,7 @@
   }
 
   // Set result.
-  result->set(data_ + ofs_, result_len);
+  *result = StringPiece(data_ + ofs_, result_len);
 
   // Iterate.
   ofs_ += result_len;
@@ -138,7 +140,7 @@
   return true;
 }
 
-bool SpdyFrameReader::ReadStringPiece32(base::StringPiece* result) {
+bool SpdyFrameReader::ReadStringPiece32(StringPiece* result) {
   // Read resultant length.
   uint32_t result_len;
   if (!ReadUInt32(&result_len)) {
@@ -153,7 +155,7 @@
   }
 
   // Set result.
-  result->set(data_ + ofs_, result_len);
+  *result = StringPiece(data_ + ofs_, result_len);
 
   // Iterate.
   ofs_ += result_len;
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 9f5140d..da6b953 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -128,20 +128,6 @@
   } while (false)
 #endif
 
-SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(uint32_t wire) {
-  return SettingsFlagsAndId(base::NetToHost32(wire) >> 24,
-                            base::NetToHost32(wire) & 0x00ffffff);
-}
-
-SettingsFlagsAndId::SettingsFlagsAndId(uint8_t flags, uint32_t id)
-    : flags_(flags), id_(id & 0x00ffffff) {
-  SPDY_BUG_IF(id > (1u << 24)) << "HTTP2 setting ID too large: " << id;
-}
-
-uint32_t SettingsFlagsAndId::GetWireFormat() const {
-  return base::HostToNet32(id_ & 0x00ffffff) | base::HostToNet32(flags_ << 24);
-}
-
 bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data,
                                                    size_t len) {
   return true;
@@ -252,8 +238,7 @@
   return SpdyConstants::kFrameHeaderSize;
 }
 
-// TODO(jamessynge): Rename this to GetRstStreamSize as the frame is fixed size.
-size_t SpdyFramer::GetRstStreamMinimumSize() const {
+size_t SpdyFramer::GetRstStreamSize() const {
   // Size, in bytes, of a RST_STREAM frame.
   // Calculated as:
   // frame prefix + 4 (status code)
@@ -826,7 +811,7 @@
   // Do some sanity checking on the control frame sizes and flags.
   switch (current_frame_type_) {
     case RST_STREAM:
-      if (current_frame_length_ != GetRstStreamMinimumSize()) {
+      if (current_frame_length_ != GetRstStreamSize()) {
         set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
       } else if (current_frame_flags_ != 0) {
         VLOG(1) << "Undefined frame flags for RST_STREAM frame: " << hex
@@ -1380,7 +1365,7 @@
   }
 
   // Validation succeeded. Pass on to visitor.
-  visitor_->OnSetting(setting_id, /*flags=*/0, value);
+  visitor_->OnSetting(setting_id, value);
   return true;
 }
 
@@ -1523,7 +1508,7 @@
 
   // Check if we had already read enough bytes to parse the fixed-length portion
   // of the RST_STREAM frame.
-  const size_t header_size = GetRstStreamMinimumSize();
+  const size_t header_size = GetRstStreamSize();
   size_t unread_header_bytes = header_size - current_frame_buffer_.len();
   bool already_parsed_header = (unread_header_bytes == 0);
   if (!already_parsed_header) {
@@ -1888,12 +1873,7 @@
 
 SpdySerializedFrame SpdyFramer::SerializeRstStream(
     const SpdyRstStreamIR& rst_stream) const {
-  // TODO(jgraettinger): For now, Chromium will support parsing RST_STREAM
-  // payloads, but will not emit them. This is used for draft HTTP/2,
-  // which doesn't currently include RST_STREAM payloads. GFE flags have been
-  // commented but left in place to simplify future patching.
-  // Compute the output buffer size, taking opaque data into account.
-  size_t expected_length = GetRstStreamMinimumSize();
+  size_t expected_length = GetRstStreamSize();
   SpdyFrameBuilder builder(expected_length);
 
   builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id());
@@ -1912,7 +1892,7 @@
   if (settings.is_ack()) {
     flags |= SETTINGS_FLAG_ACK;
   }
-  const SpdySettingsIR::ValueMap* values = &(settings.values());
+  const SettingsMap* values = &(settings.values());
 
   int setting_size = 6;
   // Size, in bytes, of this SETTINGS frame.
@@ -1927,12 +1907,12 @@
   }
 
   DCHECK_EQ(GetSettingsMinimumSize(), builder.length());
-  for (SpdySettingsIR::ValueMap::const_iterator it = values->begin();
-       it != values->end(); ++it) {
+  for (SettingsMap::const_iterator it = values->begin(); it != values->end();
+       ++it) {
     int setting_id = it->first;
     DCHECK_GE(setting_id, 0);
     builder.WriteUInt16(static_cast<uint16_t>(setting_id));
-    builder.WriteUInt32(it->second.value);
+    builder.WriteUInt32(it->second);
   }
   DCHECK_EQ(size, builder.length());
   return builder.take();
@@ -2253,6 +2233,89 @@
   SpdySerializedFrame frame_;
 };
 
+// TODO(diannahu): Use also in frame serialization.
+class FlagsSerializationVisitor : public SpdyFrameVisitor {
+ public:
+  void VisitData(const SpdyDataIR& data) override {
+    flags_ = DATA_FLAG_NONE;
+    if (data.fin()) {
+      flags_ |= DATA_FLAG_FIN;
+    }
+    if (data.padded()) {
+      flags_ |= DATA_FLAG_PADDED;
+    }
+  }
+
+  void VisitRstStream(const SpdyRstStreamIR& rst_stream) override {
+    flags_ = kNoFlags;
+  }
+
+  void VisitSettings(const SpdySettingsIR& settings) override {
+    flags_ = kNoFlags;
+    if (settings.is_ack()) {
+      flags_ |= SETTINGS_FLAG_ACK;
+    }
+  }
+
+  void VisitPing(const SpdyPingIR& ping) override {
+    flags_ = kNoFlags;
+    if (ping.is_ack()) {
+      flags_ |= PING_FLAG_ACK;
+    }
+  }
+
+  void VisitGoAway(const SpdyGoAwayIR& goaway) override { flags_ = kNoFlags; }
+
+  // TODO(diannahu): The END_HEADERS flag is incorrect for HEADERS that require
+  //     CONTINUATION frames.
+  void VisitHeaders(const SpdyHeadersIR& headers) override {
+    flags_ = HEADERS_FLAG_END_HEADERS;
+    if (headers.fin()) {
+      flags_ |= CONTROL_FLAG_FIN;
+    }
+    if (headers.padded()) {
+      flags_ |= HEADERS_FLAG_PADDED;
+    }
+    if (headers.has_priority()) {
+      flags_ |= HEADERS_FLAG_PRIORITY;
+    }
+  }
+
+  void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override {
+    flags_ = kNoFlags;
+  }
+
+  void VisitBlocked(const SpdyBlockedIR& blocked) override {
+    flags_ = kNoFlags;
+  }
+
+  // TODO(diannahu): The END_PUSH_PROMISE flag is incorrect for PUSH_PROMISEs
+  //     that require CONTINUATION frames.
+  void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override {
+    flags_ = PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
+    if (push_promise.padded()) {
+      flags_ |= PUSH_PROMISE_FLAG_PADDED;
+    }
+  }
+
+  // TODO(diannahu): The END_HEADERS flag is incorrect for CONTINUATIONs that
+  //     require CONTINUATION frames.
+  void VisitContinuation(const SpdyContinuationIR& continuation) override {
+    flags_ = HEADERS_FLAG_END_HEADERS;
+  }
+
+  void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { flags_ = kNoFlags; }
+
+  void VisitPriority(const SpdyPriorityIR& priority) override {
+    flags_ = kNoFlags;
+  }
+
+  uint8_t flags() const { return flags_; }
+
+ private:
+  uint8_t flags_ = kNoFlags;
+};
+
 }  // namespace
 
 SpdySerializedFrame SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) {
@@ -2261,6 +2324,12 @@
   return visitor.ReleaseSerializedFrame();
 }
 
+uint8_t SpdyFramer::GetSerializedFlags(const SpdyFrameIR& frame) {
+  FlagsSerializationVisitor visitor;
+  frame.Visit(&visitor);
+  return visitor.flags();
+}
+
 size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) {
   DCHECK_GT(size, kMaxControlFrameSize);
   size_t overflow = size - kMaxControlFrameSize;
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 79f66a5..fff856a30 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -47,31 +47,6 @@
 
 }  // namespace test
 
-// A datastructure for holding the ID and flag fields for SETTINGS.
-// Conveniently handles converstion to/from wire format.
-class NET_EXPORT_PRIVATE SettingsFlagsAndId {
- public:
-  static SettingsFlagsAndId FromWireFormat(uint32_t wire);
-
-  SettingsFlagsAndId() : flags_(0), id_(0) {}
-
-  // TODO(hkhalil): restrict to enums instead of free-form ints.
-  SettingsFlagsAndId(uint8_t flags, uint32_t id);
-
-  uint32_t GetWireFormat() const;
-
-  uint32_t id() const { return id_; }
-  uint8_t flags() const { return flags_; }
-
- private:
-  uint8_t flags_;
-  uint32_t id_;
-};
-
-// SettingsMap has unique (flags, value) pair for given SpdySettingsIds ID.
-typedef std::pair<SpdySettingsFlags, uint32_t> SettingsFlagsAndValue;
-typedef std::map<SpdySettingsIds, SettingsFlagsAndValue> SettingsMap;
-
 // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
 // Implement this interface to receive event callbacks as frames are
 // decoded from the framer.
@@ -150,7 +125,7 @@
 
   // Called when a complete setting within a SETTINGS frame has been parsed and
   // validated.
-  virtual void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) = 0;
+  virtual void OnSetting(SpdySettingsIds id, uint32_t value) = 0;
 
   // Called when a SETTINGS frame is received with the ACK flag set.
   virtual void OnSettingsAck() {}
@@ -343,6 +318,9 @@
   // Retrieve serialized length of SpdyHeaderBlock.
   static size_t GetSerializedLength(const SpdyHeaderBlock* headers);
 
+  // Gets the serialized flags for the given |frame|.
+  static uint8_t GetSerializedFlags(const SpdyFrameIR& frame);
+
   explicit SpdyFramer(CompressionOption option);
 
   // Used recursively from the above constructor in order to support
@@ -492,7 +470,7 @@
   // Returns the (minimum) size of frames (sans variable-length portions).
   size_t GetDataFrameMinimumSize() const;
   size_t GetFrameHeaderSize() const;
-  size_t GetRstStreamMinimumSize() const;
+  size_t GetRstStreamSize() const;
   size_t GetSettingsMinimumSize() const;
   size_t GetPingSize() const;
   size_t GetGoAwayMinimumSize() const;
diff --git a/net/spdy/spdy_framer_decoder_adapter.cc b/net/spdy/spdy_framer_decoder_adapter.cc
index b68f04a..3e7a7fe 100644
--- a/net/spdy/spdy_framer_decoder_adapter.cc
+++ b/net/spdy/spdy_framer_decoder_adapter.cc
@@ -92,9 +92,8 @@
 }
 
 void SpdyFramerVisitorAdapter::OnSetting(SpdySettingsIds id,
-                                         uint8_t flags,
                                          uint32_t value) {
-  visitor_->OnSetting(id, flags, value);
+  visitor_->OnSetting(id, value);
 }
 
 void SpdyFramerVisitorAdapter::OnPing(SpdyPingId unique_id, bool is_ack) {
diff --git a/net/spdy/spdy_framer_decoder_adapter.h b/net/spdy/spdy_framer_decoder_adapter.h
index 20b70f6..59a33d09a 100644
--- a/net/spdy/spdy_framer_decoder_adapter.h
+++ b/net/spdy/spdy_framer_decoder_adapter.h
@@ -116,7 +116,7 @@
       SpdyStreamId stream_id) override;
   void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override;
   void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override;
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
+  void OnSetting(SpdySettingsIds id, uint32_t value) override;
   void OnPing(SpdyPingId unique_id, bool is_ack) override;
   void OnSettings(bool clear_persisted) override;
   void OnSettingsAck() override;
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 4b0333b..c3eb9a9c7 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -151,9 +151,7 @@
                      SpdyRstStreamStatus status) override {
       LOG(FATAL);
     }
-    void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {
-      LOG(FATAL);
-    }
+    void OnSetting(SpdySettingsIds id, uint32_t value) override { LOG(FATAL); }
     void OnPing(SpdyPingId unique_id, bool is_ack) override { LOG(FATAL); }
     void OnSettingsEnd() override { LOG(FATAL); }
     void OnGoAway(SpdyStreamId last_accepted_stream_id,
@@ -373,9 +371,8 @@
     return true;
   }
 
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {
-    VLOG(1) << "OnSetting(" << id << ", " << std::hex << flags << ", " << value
-            << ")";
+  void OnSetting(SpdySettingsIds id, uint32_t value) override {
+    VLOG(1) << "OnSetting(" << id << ", " << std::hex << ", " << value << ")";
     ++setting_count_;
   }
 
@@ -1692,10 +1689,8 @@
     uint32_t kValue = 0x0a0b0c0d;
     SpdySettingsIR settings_ir;
 
-    SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
     SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE;
-    settings_ir.AddSetting(kId, kFlags & SETTINGS_FLAG_PLEASE_PERSIST,
-                           kFlags & SETTINGS_FLAG_PERSISTED, kValue);
+    settings_ir.AddSetting(kId, kValue);
 
     SpdySerializedFrame frame(framer.SerializeSettings(settings_ir));
     CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData));
@@ -1722,22 +1717,10 @@
     };
 
     SpdySettingsIR settings_ir;
-    settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE,
-                           false,  // persist
-                           false,  // persisted
-                           5);
-    settings_ir.AddSetting(SETTINGS_ENABLE_PUSH,
-                           false,  // persist
-                           false,  // persisted
-                           6);
-    settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS,
-                           false,  // persist
-                           false,  // persisted
-                           7);
-    settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE,
-                           false,  // persist
-                           false,  // persisted
-                           8);
+    settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5);
+    settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6);
+    settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7);
+    settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 8);
     SpdySerializedFrame frame(framer.SerializeSettings(settings_ir));
 
     CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData));
@@ -2776,10 +2759,8 @@
   // overflow when calling SimulateInFramer() below.  These settings must be
   // distinct parameters because SpdySettingsIR has a map for settings, and will
   // collapse multiple copies of the same parameter.
-  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false,
-                         0x00000002);
-  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false,
-                         0x00000002);
+  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0x00000002);
+  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0x00000002);
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   const size_t kNewLength = 8;
   SetFrameLength(&control_frame, kNewLength);
@@ -2799,18 +2780,9 @@
 TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
-  settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE,
-                         false,  // persist
-                         false,  // persisted
-                         5);
-  settings_ir.AddSetting(SETTINGS_ENABLE_PUSH,
-                         false,  // persist
-                         false,  // persisted
-                         6);
-  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS,
-                         false,  // persist
-                         false,  // persisted
-                         7);
+  settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5);
+  settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6);
+  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7);
 
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   EXPECT_LT(SpdyFramerPeer::ControlFrameBufferSize(), control_frame.size());
@@ -3373,10 +3345,7 @@
   // Follow it up with a valid control frame to make sure we handle
   // subsequent frames correctly.
   SpdySettingsIR settings_ir;
-  settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE,
-                         false,  // persist
-                         false,  // persisted
-                         10);
+  settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 10);
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
@@ -3424,7 +3393,7 @@
   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   EXPECT_EQ(9u, framer.GetDataFrameMinimumSize());
   EXPECT_EQ(9u, framer.GetFrameHeaderSize());
-  EXPECT_EQ(13u, framer.GetRstStreamMinimumSize());
+  EXPECT_EQ(13u, framer.GetRstStreamSize());
   EXPECT_EQ(9u, framer.GetSettingsMinimumSize());
   EXPECT_EQ(17u, framer.GetPingSize());
   EXPECT_EQ(17u, framer.GetGoAwayMinimumSize());
@@ -3623,7 +3592,7 @@
     framer.set_visitor(&visitor);
 
     SpdySettingsIR settings_ir;
-    settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 0, 16);
+    settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16);
     SpdySerializedFrame frame(framer.SerializeSettings(settings_ir));
     SetFrameFlags(&frame, flags);
 
@@ -3631,7 +3600,7 @@
       EXPECT_CALL(visitor, OnError(_));
     } else {
       EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK));
-      EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 16));
+      EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16));
       EXPECT_CALL(visitor, OnSettingsEnd());
     }
 
@@ -3865,18 +3834,6 @@
 
 // TODO(hkhalil): Add TEST_F(SpdyFramerTest, BlockedFrameFlags)
 
-TEST_P(SpdyFramerTest, SettingsFlagsAndId) {
-  const uint32_t kId = 0x020304;
-  const uint32_t kFlags = 0x01;
-  const uint32_t kWireFormat = base::HostToNet32(0x01020304);
-
-  SettingsFlagsAndId id_and_flags =
-      SettingsFlagsAndId::FromWireFormat(kWireFormat);
-  EXPECT_EQ(kId, id_and_flags.id());
-  EXPECT_EQ(kFlags, id_and_flags.flags());
-  EXPECT_EQ(kWireFormat, id_and_flags.GetWireFormat());
-}
-
 // Test handling of a RST_STREAM with out-of-bounds status codes.
 TEST_P(SpdyFramerTest, RstStreamStatusBounds) {
   const unsigned char kH2RstStreamInvalid[] = {
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index f070219..939e0c0 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -930,8 +930,7 @@
 
   SettingsMap settings;
   const uint32_t max_concurrent_streams = 1;
-  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(settings));
   SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
@@ -1061,8 +1060,7 @@
 
   SettingsMap settings;
   const uint32_t max_concurrent_streams = 1;
-  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(settings));
   SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
@@ -1195,8 +1193,7 @@
 
   SettingsMap settings;
   const uint32_t max_concurrent_streams = 1;
-  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(settings));
   SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
@@ -1320,8 +1317,7 @@
 
   SettingsMap settings;
   const uint32_t max_concurrent_streams = 1;
-  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(settings));
   SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
@@ -5322,12 +5318,10 @@
       stream_max_recv_window_size / 2 + kChunkSize;
 
   SettingsMap initial_settings;
-  initial_settings[SETTINGS_HEADER_TABLE_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize);
+  initial_settings[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize;
   initial_settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams);
-  initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, stream_max_recv_window_size);
+      kMaxConcurrentPushedStreams;
+  initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size;
   SpdySerializedFrame initial_settings_frame(
       spdy_util_.ConstructSpdySettings(initial_settings));
   SpdySerializedFrame initial_window_update(
@@ -5722,8 +5716,7 @@
   // Construct read frame for SETTINGS that gives enough space to upload the
   // rest of the data.
   SettingsMap settings;
-  settings[SETTINGS_INITIAL_WINDOW_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, initial_window_size * 2);
+  settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size * 2;
   SpdySerializedFrame settings_frame_large(
       spdy_util_.ConstructSpdySettings(settings));
 
@@ -5884,8 +5877,7 @@
   // Construct read frame for SETTINGS that makes the send_window_size
   // negative.
   SettingsMap new_settings;
-  new_settings[SETTINGS_INITIAL_WINDOW_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, initial_window_size / 2);
+  new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size / 2;
   SpdySerializedFrame settings_frame_small(
       spdy_util_.ConstructSpdySettings(new_settings));
   // Construct read frames for WINDOW_UPDATE that makes the send_window_size
diff --git a/net/spdy/spdy_no_op_visitor.h b/net/spdy/spdy_no_op_visitor.h
index 573d813..cd3d495 100644
--- a/net/spdy/spdy_no_op_visitor.h
+++ b/net/spdy/spdy_no_op_visitor.h
@@ -37,7 +37,7 @@
   void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {}
   void OnRstStream(SpdyStreamId stream_id,
                    SpdyRstStreamStatus status) override {}
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {}
+  void OnSetting(SpdySettingsIds id, uint32_t value) override {}
   void OnPing(SpdyPingId unique_id, bool is_ack) override {}
   void OnSettingsEnd() override {}
   void OnSettingsAck() override {}
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc
index cd773394..6993100 100644
--- a/net/spdy/spdy_protocol.cc
+++ b/net/spdy/spdy_protocol.cc
@@ -450,9 +450,7 @@
   return visitor->VisitRstStream(*this);
 }
 
-SpdySettingsIR::SpdySettingsIR()
-    : clear_settings_(false),
-      is_ack_(false) {}
+SpdySettingsIR::SpdySettingsIR() : is_ack_(false) {}
 
 SpdySettingsIR::~SpdySettingsIR() {}
 
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index bbd959a..0f29e6eb 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -130,13 +130,6 @@
   SETTINGS_FLAG_ACK = 0x01,
 };
 
-// Flags for settings within a SETTINGS frame.
-enum SpdySettingsFlags {
-  SETTINGS_FLAG_NONE = 0x00,
-  SETTINGS_FLAG_PLEASE_PERSIST = 0x01,
-  SETTINGS_FLAG_PERSISTED = 0x02,
-};
-
 enum SpdySettingsIds {
   // HPACK header table maximum size.
   SETTINGS_HEADER_TABLE_SIZE = 0x1,
@@ -154,6 +147,8 @@
   SETTINGS_MAX = SETTINGS_MAX_HEADER_LIST_SIZE
 };
 
+using SettingsMap = std::map<SpdySettingsIds, uint32_t>;
+
 // Status codes for RST_STREAM frames.
 enum SpdyRstStreamStatus {
   RST_STREAM_NO_ERROR = 0,
@@ -592,33 +587,13 @@
 
 class NET_EXPORT_PRIVATE SpdySettingsIR : public SpdyFrameIR {
  public:
-  // Associates flags with a value.
-  struct Value {
-    Value() : persist_value(false),
-              persisted(false),
-              value(0) {}
-    bool persist_value;
-    bool persisted;
-    int32_t value;
-  };
-  typedef std::map<SpdySettingsIds, Value> ValueMap;
-
   SpdySettingsIR();
-
   ~SpdySettingsIR() override;
 
   // Overwrites as appropriate.
-  const ValueMap& values() const { return values_; }
-  void AddSetting(SpdySettingsIds id,
-                  bool persist_value,
-                  bool persisted,
-                  int32_t value) {
-    values_[id].persist_value = persist_value;
-    values_[id].persisted = persisted;
-    values_[id].value = value;
-  }
+  const SettingsMap& values() const { return values_; }
+  void AddSetting(SpdySettingsIds id, int32_t value) { values_[id] = value; }
 
-  bool clear_settings() const { return clear_settings_; }
   bool is_ack() const { return is_ack_; }
   void set_is_ack(bool is_ack) {
     is_ack_ = is_ack;
@@ -627,8 +602,7 @@
   void Visit(SpdyFrameVisitor* visitor) const override;
 
  private:
-  ValueMap values_;
-  bool clear_settings_;
+  SettingsMap values_;
   bool is_ack_;
 
   DISALLOW_COPY_AND_ASSIGN(SpdySettingsIR);
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc
index 4be43d5c..4013600 100644
--- a/net/spdy/spdy_protocol_test.cc
+++ b/net/spdy/spdy_protocol_test.cc
@@ -33,18 +33,6 @@
 
 namespace test {
 
-TEST(SpdyProtocolDeathTest, TestSpdySettingsAndIdOutOfBounds) {
-  std::unique_ptr<SettingsFlagsAndId> flags_and_id;
-
-  EXPECT_SPDY_BUG(flags_and_id.reset(new SettingsFlagsAndId(1, 0xffffffff)),
-                  "HTTP2 setting ID too large.");
-  // Make sure that we get expected values in opt mode.
-  if (flags_and_id.get() != nullptr) {
-    EXPECT_EQ(1, flags_and_id->flags());
-    EXPECT_EQ(0xffffffu, flags_and_id->id());
-  }
-}
-
 TEST(SpdyProtocolTest, IsValidHTTP2FrameStreamId) {
   // Stream-specific frames must have non-zero stream ids
   EXPECT_TRUE(SpdyConstants::IsValidHTTP2FrameStreamId(1, DATA));
diff --git a/net/spdy/spdy_protocol_test_utils.cc b/net/spdy/spdy_protocol_test_utils.cc
index dd600ded..73cc067 100644
--- a/net/spdy/spdy_protocol_test_utils.cc
+++ b/net/spdy/spdy_protocol_test_utils.cc
@@ -164,8 +164,8 @@
       DVLOG(1) << "actual doesn't contain param: " << param;
       return ::testing::AssertionFailure();
     }
-    uint32_t expected_value = entry.second.value;
-    uint32_t actual_value = actual_itr->second.value;
+    uint32_t expected_value = entry.second;
+    uint32_t actual_value = actual_itr->second;
     if (expected_value != actual_value) {
       DVLOG(1) << "Values don't match for parameter: " << param;
       return ::testing::AssertionFailure();
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 59e331a..80fdd6e 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -142,12 +142,10 @@
 
 std::unique_ptr<base::Value> NetLogSpdySettingCallback(
     SpdySettingsIds id,
-    SpdySettingsFlags flags,
     uint32_t value,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   dict->SetInteger("id", id);
-  dict->SetInteger("flags", flags);
   dict->SetInteger("value", value);
   return std::move(dict);
 }
@@ -160,10 +158,9 @@
   for (SettingsMap::const_iterator it = settings->begin();
        it != settings->end(); ++it) {
     const SpdySettingsIds id = it->first;
-    const SpdySettingsFlags flags = it->second.first;
-    const uint32_t value = it->second.second;
+    const uint32_t value = it->second;
     settings_list->AppendString(
-        base::StringPrintf("[id:%u flags:%u value:%u]", id, flags, value));
+        base::StringPrintf("[id:%u value:%u]", id, value));
   }
   dict->Set("settings", std::move(settings_list));
   return std::move(dict);
@@ -2098,15 +2095,14 @@
           buffered_spdy_framer_->SerializeFrame(settings_ir))));
 }
 
-void SpdySession::OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) {
+void SpdySession::OnSetting(SpdySettingsIds id, uint32_t value) {
   CHECK(in_io_loop_);
 
   HandleSetting(id, value);
 
   // Log the setting.
   net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTING,
-                    base::Bind(&NetLogSpdySettingCallback, id,
-                               static_cast<SpdySettingsFlags>(flags), value));
+                    base::Bind(&NetLogSpdySettingCallback, id, value));
 }
 
 void SpdySession::OnSendCompressedFrame(
@@ -2651,13 +2647,10 @@
   // First, notify the server about the settings they should use when
   // communicating with us.
   SettingsMap settings_map;
-  settings_map[SETTINGS_HEADER_TABLE_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize);
-  settings_map[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams);
+  settings_map[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize;
+  settings_map[SETTINGS_MAX_CONCURRENT_STREAMS] = kMaxConcurrentPushedStreams;
   if (stream_max_recv_window_size_ != kDefaultInitialWindowSize) {
-    settings_map[SETTINGS_INITIAL_WINDOW_SIZE] =
-        SettingsFlagsAndValue(SETTINGS_FLAG_NONE, stream_max_recv_window_size_);
+    settings_map[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size_;
   }
   SendSettings(settings_map);
 
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 9dbf5cd..c5afee5 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -875,7 +875,7 @@
   void OnStreamEnd(SpdyStreamId stream_id) override;
   void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
   void OnSettings() override;
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override;
+  void OnSetting(SpdySettingsIds id, uint32_t value) override;
   void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override;
   void OnPushPromise(SpdyStreamId stream_id,
                      SpdyStreamId promised_stream_id,
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 476cb76..12b8c01c 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -1132,8 +1132,7 @@
 
   // Receive SETTINGS frame that sets max_concurrent_streams to zero.
   SettingsMap settings_zero;
-  settings_zero[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0);
+  settings_zero[SETTINGS_MAX_CONCURRENT_STREAMS] = 0;
   SpdySerializedFrame settings_frame_zero(
       spdy_util_.ConstructSpdySettings(settings_zero));
 
@@ -1142,8 +1141,7 @@
 
   // Receive SETTINGS frame that sets max_concurrent_streams to one.
   SettingsMap settings_one;
-  settings_one[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 1);
+  settings_one[SETTINGS_MAX_CONCURRENT_STREAMS] = 1;
   SpdySerializedFrame settings_frame_one(
       spdy_util_.ConstructSpdySettings(settings_one));
 
@@ -1782,8 +1780,7 @@
 
   SettingsMap new_settings;
   const uint32_t max_concurrent_streams = kInitialMaxConcurrentStreams + 1;
-  new_settings[kSpdySettingsIds] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  new_settings[kSpdySettingsIds] = max_concurrent_streams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(new_settings));
   MockRead reads[] = {
@@ -1892,10 +1889,8 @@
   };
 
   SettingsMap settings;
-  settings[SETTINGS_HEADER_TABLE_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize);
-  settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams);
+  settings[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize;
+  settings[SETTINGS_MAX_CONCURRENT_STREAMS] = kMaxConcurrentPushedStreams;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(settings));
   MockWrite writes[] = {MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix,
@@ -2619,8 +2614,7 @@
   SettingsMap new_settings;
   const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS;
   const uint32_t max_concurrent_streams = 1;
-  new_settings[kSpdySettingsIds1] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams);
+  new_settings[kSpdySettingsIds1] = max_concurrent_streams;
 
   SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
   SpdySerializedFrame req1(
@@ -3659,8 +3653,7 @@
   // gets sent.
   SettingsMap new_settings;
   int32_t window_size = 1;
-  new_settings[SETTINGS_INITIAL_WINDOW_SIZE] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, window_size);
+  new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = window_size;
 
   // Set up the socket so we read a SETTINGS frame that sets
   // INITIAL_WINDOW_SIZE.
@@ -4880,8 +4873,7 @@
 // limit for a long time.
 TEST_F(SpdySessionTest, PushedStreamShouldNotCountToClientConcurrencyLimit) {
   SettingsMap new_settings;
-  new_settings[SETTINGS_MAX_CONCURRENT_STREAMS] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 2);
+  new_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = 2;
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(new_settings));
   SpdySerializedFrame pushed(spdy_util_.ConstructSpdyPush(
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 913287c..b172e1c 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -211,7 +211,7 @@
   void OnStreamEnd(SpdyStreamId stream_id) override {}
   void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {}
   void OnSettings() override {}
-  void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {}
+  void OnSetting(SpdySettingsIds id, uint32_t value) override {}
   void OnPing(SpdyPingId unique_id, bool is_ack) override {}
   void OnRstStream(SpdyStreamId stream_id,
                    SpdyRstStreamStatus status) override {}
@@ -408,7 +408,7 @@
 
 class AllowAnyCertCTPolicyEnforcer : public CTPolicyEnforcer {
  public:
-  AllowAnyCertCTPolicyEnforcer(){};
+  AllowAnyCertCTPolicyEnforcer() {}
   ~AllowAnyCertCTPolicyEnforcer() override = default;
 
   ct::CertPolicyCompliance DoesConformToCertPolicy(
@@ -746,14 +746,9 @@
 SpdySerializedFrame SpdyTestUtil::ConstructSpdySettings(
     const SettingsMap& settings) {
   SpdySettingsIR settings_ir;
-  for (SettingsMap::const_iterator it = settings.begin();
-       it != settings.end();
+  for (SettingsMap::const_iterator it = settings.begin(); it != settings.end();
        ++it) {
-    settings_ir.AddSetting(
-        it->first,
-        (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
-        (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
-        it->second.second);
+    settings_ir.AddSetting(it->first, it->second);
   }
   return SpdySerializedFrame(
       headerless_spdy_framer_.SerializeFrame(settings_ir));
@@ -1048,21 +1043,19 @@
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id,
                                                          bool fin) {
-  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id,
                      base::StringPiece(kUploadData, kUploadDataSize));
   data_ir.set_fin(fin);
-  return SpdySerializedFrame(framer.SerializeData(data_ir));
+  return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir));
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id,
                                                          const char* data,
                                                          uint32_t len,
                                                          bool fin) {
-  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
   data_ir.set_fin(fin);
-  return SpdySerializedFrame(framer.SerializeData(data_ir));
+  return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir));
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id,
@@ -1070,11 +1063,10 @@
                                                          uint32_t len,
                                                          bool fin,
                                                          int padding_length) {
-  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
   data_ir.set_fin(fin);
   data_ir.set_padding_len(padding_length);
-  return SpdySerializedFrame(framer.SerializeData(data_ir));
+  return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir));
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructWrappedSpdyFrame(
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 9fda719..470fe25 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -1827,7 +1827,7 @@
       QuicSessionPeer::GetCryptoStream(server_session)->flow_controller());
   SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_frame;
-  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false,
+  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE,
                             kDefaultMaxUncompressedHeaderSize);
   SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame));
   QuicFlowController* client_header_stream_flow_controller =
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index e4c33bb..32f4922 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -13,9 +13,17 @@
 
   deps = [
     "//base",
+    "//device/time_zone_monitor",
     "//services/service_manager/public/cpp",
   ]
 
+  public_deps = [
+    # TODO(blundell): This dep shouldn't be necessary at all,
+    # but content_shell fails to link in the component build if
+    # this isn't here as a public_dep.
+    "//device/time_zone_monitor/public/interfaces",
+  ]
+
   data_deps = [
     ":manifest",
   ]
diff --git a/services/device/DEPS b/services/device/DEPS
new file mode 100644
index 0000000..56fffa1
--- /dev/null
+++ b/services/device/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+device",
+]
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index 924c803..27c020d1d 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -7,7 +7,10 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "device/time_zone_monitor/time_zone_monitor.h"
 #include "services/service_manager/public/cpp/connection.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 
 namespace device {
 
@@ -26,7 +29,15 @@
 
 bool DeviceService::OnConnect(const service_manager::ServiceInfo& remote_info,
                               service_manager::InterfaceRegistry* registry) {
+  registry->AddInterface<mojom::TimeZoneMonitor>(this);
   return true;
 }
 
+void DeviceService::Create(const service_manager::Identity& remote_identity,
+                           mojom::TimeZoneMonitorRequest request) {
+  if (!time_zone_monitor_)
+    time_zone_monitor_ = device::TimeZoneMonitor::Create(file_task_runner_);
+  time_zone_monitor_->Bind(std::move(request));
+}
+
 }  // namespace device
diff --git a/services/device/device_service.h b/services/device/device_service.h
index 6f55953..cea6c11 100644
--- a/services/device/device_service.h
+++ b/services/device/device_service.h
@@ -5,21 +5,24 @@
 #ifndef SERVICES_DEVICE_DEVICE_SERVICE_H_
 #define SERVICES_DEVICE_DEVICE_SERVICE_H_
 
-#include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
+#include "device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/service.h"
 
 namespace device {
 
+class TimeZoneMonitor;
+
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
 
-class DeviceService : public service_manager::Service {
+class DeviceService
+    : public service_manager::Service,
+      public service_manager::InterfaceFactory<mojom::TimeZoneMonitor> {
  public:
-  explicit DeviceService(
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
+  DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
   ~DeviceService() override;
 
  private:
@@ -28,6 +31,11 @@
   bool OnConnect(const service_manager::ServiceInfo& remote_info,
                  service_manager::InterfaceRegistry* registry) override;
 
+  // InterfaceFactory<mojom::TimeZoneMonitor>:
+  void Create(const service_manager::Identity& remote_identity,
+              mojom::TimeZoneMonitorRequest request) override;
+
+  std::unique_ptr<device::TimeZoneMonitor> time_zone_monitor_;
   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceService);
diff --git a/services/device/manifest.json b/services/device/manifest.json
index bdc1d8d8..4d57086 100644
--- a/services/device/manifest.json
+++ b/services/device/manifest.json
@@ -5,6 +5,7 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
+        "device:time_zone_monitor": [ "device::mojom::TimeZoneMonitor" ]
       },
       "requires": {
       }
diff --git a/services/device/public/cpp/constants.cc b/services/device/public/cpp/constants.cc
deleted file mode 100644
index 8bf119d4..0000000
--- a/services/device/public/cpp/constants.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/device/public/cpp/constants.h"
-
-namespace device {
-
-const char kDeviceServiceName[] = "device";
-
-}  // namespace device
diff --git a/services/device/public/cpp/constants.h b/services/device/public/cpp/constants.h
deleted file mode 100644
index 3a48f45..0000000
--- a/services/device/public/cpp/constants.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_
-#define SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_
-
-namespace device {
-
-extern const char kDeviceServiceName[];
-
-}  // namespace device
-
-#endif  // SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_
diff --git a/services/device/public/cpp/BUILD.gn b/services/device/public/interfaces/BUILD.gn
similarity index 65%
rename from services/device/public/cpp/BUILD.gn
rename to services/device/public/interfaces/BUILD.gn
index f341a18b..dec07695 100644
--- a/services/device/public/cpp/BUILD.gn
+++ b/services/device/public/interfaces/BUILD.gn
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("cpp") {
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
   sources = [
-    "constants.cc",
-    "constants.h",
+    "constants.mojom",
   ]
 }
diff --git a/services/device/public/interfaces/OWNERS b/services/device/public/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/services/device/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/device/public/interfaces/constants.mojom b/services/device/public/interfaces/constants.mojom
new file mode 100644
index 0000000..de400fc
--- /dev/null
+++ b/services/device/public/interfaces/constants.mojom
@@ -0,0 +1,7 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module device.mojom;
+
+const string kServiceName = "device";
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index 788f706..2239dde 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -75,7 +75,6 @@
   const string kAlwaysOnTop_Property = "prop:always_on_top";
 
   // The application icon; typically larger for shelf icons, etc. Type: SkBitmap
-  // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522?
   const string kAppIcon_Property = "prop:app-icon";
 
   // The application ID (eg. 'mojo:foo'). Maps to
@@ -123,7 +122,6 @@
   const string kShowState_Property = "prop:show-state";
 
   // The window icon; typically 16x16 for titlebars. Type: SkBitmap
-  // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522?
   const string kWindowIcon_Property = "prop:window-icon";
 
   // A flag controlling the window's presence on the mash shelf. Type: bool
diff --git a/storage/browser/fileapi/file_system_operation_context.cc b/storage/browser/fileapi/file_system_operation_context.cc
index b392d7b..6f03ff45b 100644
--- a/storage/browser/fileapi/file_system_operation_context.cc
+++ b/storage/browser/fileapi/file_system_operation_context.cc
@@ -27,7 +27,7 @@
 }
 
 FileSystemOperationContext::~FileSystemOperationContext() {
-  DetachUserDataThread();
+  DetachFromSequence();
   setter_thread_checker_.DetachFromThread();
 }
 
diff --git a/storage/browser/fileapi/file_system_operation_impl.cc b/storage/browser/fileapi/file_system_operation_impl.cc
index 196f693..9defb3c 100644
--- a/storage/browser/fileapi/file_system_operation_impl.cc
+++ b/storage/browser/fileapi/file_system_operation_impl.cc
@@ -384,7 +384,7 @@
       pending_operation_(kOperationNone),
       weak_factory_(this) {
   DCHECK(operation_context_.get());
-  operation_context_->DetachUserDataThread();
+  operation_context_->DetachFromSequence();
   async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
   DCHECK(async_file_util_);
 }
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 63b97a7..4599c63 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -3511,6 +3511,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build151-m1",
+              "os": "Ubuntu-14.04",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -25426,6 +25445,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0166",
+              "id": "build104-b1",
+              "os": "Mac-10.11",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -80166,6 +80204,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0534",
+              "id": "build134-m1",
+              "os": "Windows-10-10240",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -91152,6 +91209,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6779",
+              "id": "build104-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -113105,6 +113181,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "id": "build95-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -124091,6 +124186,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build188-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
@@ -146063,6 +146177,25 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "media_perftests",
+        "name": "media_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build146-m1",
+              "os": "Windows-2012ServerR2-SP0",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 21600,
+          "hard_timeout": 7200,
+          "io_timeout": 3600
+        }
+      },
+      {
         "args": [
           "memory.blink_memory_mobile",
           "-v",
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
index d68ce32..6f36796 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -1,5 +1,3 @@
--ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled
-
 # NavigationHandle::IsRendererInitiated some times return differently in
 # browser side navigation tests.
 -SBNavigationObserverBrowserTest.DirectDownloadNoReferrerTargetBlank
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
index 60e438de..15285ed 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -1,5 +1,4 @@
 -NavigationControllerBrowserTest.EnsureSamePageNavigationUpdatesFrameNavigationEntry
--RequestDataResourceDispatcherHostBrowserTest.*
 
 # Browser-initiated fragment navigations are handled improperly. https://crbug.com/663777
 -NavigationControllerBrowserTest.SamePageBrowserInitiated
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 8b21a0a..e4bc8a3 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -852,6 +852,14 @@
       "cc_perftests",
     ],
   },
+  "media_perftests": {
+    "label": "//media:media_perftests",
+    "type": "script",
+    "script": "//testing/scripts/run_gtest_perf_test.py",
+    "args": [
+      "media_perftests",
+    ],
+  },
   "load_library_perf_tests": {
     "label": "//chrome/test:load_library_perf_tests",
     "type": "script",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dd6bbd7..6716526 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -212,7 +212,10 @@
             ],
             "experiments": [
                 {
-                    "name": "Default"
+                    "name": "Default",
+                    "params": {
+                        "RedirectSequencedWorkerPools": "true"
+                    }
                 },
                 {
                     "name": "EnableViaFlag",
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index eb6163b..9ff65fe 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -172,6 +172,14 @@
 crbug.com/551843 [ Linux ] fast/text/fallback-traits-fixup.html [ WontFix ]
 crbug.com/551843 [ Linux Win ] fast/text/emoji-font-weight-mac.html [ WontFix ]
 
+# Variable system font only supported on Mac atm.
+crbug.com/670246 [ Linux Android Win Mac10.9 Mac10.10 ] fast/text/variable-fonts/variable-mac-system-font.html [ WontFix ]
+# OpenType Variations not supported on Windows at the moment
+# Linux disabled until we can ship a statically linked newer version of FreeType
+crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] fast/text/variable-fonts/variable-box-font.html [ WontFix ]
+crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] http/tests/webfont/variable-box-font-arraybuffer.html [ WontFix ]
+crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] virtual/mojo-loading/http/tests/webfont/variable-box-font-arraybuffer.html [ WontFix ]
+
 # prefer_compositing_to_lcd_text causes things to get composited regardless of their opaqueness, causing the test to fail
 crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-opaque-to-transparent.html [ WontFix ]
 crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-transparent-to-opaque.html [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 3705e47..22c8948 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -56,6 +56,8 @@
 
 crbug.com/517449 [ Android ] images/optimize-contrast-image.html [ Failure ]
 
+crbug.com/676561 [ Android ] images/paletted-png-with-color-profile.html [ Skip ]
+
 crbug.com/531286 virtual/gpu/fast/canvas/yuv-video-on-accelerated-canvas.html [ Failure ]
 # The failure in the next line is due to virtual/gpu bots using osmesa
 # Under osmesa, skia's filter quality doesn't work correctly. This test is expected to pass
@@ -800,8 +802,6 @@
 
 crbug.com/596968 [ Win ] inspector-protocol/input/eventTimestamp.html [ Failure Pass ]
 
-crbug.com/667286 inspector/sources/debugger-ui/function-generator-details.html [ NeedsManualRebaseline ]
-
 crbug.com/487281 [ Mac ] fast/forms/select/menulist-narrow-width.html [ Failure ]
 
 crbug.com/543110 [ Mac ] fast/text/international/text-shaping-arabic.html [ Failure ]
@@ -1974,6 +1974,8 @@
 crbug.com/508728 imported/wpt/webstorage/document-domain.html [ Failure Timeout ]
 crbug.com/508728 virtual/mojo-localstorage/imported/wpt/webstorage/document-domain.html [ Failure Timeout ]
 
+crbug.com/676572 [ Debug ] virtual/mojo-localstorage/imported/wpt/webstorage/storage_local_setitem_quotaexceedederr.html [ Pass Timeout ]
+
 # Bug(dpranke)
 # crbug.com/508728 imported/wpt/workers/Worker_cross_origin_security_err.htm [ Timeout ]
 
@@ -2036,8 +2038,7 @@
 crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-019.html [ Failure ]
 crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-024.html [ Failure ]
 crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-025.html [ Failure ]
-crbug.com/669490 imported/csswg-test/css-ui-3/caret-color-018.html [ Failure ]
-crbug.com/669490 imported/csswg-test/css-ui-3/caret-color-020.html [ Failure ]
+crbug.com/676295 imported/csswg-test/css-ui-3/caret-color-021.html [ Failure ]
 crbug.com/669473 imported/csswg-test/css-ui-3/text-overflow-002.html [ Failure ]
 crbug.com/669473 imported/csswg-test/css-ui-3/text-overflow-004.html [ Failure ]
 
@@ -2363,7 +2364,6 @@
 crbug.com/664855 virtual/scroll_customization/fast/scroll-behavior/main-frame-interrupted-scroll.html [ Pass Failure ]
 crbug.com/664856 virtual/sharedarraybuffer/fast/workers/worker-gc.html [ Pass Failure ]
 
-crbug.com/664857 virtual/threaded/animations/animation-transition-collision-crash.html [ Pass Failure ]
 crbug.com/664857 virtual/threaded/animations/composited-animations-simple.html [ Pass Failure ]
 
 # Possible duplicate of crbug.com/665577
diff --git a/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html b/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html
index a9601016..d0b756e 100644
--- a/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html
+++ b/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html
@@ -1,3 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <style>
@@ -19,15 +21,17 @@
 </style>
 <div id="target"></div>
 <script>
-var asyncHandle = async_test('Don\'t crash when animating transitioned properties');
-var frameWait = 7;
-function frame() {
-  frameWait--;
-  if (frameWait) {
-    requestAnimationFrame(frame);
-  } else {
-    asyncHandle.done();
+'use strict';
+async_test(t => {
+  let frameWait = 5;
+  function frame() {
+    frameWait--;
+    if (frameWait) {
+      requestAnimationFrame(frame);
+    } else {
+      t.done();
+    }
   }
-}
-requestAnimationFrame(frame);
-</script>
\ No newline at end of file
+  requestAnimationFrame(frame);
+}, 'Don\'t crash when animating transitioned properties');
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html b/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html
new file mode 100644
index 0000000..82d5d6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.target {
+  display: inline-block;
+  font-size: 60pt;
+  color: rgb(50, 50, 50);
+}
+.expected {
+  margin-right: 15px;
+}
+</style>
+<body contenteditable>
+<template id="target-template">T</template>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+assertComposition({
+  property: 'caret-color',
+  underlying: 'rgb(50, 50, 50)',
+  addFrom: 'rgb(100, 100, 100)',
+  addTo: 'rgb(200, 200, 200)',
+}, [
+  {at: -0.3, is: 'rgb(120, 120, 120)'},
+  {at: 0, is: 'rgb(150, 150, 150)'},
+  {at: 0.5, is: 'rgb(200, 200, 200)'},
+  {at: 1, is: 'rgb(250, 250, 250)'},
+  {at: 1.5, is: 'rgb(255, 255, 255)'},
+]);
+
+assertComposition({
+  property: 'caret-color',
+  underlying: 'auto',
+  addFrom: 'rgb(100, 100, 100)',
+  addTo: 'rgb(200, 200, 200)',
+}, [
+  {at: -0.3, is: 'rgb(120, 120, 120)'},
+  {at: 0, is: 'rgb(150, 150, 150)'},
+  {at: 0.5, is: 'rgb(200, 200, 200)'},
+  {at: 1, is: 'rgb(250, 250, 250)'},
+  {at: 1.5, is: 'rgb(255, 255, 255)'},
+]);
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html b/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html
new file mode 100644
index 0000000..3f0a8cfc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  caret-color: blue;
+}
+.target {
+  display: inline-block;
+  font-size: 60pt;
+  caret-color: yellow;
+}
+.expected {
+  margin-right: 15px;
+}
+</style>
+<body alink="red" link="blue" contenteditable>
+<template id="target-template">T</template>
+<script src="resources/interpolation-test.js"></script>
+<script>
+assertInterpolation({
+  property: 'caret-color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 255, 0)'},
+  {at: 0, is: 'rgb(255, 255, 0)'},
+  {at: 0.3, is: 'rgb(179, 217, 0)'},
+  {at: 0.6, is: 'rgb(102, 179, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 65, 0)'},
+]);
+
+assertNoInterpolation({
+  property: 'caret-color',
+  from: 'initial',
+  to: 'green',
+});
+
+assertNoInterpolation({
+  property: 'caret-color',
+  from: 'auto',
+  to: 'green',
+});
+
+assertInterpolation({
+  property: 'caret-color',
+  from: 'currentColor',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 38, 0)'},
+  {at: 0.6, is: 'rgb(0, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: 'caret-color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: 'caret-color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: 'caret-color',
+  from: 'black',
+  to: 'orange',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(77, 50, 0)'},
+  {at: 0.6, is: 'rgb(153, 99, 0)'},
+  {at: 1, is: 'rgb(255, 165, 0)'},
+  {at: 1.5, is: 'rgb(255, 248, 0)'},
+]);
+
+assertInterpolation({
+  property: 'caret-color',
+  from: '-webkit-activelink',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 0, 0)'},
+  {at: 0, is: 'rgb(255, 0, 0)'},
+  {at: 0.3, is: 'rgb(179, 38, 0)'},
+  {at: 0.6, is: 'rgb(102, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: 'caret-color',
+  from: '-webkit-link',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
index d1346f2..e6739ef 100644
--- a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
+++ b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
@@ -4,25 +4,20 @@
     'mockBatteryMonitor',
     ['device/battery/battery_monitor.mojom',
      'device/battery/battery_status.mojom',
-     'mojo/public/js/router',
+     'mojo/public/js/bindings',
     ]).then(mojo => {
-  let [batteryMonitor, batteryStatus, router] = mojo.modules;
+  let [batteryMonitor, batteryStatus, bindings] = mojo.modules;
 
-  class MockBatteryMonitor extends batteryMonitor.BatteryMonitor.stubClass {
+  class MockBatteryMonitor {
     constructor(interfaceProvider) {
-      super();
       interfaceProvider.addInterfaceOverrideForTesting(
           batteryMonitor.BatteryMonitor.name,
-          handle => this.connect_(handle));
+          handle => this.bindingSet_.addBinding(this, handle));
 
       this.interfaceProvider_ = interfaceProvider;
       this.pendingRequests_ = [];
       this.status_ = null;
-    }
-
-    connect_(handle) {
-      this.router_ = new router.Router(handle);
-      this.router_.setIncomingReceiver(this);
+      this.bindingSet_ = new bindings.BindingSet(batteryMonitor.BatteryMonitor);
     }
 
     queryNextStatus() {
diff --git a/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt b/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt
index 54ea2db..65fd4a2 100644
--- a/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt
@@ -1,6 +1,6 @@
 CONSOLE MESSAGE: line 14: --bar: [null]
 CONSOLE MESSAGE: line 14: --foo: [CSSStyleValue= bar]
-CONSOLE MESSAGE: line 14: align-items: [CSSStyleValue=normal]
+CONSOLE MESSAGE: line 14: align-items: [CSSKeywordValue=normal]
 CONSOLE MESSAGE: line 14: border-radius: [CSSStyleValue=2px]
 This tests the style information in the paint callback.
 
@@ -8,5 +8,5 @@
 
 --bar: [null]
 --foo: [CSSStyleValue= bar]
-align-items: [CSSStyleValue=normal]
+align-items: [CSSKeywordValue=normal]
 border-radius: [CSSStyleValue=2px]
diff --git a/third_party/WebKit/LayoutTests/csspaint/style-background-image.html b/third_party/WebKit/LayoutTests/csspaint/style-background-image.html
index edb2d78..0fe62fc 100644
--- a/third_party/WebKit/LayoutTests/csspaint/style-background-image.html
+++ b/third_party/WebKit/LayoutTests/csspaint/style-background-image.html
@@ -33,7 +33,7 @@
 <p>See the devtools console for test output. The console should log:</p>
 --bar: [null]<br>
 --foo: [CSSStyleValue= bar]<br>
-align-items: [CSSStyleValue=normal]<br>
+align-items: [CSSKeywordValue=normal]<br>
 border-radius: [CSSStyleValue=2px]
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html b/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html
index 4d33bb67..4bae586 100644
--- a/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html
+++ b/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html
@@ -122,11 +122,31 @@
     }
   }
   w.customElements.define('a-a', A);
+  let e = w.document.createElement('a-a');
+  assert_true(e.matches(':defined'),
+    'constructing an autonomous custom element with create element should ' +
+    'not throw InvalidStateError ' +
+    'and should return a "custom" element');
+}, 'Already constructed marker, create element');
+
+test_with_window((w) => {
+  let flag = true;
+  class A extends w.HTMLElement {
+    constructor() {
+      if (flag) {
+        flag = false;
+        new A();
+      }
+      super();
+    }
+  }
+  w.customElements.define('a-a', A);
+  let d = w.document.createElement('div');
   assert_reports(w, 'INVALID_STATE_ERR', () => {
-    w.document.createElement('a-a');
+    d.innerHTML = '<a-a>';
   }, 'Creating an element that is already constructed marker should report ' +
      'InvalidStateError');
-}, 'Already constructed marker, create element');
+}, 'Already constructed marker, fragment parsing should set marker');
 
 test_with_window((w) => {
   let errors = [];
diff --git a/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
index 9826f8aa..9b5b1ddec 100644
--- a/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
+++ b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
@@ -68,6 +68,31 @@
 });
 </script>
 
+<template data-test="foreign content insertion executes connected">
+  <script>
+  'use strict';
+
+  customElements.define('a-a', class extends HTMLElement {
+    constructor() {
+      super();
+    }
+    connectedCallback() {
+      window.connectedChildNodeCount = this.childNodes.length;
+    }
+  });
+  </script>
+  <a-a><div></div></a-a>
+</template>
+<script>
+'use strict';
+
+test_with_content((w) => {
+  assert_equals(w.connectedChildNodeCount, 0,
+      'the parser should have run the connected callback when inserting the ' +
+      'element, before continuing tree construction');
+});
+</script>
+
 <template data-test="element creation failure produces unknown element">
   <script>
   'use strict';
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js b/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js
index 836c79d..40b657d 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js
+++ b/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js
@@ -10,9 +10,9 @@
     ['device/geolocation/public/interfaces/geolocation.mojom',
      'third_party/WebKit/public/platform/modules/permissions/permission.mojom',
      'third_party/WebKit/public/platform/modules/permissions/permission_status.mojom',
-     'mojo/public/js/router',
+     'mojo/public/js/bindings',
     ]).then(mojo => {
-  let [geolocation, permission, permissionStatus, router] =
+  let [geolocation, permission, permissionStatus, bindings] =
       mojo.modules;
 
   class GeolocationServiceMock {
@@ -51,6 +51,11 @@
       this.permissionStatus_ = permissionStatus.PermissionStatus.ASK;
       this.rejectPermissionConnections_ = false;
       this.rejectGeolocationConnections_ = false;
+
+      this.geolocationBindingSet_ = new bindings.BindingSet(
+          geolocation.GeolocationService);
+      this.permissionBindingSet_ = new bindings.BindingSet(
+          permission.PermissionService);
     }
 
     connectGeolocation_(handle) {
@@ -58,10 +63,7 @@
         mojo.core.close(handle);
         return;
       }
-      this.geolocationStub_ = new geolocation.GeolocationService.stubClass(
-          this);
-      this.geolocationRouter_ = new router.Router(handle);
-      this.geolocationRouter_.setIncomingReceiver(this.geolocationStub_);
+      this.geolocationBindingSet_.addBinding(this, handle);
     }
 
     connectPermission_(handle) {
@@ -69,9 +71,7 @@
         mojo.core.close(handle);
         return;
       }
-      this.permissionStub_ = new permission.PermissionService.stubClass(this);
-      this.permissionRouter_ = new router.Router(handle);
-      this.permissionRouter_.setIncomingReceiver(this.permissionStub_);
+      this.permissionBindingSet_.addBinding(this, handle);
     }
 
     setHighAccuracy(highAccuracy) {
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html b/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html
new file mode 100644
index 0000000..f074f658
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>body { margin:8px; }</style>
+<div style="margin:100px; columns:2; column-gap:20px; width:420px;">
+    <div id="empty"></div>
+</div>
+<script>
+    test(function() {
+        var rects = document.getElementById("empty").getClientRects();
+        assert_equals(rects.length, 1);
+        assert_equals(rects[0].left, 108);
+        assert_equals(rects[0].top, 100);
+        assert_equals(rects[0].right, 308);
+        assert_equals(rects[0].bottom, 100);
+    }, "Zero-height block in otherwise empty multicol");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html
new file mode 100644
index 0000000..6d537932
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+    @font-face {
+    font-family: variabletest_box;
+    src: url(../../../http/tests/resources/variabletest_box.ttf);
+    }
+
+    body {
+    font-family: variabletest_box, sans-serif;
+    font-size: 200px;
+    }
+</style>
+â–„ â–€
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html
new file mode 100644
index 0000000..c83abcc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+    @font-face {
+    font-family: variabletest_box;
+    src: url(../../../http/tests/resources/variabletest_box.ttf);
+    }
+
+    body {
+    font-family: variabletest_box,
+    sans-serif;
+    font-size: 200px;
+    }
+
+    .a_low {
+    font-variation-settings: "upwd" 0;
+    }
+
+    .a_up {
+    font-variation-settings: "upwd" 350;
+    }
+</style>
+<!-- The variabletest_box font has an A glyph that looks like a lower half box,
+  with deltas on the 'upwd' variation axis that allow shifting the box up. At
+  350, the box is at the top. The font also has two glyphs for UPPER HALF BLOCK
+  and LOWER HALF BLOCK, which look identical to the respective variations of A.
+-->
+A <span class="a_up">A</span>
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html
new file mode 100644
index 0000000..2cbd637
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+.variation {
+font-family: Skia, sans-serif;
+font-size: 150px;
+}
+
+#narrow {
+font-variation-settings: "wdth" 0.61998;
+}
+
+#wide {
+font-variation-settings: "wdth" 1.29999;
+}
+</style>
+<span class="variation" id="narrow">wwwwide</span><br>
+<span class="variation" id="wide">wwwwide</span>
+<script>
+setup({ explicit_done: true });
+test(function() { assert_true(narrow.getBoundingClientRect().width <
+                              wide.getBoundingClientRect().width * 0.6); },
+     "Narrow Skia variation is less than 60% as wide.");
+done();
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
index e404dbc..9e1f966 100644
--- a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
+++ b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
@@ -11,15 +11,15 @@
 let budgetServiceMock = loadMojoModules(
     'budgetServiceMock',
     ['third_party/WebKit/public/platform/modules/budget_service/budget_service.mojom',
-     'mojo/public/js/router'
+     'mojo/public/js/bindings'
     ]).then(mojo => {
-  const [budgetService, router] = mojo.modules;
+  const [budgetService, bindings] = mojo.modules;
 
   class BudgetServiceMock {
     constructor(interfaceProvider) {
       interfaceProvider.addInterfaceOverrideForTesting(
           budgetService.BudgetService.name,
-          handle => this.connectBudgetService_(handle));
+          handle => this.bindingSet_.addBinding(this, handle));
 
       this.interfaceProvider_ = interfaceProvider;
 
@@ -27,12 +27,7 @@
       this.cost_ = {};
       this.budget_ = [];
       this.error_ = budgetService.BudgetServiceErrorType.NONE;
-    }
-
-    connectBudgetService_(handle) {
-      this.budgetServiceStub_ = new budgetService.BudgetService.stubClass(this);
-      this.budgetServiceRouter_ = new router.Router(handle);
-      this.budgetServiceRouter_.setIncomingReceiver(this.budgetServiceStub_);
+      this.bindingSet_ = new bindings.BindingSet(budgetService.BudgetService);
     }
 
     // This is called directly from test JavaScript to set up the return value
diff --git a/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf b/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf
new file mode 100644
index 0000000..840a78e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html
new file mode 100644
index 0000000..9adfa624
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+    @font-face {
+    font-family: variabletest_box;
+    src: url(/resources/variabletest_box.ttf);
+    }
+
+    body {
+    font-family: variabletest_box, sans-serif;
+    font-size: 200px;
+    }
+</style>
+â–„ â–€
diff --git a/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html
new file mode 100644
index 0000000..ebe0bb40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script>
+if(window.testRunner)
+    testRunner.waitUntilDone();
+
+fetch('/resources/variabletest_box.ttf').then(function(response) {
+    response.arrayBuffer().then(function(fontBuffer) {
+        var fontface = new FontFace("variabletest_box", fontBuffer);
+        fontface.load().then(function() {
+            document.fonts.add(fontface);
+            if(window.testRunner)
+                testRunner.notifyDone();
+        });
+    });
+});
+</script>
+<style>
+    body {
+    font-family: variabletest_box;
+    font-size: 200px;
+    }
+
+    .a_low {
+    font-variation-settings: "upwd" 0;
+    }
+
+    .a_up {
+    font-variation-settings: "upwd" 350;
+    }
+</style>
+<!-- The variabletest_box font has an A glyph that looks like a lower half box,
+  with deltas on the 'upwd' variation axis that allow shifting the box up. At
+  350, the box is at the top. The font also has two glyphs for UPPER HALF BLOCK
+  and LOWER HALF BLOCK, which look identical to the respective variations of A.
+-->
+A <span class="a_up">A</span>
diff --git a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
index e712c32..5d543e6 100644
--- a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
+++ b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
@@ -4,15 +4,14 @@
   'mockImageCapture',
   ['media/capture/mojo/image_capture.mojom',
    'mojo/public/js/bindings',
-   'mojo/public/js/connection',
    'content/public/renderer/interfaces',
-  ], (imageCapture, bindings, connection, interfaces) => {
+  ], (imageCapture, bindings, interfaces) => {
 
   class MockImageCapture {
     constructor() {
       interfaces.addInterfaceOverrideForTesting(
           imageCapture.ImageCapture.name,
-          pipe => this.bindToPipe(pipe));
+          handle => this.bindingSet_.addBinding(this, handle));
 
       this.capabilities_ = { capabilities : {
           iso : { min : 100.0, max : 12000.0, current : 400.0, step : 1.0 },
@@ -34,11 +33,7 @@
           sharpness : { min : 4.0, max : 7.0, current : 7.0, step : 1.0 },
       }};
       this.settings_ = null;
-    }
-
-    bindToPipe(pipe) {
-      this.stub_ = connection.bindHandleToStub(pipe, imageCapture.ImageCapture);
-      bindings.StubBindings(this.stub_).delegate = this;
+      this.bindingSet_ = new bindings.BindingSet(imageCapture.ImageCapture);
     }
 
     getCapabilities(source_id) {
diff --git a/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html b/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html
index ba3610bb..eba164e 100644
--- a/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html
+++ b/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html
@@ -23,6 +23,23 @@
                 document.body.appendChild(div);
             }        
     }
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        window.addEventListener('load', () => {
+            // This is a workaroud for an issue that the load event may be
+            // dispatched before loading the background image. See
+            // https://crbug.com/675870.
+            const id = setInterval(() => {
+                // Force layout.
+                document.body.offsetTop;
+                if (internals.isLoading('./resources/pixel-crack-image-background-webkit-transform-scale.png')) {
+                    return;
+                }
+                clearInterval(id);
+                testRunner.notifyDone();
+            }, 50);
+        });
+    }
     </script>
 </head>
 <body onload="runTest()">
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html b/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html
new file mode 100644
index 0000000..1f1d0ae
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: Default caret-color test animation</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css3-ui/#caret-color">
+<link rel="help" href="https://www.w3.org/TR/web-animations-1/#dom-animatable-animate">
+<link rel="help" href="https://www.w3.org/TR/css3-color/#color0">
+<meta name="assert" content="Test checks that the default value for caret-color property, which is 'auto', is not interpolatible.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  @keyframes caret-color-to-lime {
+    to { caret-color: lime; }
+  }
+
+  #textarea {
+    color: magenta;
+    animation: caret-color-to-lime 2s -1s paused;
+  }
+</style>
+<body>
+  <textarea id="textarea"></textarea>
+  <div id=log></div>
+
+  <script>
+    test(
+      function(){
+        var textarea = document.getElementById("textarea");
+        assert_equals(getComputedStyle(textarea).caretColor, 'rgb(0, 255, 0)');
+      }, "Default caret-color is not interpolatible");
+</script>
+</body>
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt
index 8835656..50198f6 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt
@@ -1,8 +1,5 @@
-CONSOLE ERROR: line 14: Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed
-CONSOLE ERROR: line 14: Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed
-FAIL HTML parser must use the returned value of the custom element constructor instead of the one created before super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) false
-FAIL HTML parser must use the returned value of the custom element constructor instead using the one created in super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) false
+FAIL HTML parser must use the returned value of the custom element constructor instead of the one created before super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) true
+FAIL HTML parser must use the returned value of the custom element constructor instead using the one created in super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) true
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt
index 0ad0a51..5245df17 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt
@@ -14,6 +14,7 @@
 lineNumber = 13
 columnNumber = 13
 script is valid: yes
+[[Scopes]] = Scopes[2]
 
 Running: testIterSuspended
 iterSuspended: type = object, subtype = generator
@@ -28,6 +29,7 @@
 lineNumber = 15
 columnNumber = 4
 script is valid: yes
+[[Scopes]] = Scopes[2]
 
 Running: testIterClosed
 iterClosed: type = object, subtype = generator
@@ -42,6 +44,7 @@
 lineNumber = 13
 columnNumber = 13
 script is valid: yes
+[[Scopes]] = Scopes[2]
 
 Running: testIterObjGenerator
 iterObjGenerator: type = object, subtype = generator
@@ -56,6 +59,7 @@
 lineNumber = 24
 columnNumber = 8
 script is valid: yes
+[[Scopes]] = Scopes[2]
 
 Running: testAnonymousGenIter
 anonymousGenIter: type = object, subtype = generator
@@ -69,4 +73,5 @@
 lineNumber = 38
 columnNumber = 4
 script is valid: yes
+[[Scopes]] = Scopes[2]
 
diff --git a/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js b/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js
index 2a73642..963e40e0 100644
--- a/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js
+++ b/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js
@@ -42,9 +42,9 @@
 let mediaSessionServiceMock = loadMojoModules(
     'mediaSessionServiceMock',
     ['third_party/WebKit/public/platform/modules/mediasession/media_session.mojom',
-     'mojo/public/js/router',
+     'mojo/public/js/bindings',
     ]).then(mojo => {
-      let [mediaSessionService, router] = mojo.modules;
+      let [mediaSessionService, bindings] = mojo.modules;
 
       MediaSessionAction = mediaSessionService.MediaSessionAction;
       MediaSessionPlaybackState = mediaSessionService.MediaSessionPlaybackState;
@@ -53,15 +53,11 @@
         constructor(interfaceProvider) {
           interfaceProvider.addInterfaceOverrideForTesting(
               mediaSessionService.MediaSessionService.name,
-              handle => this.connectMediaSessionService_(handle));
+              handle => this.bindingSet_.addBinding(this, handle));
           this.interfaceProvider_ = interfaceProvider;
           this.pendingResponse_ = null;
-        }
-
-        connectMediaSessionService_(handle) {
-          this.mediaSessionServiceStub_ = new mediaSessionService.MediaSessionService.stubClass(this);
-          this.mediaSessionServiceRouter_ = new router.Router(handle);
-          this.mediaSessionServiceRouter_.setIncomingReceiver(this.mediaSessionServiceStub_);
+          this.bindingSet_ = new bindings.BindingSet(
+              mediaSessionService.MediaSessionService);
         }
 
         setMetadata(metadata) {
diff --git a/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js b/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js
index f9dcca2..ff8f03e 100644
--- a/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js
+++ b/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js
@@ -70,9 +70,8 @@
 function nfc_mocks(mojo) {
   return define('NFC mocks', [
     'mojo/public/js/bindings',
-    'mojo/public/js/connection',
     'device/nfc/nfc.mojom',
-  ], (bindings, connection, nfc) => {
+  ], (bindings, nfc) => {
 
     function toMojoNFCRecordType(type) {
       switch (type) {
@@ -259,6 +258,8 @@
 
     class MockNFC {
       constructor() {
+        this.bindingSet = new bindings.BindingSet(nfc.NFC);
+
         this.hw_status_ = NFCHWStatus.ENABLED;
         this.pushed_message_ = null;
         this.push_options_ = null;
@@ -270,7 +271,7 @@
         this.watchers_ = [];
       }
 
-      // NFC.stubClass delegate functions
+      // NFC delegate functions
       push(message, options) {
         let error = this.isReady();
         if (error)
@@ -338,14 +339,6 @@
         return Promise.resolve(createNFCError(null));
       }
 
-
-      // Mock utility functions
-      bindToPipe(pipe) {
-        this.stub_ = connection.bindHandleToStub(
-            pipe, nfc.NFC);
-        bindings.StubBindings(this.stub_).delegate = this;
-      }
-
       isReady() {
         if (this.hw_status_ === NFCHWStatus.DISABLED)
           return createNFCError(nfc.NFCErrorType.DEVICE_DISABLED);
@@ -408,8 +401,8 @@
     let mockNFC = new MockNFC;
     mojo.frameInterfaces.addInterfaceOverrideForTesting(
         nfc.NFC.name,
-        pipe => {
-          mockNFC.bindToPipe(pipe);
+        handle => {
+          mockNFC.bindingSet.addBinding(mockNFC, handle);
         });
 
     return Promise.resolve({
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
index 464fec30..d5a3eb8d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js b/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js
index 9b2dbd0..6df94aa 100644
--- a/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js
+++ b/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js
@@ -8,23 +8,19 @@
     'presentationServiceMock',
     [
       'third_party/WebKit/public/platform/modules/presentation/presentation.mojom',
-      'mojo/public/js/router',
+      'mojo/public/js/bindings',
     ]).then(mojo => {
-      let [ presentationService, router ] = mojo.modules;
+      let [ presentationService, bindings ] = mojo.modules;
 
       class PresentationServiceMock {
         constructor(interfaceProvider) {
           interfaceProvider.addInterfaceOverrideForTesting(
               presentationService.PresentationService.name,
-              handle => this.connectPresentationService_(handle));
+              handle => this.bindingSet_.addBinding(this, handle));
           this.interfaceProvider_ = interfaceProvider;
           this.pendingResponse_ = null;
-        }
-
-        connectPresentationService_(handle) {
-          this.presentationServiceStub_ = new presentationService.PresentationService.stubClass(this);
-          this.presentationServiceRouter_ = new router.Router(handle);
-          this.presentationServiceRouter_.setIncomingReceiver(this.presentationServiceStub_);
+          this.bindingSet_ = new bindings.BindingSet(
+              presentationService.PresentationService);
         }
 
         startSession(urls) {
diff --git a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
index 262145f..03f32d9 100644
--- a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
+++ b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
@@ -21,10 +21,9 @@
   return define('Generic Sensor API mocks', [
     'mojo/public/js/core',
     'mojo/public/js/bindings',
-    'mojo/public/js/connection',
     'device/generic_sensor/public/interfaces/sensor_provider.mojom',
     'device/generic_sensor/public/interfaces/sensor.mojom',
-  ], (core, bindings, connection, sensor_provider, sensor) => {
+  ], (core, bindings, sensor_provider, sensor) => {
 
     // Helper function that returns resolved promise with result.
     function sensorResponse(success) {
@@ -33,9 +32,8 @@
 
     // Class that mocks Sensor interface defined in sensor.mojom
     class MockSensor {
-      constructor(stub, handle, offset, size, reportingMode) {
+      constructor(sensorRequest, handle, offset, size, reportingMode) {
         this.client_ = null;
-        this.stub_ = stub;
         this.expects_modified_reading_ = false;
         this.start_should_fail_ = false;
         this.reporting_mode_ = reportingMode;
@@ -52,10 +50,11 @@
         this.buffer_array_ = rv.buffer;
         this.buffer_ = new Float64Array(this.buffer_array_);
         this.resetBuffer();
-        bindings.StubBindings(this.stub_).delegate = this;
-        bindings.StubBindings(this.stub_).connectionErrorHandler = () => {
+        this.binding_ = new bindings.Binding(sensor.Sensor, this,
+                                             sensorRequest);
+        this.binding_.setConnectionErrorHandler(() => {
           this.reset();
-        };
+        });
       }
 
       // Returns default configuration.
@@ -137,7 +136,7 @@
         this.resetBuffer();
         core.unmapBuffer(this.buffer_array_);
         this.buffer_array_ = null;
-        bindings.StubBindings(this.stub_).close();
+        this.binding_.close();
       }
 
       // Zeroes shared buffer.
@@ -241,6 +240,8 @@
         this.resolve_func_ = null;
         this.is_continuous_ = false;
         this.max_frequency_ = 60;
+        this.binding_ = new bindings.Binding(sensor_provider.SensorProvider,
+                                             this);
       }
 
       // Returns initialized Sensor proxy to the client.
@@ -258,8 +259,7 @@
         }
 
         if (this.active_sensor_ == null) {
-          var stub = connection.bindHandleToStub(request.handle, sensor.Sensor);
-          let mockSensor = new MockSensor(stub, this.shared_buffer_handle_,
+          let mockSensor = new MockSensor(request, this.shared_buffer_handle_,
               offset, this.reading_size_in_bytes_, reporting_mode);
           this.active_sensor_ = mockSensor;
         }
@@ -285,21 +285,17 @@
           this.resolve_func_(this.active_sensor_);
         }
 
-        var client_request = new bindings.InterfaceRequest(
-            connection.bindProxy(proxy => {
-              this.active_sensor_.client_ = proxy;
-            }, sensor.SensorClient));
-        return getSensorResponse(init_params, client_request);
+        this.active_sensor_.client_ = new sensor.SensorClientPtr();
+        return getSensorResponse(
+            init_params, bindings.makeRequest(this.active_sensor_.client_));
       }
 
       // Binds object to mojo message pipe
       bindToPipe(pipe) {
-        this.stub_ = connection.bindHandleToStub(
-            pipe, sensor_provider.SensorProvider);
-        bindings.StubBindings(this.stub_).delegate = this;
-        bindings.StubBindings(this.stub_).connectionErrorHandler = () => {
+        this.binding_.bind(pipe);
+        this.binding_.setConnectionErrorHandler(() => {
           this.reset();
-        };
+        });
       }
 
       // Mock functions
@@ -315,8 +311,7 @@
         this.resolve_func_ = null;
         this.max_frequency_ = 60;
         this.is_continuous_ = false;
-        if (this.stub_)
-          bindings.StubBindings(this.stub_).close();
+        this.binding_.close();
       }
 
       // Sets flag that forces mock SensorProvider to fail when getSensor() is
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
index ed870ba..8395550 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
@@ -4,22 +4,18 @@
   'mockBarcodeDetection',
   ['third_party/WebKit/public/platform/modules/shapedetection/barcodedetection.mojom',
    'mojo/public/js/bindings',
-   'mojo/public/js/connection',
    'mojo/public/js/core',
    'content/public/renderer/frame_interfaces',
-  ], (barcodeDetection, bindings, connection, mojo, interfaces) => {
+  ], (barcodeDetection, bindings, mojo, interfaces) => {
 
   class MockBarcodeDetection {
     constructor() {
+      this.bindingSet_ = new bindings.BindingSet(
+          barcodeDetection.BarcodeDetection);
+
       interfaces.addInterfaceOverrideForTesting(
           barcodeDetection.BarcodeDetection.name,
-          pipe => this.bindToPipe(pipe));
-    }
-
-    bindToPipe(pipe) {
-      this.stub_ = connection.bindHandleToStub(pipe,
-                                               barcodeDetection.BarcodeDetection);
-      bindings.StubBindings(this.stub_).delegate = this;
+          handle => this.bindingSet_.addBinding(this, handle));
     }
 
     detect(frame_data, width, height) {
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
index c934619..62220e8 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
@@ -5,30 +5,22 @@
   ['third_party/WebKit/public/platform/modules/shapedetection/facedetection.mojom',
    'third_party/WebKit/public/platform/modules/shapedetection/facedetection_provider.mojom',
    'mojo/public/js/bindings',
-   'mojo/public/js/connection',
    'mojo/public/js/core',
    'content/public/renderer/frame_interfaces',
-  ], (faceDetection, faceDetectionProvider, bindings, connection, mojo, interfaces) => {
+  ], (faceDetection, faceDetectionProvider, bindings, mojo, interfaces) => {
 
   class MockFaceDetectionProvider {
     constructor() {
+      this.bindingSet_ = new bindings.BindingSet(
+          faceDetectionProvider.FaceDetectionProvider);
+
       interfaces.addInterfaceOverrideForTesting(
           faceDetectionProvider.FaceDetectionProvider.name,
-          pipe => this.bindToPipe(pipe));
-    }
-
-    bindToPipe(pipe) {
-      this.stub_ = connection.bindHandleToStub(
-          pipe, faceDetectionProvider.FaceDetectionProvider);
-      bindings.StubBindings(this.stub_).delegate = this;
+          handle => this.bindingSet_.addBinding(this, handle));
     }
 
     createFaceDetection(request, options) {
-      this.mock_service_ = new MockFaceDetection(options);
-      this.mock_service_.stub_ = connection.bindHandleToStub(
-          request.handle, faceDetection.FaceDetection);
-      bindings.StubBindings(this.mock_service_.stub_).delegate =
-          this.mock_service_;
+      this.mock_service_ = new MockFaceDetection(request, options);
     }
 
     getFrameData() {
@@ -45,9 +37,11 @@
   }
 
   class MockFaceDetection {
-    constructor(options) {
+    constructor(request, options) {
       this.maxDetectedFaces_ = options.max_detected_faces;
       this.fastMode_ = options.fast_mode;
+      this.binding_ = new bindings.Binding(faceDetection.FaceDetection, this,
+                                           request);
     }
 
     detect(frame_data, width, height) {
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt
index 46e0b02..94bf551 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt
@@ -15,15 +15,15 @@
 PASS Setting animation-direction to invalid value hello throws 
 PASS Setting animation-direction to invalid value [object Object] throws 
 PASS Setting animation-direction to invalid value CSSKeywordValue throws 
-FAIL Getting animation-direction when it is set to normal assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to reverse assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to alternate assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to alternate-reverse assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting animation-direction when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL getAll for single-valued animation-direction assert_equals: Returned type is incorrect: expected "CSSKeywordValue" but got "CSSStyleValue"
-FAIL getAll for list-valued animation-direction assert_equals: Expected getAll to return an array containing two instances of CSSStyleValue expected 2 but got 1
+PASS Getting animation-direction when it is set to normal 
+PASS Getting animation-direction when it is set to reverse 
+PASS Getting animation-direction when it is set to alternate 
+PASS Getting animation-direction when it is set to alternate-reverse 
+PASS Getting animation-direction when it is set to initial 
+PASS Getting animation-direction when it is set to inherit 
+PASS Getting animation-direction when it is set to unset 
+PASS getAll for single-valued animation-direction 
+PASS getAll for list-valued animation-direction 
 PASS Delete animation-direction removes the value from the styleMap 
 PASS animation-direction shows up in getProperties 
 FAIL Set animation-direction to a sequence assert_equals: expected "normal, normal" but got "normal normal"
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt
index 1cb38a58..8ca5f42 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt
@@ -16,10 +16,10 @@
 PASS Setting bottom to invalid value hello throws 
 PASS Setting bottom to invalid value [object Object] throws 
 PASS Setting bottom to invalid value CSSKeywordValue throws 
-FAIL Getting bottom when it is set to auto assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting bottom when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting bottom when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting bottom when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false
+PASS Getting bottom when it is set to auto 
+PASS Getting bottom when it is set to initial 
+PASS Getting bottom when it is set to inherit 
+PASS Getting bottom when it is set to unset 
 PASS Getting bottom with a CSSSimpleLength whose value is 1px 
 PASS Getting bottom with a CSSSimpleLength whose value is 2% 
 PASS Getting bottom with a CSSSimpleLength whose value is 3em 
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
index 04551e0..e3837b5 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
@@ -219,11 +219,7 @@
       let result = element.styleMap.getAll(propertyName);
       assert_equals(result.length, 2,
           'Expected getAll to return an array containing two instances ' +
-          'of CSSStyleValue');
-      assert_equals(result[0].constructor, CSSStyleValue,
-          'Expected first result to be an instance of CSSStyleValue');
-      assert_equals(result[1].constructor, CSSStyleValue,
-          'Expected second result to be an instance of CSSStyleValue');
+          'of ' + validObject.constructor.name);
       assert_equals(result[0].constructor.name, validObject.constructor.name);
       assert_equals(result[1].constructor.name, validObject.constructor.name);
       assert_equals(result[0].cssText, validObject.cssText);
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt
deleted file mode 100644
index b9af4b0..0000000
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-This is a testharness.js-based test.
-PASS Setting transform to none 
-PASS Setting transform to initial 
-PASS Setting transform to inherit 
-PASS Setting transform to unset 
-PASS Setting transform to CSSTransformValue with value skew(30deg, 0deg) 
-PASS Setting transform to CSSTransformValue with value skew(10rad, 0deg) 
-PASS Setting transform to CSSTransformValue with value skew(2grad, 0deg) 
-PASS Setting transform to CSSTransformValue with value skew(0.2turn, 0deg) 
-PASS Setting transform to CSSTransformValue with value skew(0deg, 30deg) 
-PASS Setting transform to CSSTransformValue with value skew(0deg, 10rad) 
-PASS Setting transform to CSSTransformValue with value skew(0deg, 2grad) 
-PASS Setting transform to CSSTransformValue with value skew(0deg, 0.2turn) 
-PASS Setting transform to CSSTransformValue with value rotate(30deg) 
-PASS Setting transform to CSSTransformValue with value rotate(10rad) 
-PASS Setting transform to CSSTransformValue with value rotate(2grad) 
-PASS Setting transform to CSSTransformValue with value rotate(0.2turn) 
-PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 30deg) 
-PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 10rad) 
-PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 2grad) 
-PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 0.2turn) 
-PASS Setting transform to invalid value CSSSimpleLength throws 
-PASS Setting transform to invalid value null throws 
-PASS Setting transform to invalid value undefined throws 
-PASS Setting transform to invalid value true throws 
-PASS Setting transform to invalid value false throws 
-PASS Setting transform to invalid value 1 throws 
-PASS Setting transform to invalid value hello throws 
-PASS Setting transform to invalid value [object Object] throws 
-PASS Setting transform to invalid value CSSKeywordValue throws 
-FAIL Getting transform when it is set to none assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting transform when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting transform when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false
-FAIL Getting transform when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false
-PASS Getting transform with a CSSTransformValue whose value is skew(30deg, 0deg) 
-PASS Getting transform with a CSSTransformValue whose value is skew(10rad, 0deg) 
-PASS Getting transform with a CSSTransformValue whose value is skew(2grad, 0deg) 
-PASS Getting transform with a CSSTransformValue whose value is skew(0.2turn, 0deg) 
-PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 30deg) 
-PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 10rad) 
-PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 2grad) 
-PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 0.2turn) 
-PASS Getting transform with a CSSTransformValue whose value is rotate(30deg) 
-PASS Getting transform with a CSSTransformValue whose value is rotate(10rad) 
-PASS Getting transform with a CSSTransformValue whose value is rotate(2grad) 
-PASS Getting transform with a CSSTransformValue whose value is rotate(0.2turn) 
-PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 30deg) 
-PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 10rad) 
-PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 2grad) 
-PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 0.2turn) 
-PASS Getting transform when it is set to "skew(45deg)" via a string 
-PASS Getting transform when it is set to "skew(1rad)" via a string 
-PASS Getting transform when it is set to "skew(6.2grad)" via a string 
-PASS Getting transform when it is set to "skew(0.31turn)" via a string 
-PASS Getting transform when it is set to "skewX(45deg)" via a string 
-PASS Getting transform when it is set to "skewX(1rad)" via a string 
-PASS Getting transform when it is set to "skewX(6.2grad)" via a string 
-PASS Getting transform when it is set to "skewX(0.31turn)" via a string 
-PASS Getting transform when it is set to "skewY(45deg)" via a string 
-PASS Getting transform when it is set to "skewY(1rad)" via a string 
-PASS Getting transform when it is set to "skewY(6.2grad)" via a string 
-PASS Getting transform when it is set to "skewY(0.31turn)" via a string 
-PASS Getting transform when it is set to "rotateX(45deg)" via a string 
-PASS Getting transform when it is set to "rotateX(1rad)" via a string 
-PASS Getting transform when it is set to "rotateX(6.2grad)" via a string 
-PASS Getting transform when it is set to "rotateX(0.31turn)" via a string 
-PASS Getting transform when it is set to "rotateY(45deg)" via a string 
-PASS Getting transform when it is set to "rotateY(1rad)" via a string 
-PASS Getting transform when it is set to "rotateY(6.2grad)" via a string 
-PASS Getting transform when it is set to "rotateY(0.31turn)" via a string 
-PASS Getting transform when it is set to "rotateZ(45deg)" via a string 
-PASS Getting transform when it is set to "rotateZ(1rad)" via a string 
-PASS Getting transform when it is set to "rotateZ(6.2grad)" via a string 
-PASS Getting transform when it is set to "rotateZ(0.31turn)" via a string 
-PASS getAll for single-valued transform 
-PASS Delete transform removes the value from the styleMap 
-PASS transform shows up in getProperties 
-PASS Setting transform to a sequence throws 
-PASS Appending to transform throws 
-PASS Unhandled case doesn't crash. 
-PASS Getting transform when it has a rotate with a calc angle does not crash 
-PASS Getting transform when it has a rotate3d with a calc angle does not crash 
-PASS Getting transform when it has a skew with a calc angle does not crash 
-PASS Getting transform when it has a 2-argument skew with a calc angle does not crash 
-PASS Getting transform when it has a skewX with a calc angle does not crash 
-PASS Getting transform when it has a skewY with a calc angle does not crash 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html
index f9257e19..948d002 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html
@@ -12,31 +12,31 @@
 // supporting some of these properties.
 
 test(function() {
-  testElement.style.backgroundColor = 'green';
+  testElement.style.backgroundColor = '#00FF00';
 
   var result = testElement.styleMap.get('background-color');
   assert_equals(result.constructor, CSSStyleValue);
-  assert_equals(result.cssText, 'green');
+  assert_equals(result.cssText, 'rgb(0, 255, 0)');
 }, 'Unsupported property returns a base StyleValue with the correct cssText.');
 
 test(function() {
-  testElement.style.backgroundColor = 'green';
+  testElement.style.backgroundColor = '#00FF00';
 
   secondElement.styleMap.set('background-color', testElement.styleMap.get('background-color'));
 
   var result = secondElement.styleMap.get('background-color');
   assert_equals(result.constructor, CSSStyleValue);
-  assert_equals(result.cssText, 'green');
+  assert_equals(result.cssText, 'rgb(0, 255, 0)');
 }, 'Setting the same property using the result of getting an unknown value works');
 
 test(function() {
-  testElement.style.color = 'green';
+  testElement.style.color = '#00FF00';
 
   secondElement.styleMap.set('border-left-color', testElement.styleMap.get('color'));
 
   var result = secondElement.styleMap.get('border-left-color');
   assert_equals(result.constructor, CSSStyleValue);
-  assert_equals(result.cssText, 'green');
+  assert_equals(result.cssText, 'rgb(0, 255, 0)');
 }, 'Setting a different property using the result of getting an unknown value works');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt
deleted file mode 100644
index 011db21..0000000
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This test verifies that the AudioBasicInspectorNode based nodes work correctly.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node.
-PASS RealtimeAnalyserNode got pulled when connected from upstream node and to destination node.
-PASS RealtimeAnalyserNode didn't get pulled when it should not.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
index a46a49c..840bad8 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
+++ b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
@@ -2,23 +2,24 @@
 
 <html>
 <head>
-<script src="../../resources/js-test.js"></script>
-<script src="../resources/compatibility.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script src="../resources/audit-util.js"></script>
-<script src="../resources/audio-testing.js"></script>
+<script src="../resources/audit.js"></script>
 </head>
 
 <body>
 
-<div id="description"></div>
-<div id="console"></div>
-
 <script>
-description("This test verifies that the AudioBasicInspectorNode based nodes work correctly.");
+// This test verifies that the AudioBasicInspectorNode based nodes work correctly
+
+var audit = Audit.createTaskRunner();
 
 var sampleRate = 44100.0;
-// We carefully arrange the renderLengthInFrames to be a multiple of the AudioNode rendering quantum (AudioNode::ProcessingSizeInFrames)
-// so that AudioSourceNode will not feed tailing zeroes into the context and fail this test.
+// We carefully arrange the renderLengthInFrames to be a multiple of the
+// AudioNode rendering quantum (AudioNode::ProcessingSizeInFrames) so that
+// AudioSourceNode will not feed tailing zeroes into the context and fail this
+// test.
 var renderLengthInFrames = 256;
 var fftSize = 64;
 
@@ -31,106 +32,92 @@
 var analyser;
 
 function constructCommonGraph() {
-    // Create offline audio context.
-    context = new OfflineAudioContext(1, renderLengthInFrames, sampleRate);
-    constantBuffer = createConstantBuffer(context, renderLengthInFrames, 1);
+  // Create offline audio context.
+  context = new OfflineAudioContext(1, renderLengthInFrames, sampleRate);
+  constantBuffer = createConstantBuffer(context, renderLengthInFrames, 1);
 
-    bufferSource = context.createBufferSource();
-    bufferSource.buffer = constantBuffer;
+  bufferSource = context.createBufferSource();
+  bufferSource.buffer = constantBuffer;
 
-    analyser = context.createAnalyser();
-    analyser.fftSize = fftSize;
+  analyser = context.createAnalyser();
+  analyser.fftSize = fftSize;
 
-    bufferSource.connect(analyser);
+  bufferSource.connect(analyser);
 }
 
-function test1Finished() {
-    var timeDomainData = new Uint8Array(fftSize);
-    analyser.getByteTimeDomainData(timeDomainData);
+function test1Finished(should) {
+  var timeDomainData = new Uint8Array(fftSize);
+  analyser.getByteTimeDomainData(timeDomainData);
 
-    if (timeDomainData[0] >= audioDataOne)
-        testPassed("RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node.");
-    else
-        testFailed("RealtimeAnalyserNode failed to get pulled when connected from upstream node but not to downstream node.");
-
-    test2();
+  should(timeDomainData[0] >= audioDataOne,
+      "RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node"
+    )
+    .beTrue();
 }
 
-// To verify the realtimeAnalyser can pull data when there is an upstream node connected to it
-// but it's not connected to a downstream node.
-function test1() {
-    constructCommonGraph();
+// To verify the realtimeAnalyser can pull data when there is an upstream node
+// connected to it but it's not connected to a downstream node.
+audit.define("test1", function (task, should) {
+  constructCommonGraph();
 
-    bufferSource.start(0);
+  bufferSource.start(0);
 
-    context.oncomplete = test1Finished;
-    context.startRendering();
+  context.startRendering()
+    .then(test1Finished(should))
+    .then(task.done.bind(task));
+});
+
+function test2Finished(should) {
+  var timeDomainData = new Uint8Array(fftSize);
+  analyser.getByteTimeDomainData(timeDomainData);
+
+  should(timeDomainData[0] >= audioDataOne,
+      "RealtimeAnalyserNode got pulled when connected from upstream node and to destination node"
+    )
+    .beTrue();
 }
 
-function test2Finished() {
-    var timeDomainData = new Uint8Array(fftSize);
-    analyser.getByteTimeDomainData(timeDomainData);
+// To verify the realtimeAnalyser can process normally when there is an upstream
+// node connected to it and it's also connected to a downstream node which
+// ultimately connect to audio destination.
+audit.define("test2", function (task, should) {
+  constructCommonGraph();
 
-    if (timeDomainData[0] >= audioDataOne)
-        testPassed("RealtimeAnalyserNode got pulled when connected from upstream node and to destination node.");
-    else
-        testFailed("RealtimeAnalyserNode failed to be pulled when connected by upstream node and to destination node.");
+  analyser.connect(context.destination);
 
-    test3();
+  bufferSource.start(0);
+
+  context.startRendering()
+    .then(test2Finished(should))
+    .then(task.done.bind(task));
+});
+
+function test3Finished(should) {
+  var timeDomainData = new Uint8Array(fftSize);
+  analyser.getByteTimeDomainData(timeDomainData);
+
+  // If realtimeAnalyser hasn't pulled any data, the data in buffer will be 0.
+  should(timeDomainData[0] == audioDataZero,
+      "RealtimeAnalyserNode didn't get pulled when it should not")
+    .beTrue();;
 }
 
-// To verify the realtimeAnalyser can process normally when there is an upstream node connected to it
-// and it's also connected to a downstream node which ultimately connect to audio destination.
-function test2() {
-    constructCommonGraph();
+// To verify the realtimeAnalyser will stop pulling if it is connected to a
+// downstream node which is not ultimatly connected to any audio destination.
+audit.define("test3", function (task, should) {
+  constructCommonGraph();
 
-    analyser.connect(context.destination);
+  var gain = context.createGain();
+  analyser.connect(gain);
 
-    bufferSource.start(0);
+  bufferSource.start(0);
 
-    context.oncomplete = test2Finished;
-    context.startRendering();
-}
+  context.startRendering()
+    .then(test3Finished(should))
+    .then(task.done.bind(task));
+});
 
-function test3Finished() {
-    var timeDomainData = new Uint8Array(fftSize);
-    analyser.getByteTimeDomainData(timeDomainData);
-
-    // If realtimeAnalyser hasn't pulled any data, the data in buffer will be 0.
-    if (timeDomainData[0] == audioDataZero)
-        testPassed("RealtimeAnalyserNode didn't get pulled when it should not.");
-    else
-        testFailed("RealtimeAnalyserNode been pulled when it should not.");
-
-    finishJSTest();
-}
-
-// To verify the realtimeAnalyser will stop pulling if it is connected to a downstream node
-// which is not ultimatly connected to any audio destination.
-function test3() {
-    constructCommonGraph();
-
-    var gain = context.createGain();
-    analyser.connect(gain);
-
-    bufferSource.start(0);
-
-    context.oncomplete = test3Finished;
-    context.startRendering();
-}
-
-function runTest() {
-    if (window.testRunner) {
-        testRunner.dumpAsText();
-        testRunner.waitUntilDone();
-    }
-
-    window.jsTestIsAsync = true;
-
-    test1();
-}
-
-runTest();
+audit.run();
 
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
index 7d337e6f..cfadbf8 100644
--- a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
@@ -270,12 +270,16 @@
         // Catch did not happen, so the test is failed.
         failDetail = '${actual} did not throw an exception.';
       } catch (error) {
-        if (this._expected === undefined) {
+        if (this._expected === null) {
+          // The expected error type was not given.
           didThrowCorrectly = true;
-          passDetail = '${actual} threw an exception of ' + error.name + '.';
+          passDetail = '${actual} threw ' + error.name + ': "'
+              + error.message + '".';
         } else if (error.name === this._expected) {
+          // The expected error type match the actual one.
           didThrowCorrectly = true;
-          passDetail = '${actual} threw ${expected} : "' + error.message + '".';
+          passDetail = '${actual} threw ${expected}: "'
+              + error.message + '".';
         } else {
           didThrowCorrectly = false;
           failDetail = '${actual} threw "' + error.name
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
index f38b660..f2fcf6a 100644
--- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
+++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
@@ -10,8 +10,9 @@
 PASS < [numerical] All assertions passed. (total 5 assertions) 
 PASS > [basic] Simple unit tests for basic assertions. 
 PASS   OfflineAudioContext does exist. 
-PASS   Setting foo to 0 did not throw an exception. 
-PASS   function () { var foo = bar; } threw ReferenceError : "bar is not defined". 
+PASS   Setting foo1 to 0 did not throw an exception. 
+PASS   function () { var foo2 = bar; } threw ReferenceError: "bar is not defined". 
+PASS   function () { var foo3 = bar; } threw ReferenceError: "bar is not defined". 
 PASS   3 < 5 is true. 
 PASS   false is false. 
 PASS   1 is equal to 1. 
@@ -23,7 +24,7 @@
 PASS   1 is less than or equal to 1. 
 PASS   Decoding audio data with no argument rejected correctly with TypeError: Failed to execute 'decodeAudioData' on 'BaseAudioContext': 1 argument required, but only 0 present.. 
 PASS   Start OAC rendering resolved correctly. 
-PASS < [basic] All assertions passed. (total 14 assertions) 
+PASS < [basic] All assertions passed. (total 15 assertions) 
 PASS # AUDIT TASK RUNNER FINISHED: 2 tasks ran successfully. 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
index 73a034f..1624a68 100644
--- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
+++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
@@ -16,8 +16,9 @@
       task.describe('Simple unit tests for basic assertions.');
 
       should(OfflineAudioContext, 'OfflineAudioContext').exist();
-      should(function () { var foo = 0; }, 'Setting foo to 0').notThrow();
-      should(function () { var foo = bar; }).throw('ReferenceError');
+      should(function () { var foo1 = 0; }, 'Setting foo1 to 0').notThrow();
+      should(function () { var foo2 = bar; }).throw();
+      should(function () { var foo3 = bar; }).throw('ReferenceError');
       should(3 < 5, '3 < 5').beTrue();
       should(false).beFalse();
       should(1).beEqualTo(1)
diff --git a/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js b/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js
index c1f1736..0e04983 100644
--- a/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js
+++ b/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js
@@ -2,17 +2,18 @@
 
 let mockShareService = loadMojoModules(
     'mockShareService',
-    ['mojo/public/js/router',
+    ['mojo/public/js/bindings',
      'third_party/WebKit/public/platform/modules/webshare/webshare.mojom',
     ]).then(mojo => {
-  let [router, webshare] = mojo.modules;
+  let [bindings, webshare] = mojo.modules;
 
-  class MockShareService extends webshare.ShareService.stubClass {
+  class MockShareService {
     constructor(interfaceProvider) {
-      super();
+      this.bindingSet_ = new bindings.BindingSet(webshare.ShareService);
+
       interfaceProvider.addInterfaceOverrideForTesting(
           webshare.ShareService.name,
-          handle => this.connect_(handle));
+          handle => this.bindingSet_.addBinding(this, handle));
     }
 
     // Returns a Promise that gets rejected if the test should fail.
@@ -23,11 +24,6 @@
       return new Promise((resolve, reject) => {this.reject_ = reject});
     }
 
-    connect_(handle) {
-      this.router_ = new router.Router(handle);
-      this.router_.setIncomingReceiver(this);
-    }
-
     share(title, text, url) {
       let callback = null;
       let result = new Promise(resolve => {callback = resolve;});
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
index 2079a19..5b020d90 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
@@ -17,6 +17,7 @@
 #include "core/dom/custom/CustomElement.h"
 #include "core/events/ErrorEvent.h"
 #include "core/html/HTMLElement.h"
+#include "core/html/imports/HTMLImportsController.h"
 #include "v8.h"
 #include "wtf/Allocator.h"
 
@@ -185,16 +186,30 @@
   // Create an element with the synchronous custom elements flag set.
   // https://dom.spec.whatwg.org/#concept-create-element
 
-  // Create an element and push to the construction stack.
-  // V8HTMLElement::constructorCustom() can only refer to
-  // window.document(), but it is different from the document here
-  // when it is an import document. This is not exactly what the
-  // spec defines, but the non-imports behavior matches to the spec.
-  Element* element = createElementForConstructor(document);
+  // TODO(dominicc): Implement step 5 which constructs customized
+  // built-in elements.
+
+  Element* element = nullptr;
   {
-    ConstructionStackScope constructionStackScope(this, element);
     v8::TryCatch tryCatch(m_scriptState->isolate());
-    element = runConstructor();
+
+    bool isImportDocument = document.importsController() &&
+                            document.importsController()->master() != document;
+    if (isImportDocument) {
+      // V8HTMLElement::constructorCustom() can only refer to
+      // window.document() which is not the import document. Create
+      // elements in import documents ahead of time so they end up in
+      // the right document. This subtly violates recursive
+      // construction semantics, but only in import documents.
+      element = createElementForConstructor(document);
+      DCHECK(!tryCatch.HasCaught());
+
+      ConstructionStackScope constructionStackScope(this, element);
+      element = callConstructor();
+    } else {
+      element = callConstructor();
+    }
+
     if (tryCatch.HasCaught()) {
       exceptionState.rethrowV8Exception(tryCatch.Exception());
       return handleCreateElementSyncException(document, tagName, isolate,
@@ -224,9 +239,9 @@
   v8::TryCatch tryCatch(isolate);
   tryCatch.SetVerbose(true);
 
-  Element* result = runConstructor();
+  Element* result = callConstructor();
 
-  // To report exception thrown from runConstructor()
+  // To report exception thrown from callConstructor()
   if (tryCatch.HasCaught())
     return false;
 
@@ -245,7 +260,7 @@
   return true;
 }
 
-Element* ScriptCustomElementDefinition::runConstructor() {
+Element* ScriptCustomElementDefinition::callConstructor() {
   v8::Isolate* isolate = m_scriptState->isolate();
   DCHECK(ScriptState::current(isolate) == m_scriptState);
   ExecutionContext* executionContext = m_scriptState->getExecutionContext();
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h
index 27eb3dd..eac09b8 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h
@@ -73,7 +73,9 @@
   // Implementations of |CustomElementDefinition|
   ScriptValue getConstructorForScript() final;
   bool runConstructor(Element*) override;
-  Element* runConstructor();
+
+  // Calls the constructor. The script scope, etc. must already be set up.
+  Element* callConstructor();
 
   void runCallback(v8::Local<v8::Function>,
                    Element*,
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
index eb899ff..0a3d410 100644
--- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
@@ -184,9 +184,10 @@
 InterpolationValue CSSColorInterpolationType::maybeConvertInitial(
     const StyleResolverState&,
     ConversionCheckers& conversionCheckers) const {
-  const StyleColor initialColor =
-      ColorPropertyFunctions::getInitialColor(cssProperty());
-  return convertStyleColorPair(initialColor, initialColor);
+  StyleColor initialColor;
+  if (ColorPropertyFunctions::getInitialColor(cssProperty(), initialColor))
+    return convertStyleColorPair(initialColor, initialColor);
+  return nullptr;
 }
 
 InterpolationValue CSSColorInterpolationType::maybeConvertInherit(
diff --git a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
index 7622bb5..2c8357f 100644
--- a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
@@ -142,6 +142,7 @@
     case CSSPropertyBorderLeftColor:
     case CSSPropertyBorderRightColor:
     case CSSPropertyBorderTopColor:
+    case CSSPropertyCaretColor:
     case CSSPropertyColor:
     case CSSPropertyFloodColor:
     case CSSPropertyLightingColor:
diff --git a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp
index 5755a3c..c6dba58 100644
--- a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp
+++ b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp
@@ -8,8 +8,14 @@
 
 namespace blink {
 
-StyleColor ColorPropertyFunctions::getInitialColor(CSSPropertyID property) {
-  return getUnvisitedColor(property, ComputedStyle::initialStyle());
+bool ColorPropertyFunctions::getInitialColor(CSSPropertyID property,
+                                             StyleColor& initialColor) {
+  if (property == CSSPropertyCaretColor)
+    return false;
+  // TODO(rego): Make getUnvisitedColor() return a bool, so we don't need a
+  // special case for caret-color here (http://crbug.com/676295).
+  initialColor = getUnvisitedColor(property, ComputedStyle::initialStyle());
+  return true;
 }
 
 StyleColor ColorPropertyFunctions::getUnvisitedColor(
@@ -26,6 +32,12 @@
       return style.borderTopColor();
     case CSSPropertyBorderBottomColor:
       return style.borderBottomColor();
+    case CSSPropertyCaretColor:
+      // TODO(rego): "auto" value for caret-color should not interpolate
+      // (http://crbug.com/676295).
+      if (style.caretColor().isAutoColor())
+        return StyleColor::currentColor();
+      return style.caretColor().toStyleColor();
     case CSSPropertyColor:
       return style.color();
     case CSSPropertyOutlineColor:
@@ -67,6 +79,12 @@
       return style.visitedLinkBorderTopColor();
     case CSSPropertyBorderBottomColor:
       return style.visitedLinkBorderBottomColor();
+    case CSSPropertyCaretColor:
+      // TODO(rego): "auto" value for caret-color should not interpolate
+      // (http://crbug.com/676295).
+      if (style.visitedLinkCaretColor().isAutoColor())
+        return StyleColor::currentColor();
+      return style.visitedLinkCaretColor().toStyleColor();
     case CSSPropertyColor:
       return style.visitedLinkColor();
     case CSSPropertyOutlineColor:
@@ -114,6 +132,8 @@
     case CSSPropertyBorderTopColor:
       style.setBorderTopColor(color);
       return;
+    case CSSPropertyCaretColor:
+      return style.setCaretColor(color);
     case CSSPropertyColor:
       style.setColor(color);
       return;
@@ -163,6 +183,8 @@
     case CSSPropertyBorderTopColor:
       style.setVisitedLinkBorderTopColor(color);
       return;
+    case CSSPropertyCaretColor:
+      return style.setVisitedLinkCaretColor(color);
     case CSSPropertyColor:
       style.setVisitedLinkColor(color);
       return;
diff --git a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h
index 7fe546a..47fe8c7 100644
--- a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h
+++ b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h
@@ -14,7 +14,7 @@
 
 class ColorPropertyFunctions {
  public:
-  static StyleColor getInitialColor(CSSPropertyID);
+  static bool getInitialColor(CSSPropertyID, StyleColor& initialColor);
   static StyleColor getUnvisitedColor(CSSPropertyID, const ComputedStyle&);
   static StyleColor getVisitedColor(CSSPropertyID, const ComputedStyle&);
   static void setUnvisitedColor(CSSPropertyID, ComputedStyle&, const Color&);
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
index 90d0100..868d4fa 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
@@ -417,6 +417,12 @@
             CSSIdentifierValue::create(CSSValueAuto));
       }
       return createFromLengthBox(style.clip(), style);
+    case CSSPropertyCaretColor:
+      if (style.caretColor().isAutoColor()) {
+        return AnimatableUnknown::create(
+            CSSIdentifierValue::create(CSSValueAuto));
+      }
+      return createFromColor(property, style);
     case CSSPropertyColor:
       return createFromColor(property, style);
     case CSSPropertyFillOpacity:
diff --git a/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp b/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp
index 0970c2c..7249eff 100644
--- a/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp
+++ b/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp
@@ -28,7 +28,8 @@
       m_customPlatformData->fontPlatformData(
           fontDescription.effectiveFontSize(),
           fontDescription.isSyntheticBold(),
-          fontDescription.isSyntheticItalic(), fontDescription.orientation()),
+          fontDescription.isSyntheticItalic(), fontDescription.orientation(),
+          fontDescription.variationSettings()),
       CustomFontData::create());
 }
 
diff --git a/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp b/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp
index e9d3bc9..7094669d 100644
--- a/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp
@@ -44,9 +44,9 @@
   DummyFontFaceSource fontFaceSource;
   // Even if the hash value collide, fontface cache should return different
   // value for different fonts.
-  EXPECT_EQ(simulateHashCalculation(2821), simulateHashCalculation(3346));
-  EXPECT_NE(fontFaceSource.getFontDataForSize(2821),
-            fontFaceSource.getFontDataForSize(3346));
+  EXPECT_EQ(simulateHashCalculation(8775), simulateHashCalculation(418));
+  EXPECT_NE(fontFaceSource.getFontDataForSize(8775),
+            fontFaceSource.getFontDataForSize(418));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index 36a63603..5a30f1d1 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -221,7 +221,7 @@
 break-inside type_name=EBreak
 buffered-rendering svg
 caption-side inherited, keyword_only, independent, keywords=[top|bottom|left|right], initial_keyword=top
-caret-color inherited, custom_all
+caret-color interpolable, inherited, custom_all
 clear
 clip interpolable, converter=convertClip, custom_all
 clip-path interpolable, converter=convertClipPath
diff --git a/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp b/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp
index 122663e..3d2e777 100644
--- a/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp
+++ b/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp
@@ -218,7 +218,8 @@
       m_font->platformDataFromCustomData(fontDescription.effectiveFontSize(),
                                          fontDescription.isSyntheticBold(),
                                          fontDescription.isSyntheticItalic(),
-                                         fontDescription.orientation()),
+                                         fontDescription.orientation(),
+                                         fontDescription.variationSettings()),
       CustomFontData::create());
 }
 
diff --git a/third_party/WebKit/Source/core/css/StyleAutoColor.h b/third_party/WebKit/Source/core/css/StyleAutoColor.h
index c3bb6ea76..c35799d 100644
--- a/third_party/WebKit/Source/core/css/StyleAutoColor.h
+++ b/third_party/WebKit/Source/core/css/StyleAutoColor.h
@@ -34,8 +34,10 @@
   }
 
   StyleColor toStyleColor() const {
+    DCHECK(m_type != ValueType::Auto);
     if (m_type == ValueType::SpecifiedColor)
       return StyleColor(m_color);
+    DCHECK(m_type == ValueType::CurrentColor);
     return StyleColor::currentColor();
   }
 
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
index eb43610..8530c01 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
@@ -21,6 +21,30 @@
   return new CSSKeywordValue(keyword);
 }
 
+CSSKeywordValue* CSSKeywordValue::fromCSSValue(const CSSValue& value) {
+  if (value.isInheritedValue())
+    return new CSSKeywordValue(getValueName(CSSValueInherit));
+  if (value.isInitialValue())
+    return new CSSKeywordValue(getValueName(CSSValueInitial));
+  if (value.isUnsetValue())
+    return new CSSKeywordValue(getValueName(CSSValueUnset));
+  if (value.isIdentifierValue()) {
+    return new CSSKeywordValue(
+        getValueName(toCSSIdentifierValue(value).getValueID()));
+  }
+  if (value.isCustomIdentValue()) {
+    const CSSCustomIdentValue& identValue = toCSSCustomIdentValue(value);
+    if (identValue.isKnownPropertyID()) {
+      // CSSPropertyID represents the LHS of a CSS declaration, and
+      // CSSKeywordValue represents a RHS.
+      return nullptr;
+    }
+    return new CSSKeywordValue(identValue.value());
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
 const AtomicString& CSSKeywordValue::keywordValue() const {
   return m_keywordValue;
 }
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h
index edf620f8..d08d969 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h
+++ b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h
@@ -20,6 +20,7 @@
 
  public:
   static CSSKeywordValue* create(const AtomicString& keyword, ExceptionState&);
+  static CSSKeywordValue* fromCSSValue(const CSSValue&);
 
   StyleValueType type() const override { return KeywordType; }
 
diff --git a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
index 70a5f9b..06565694 100644
--- a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
@@ -6,6 +6,7 @@
 
 #include "core/css/CSSImageValue.h"
 #include "core/css/CSSValue.h"
+#include "core/css/cssom/CSSKeywordValue.h"
 #include "core/css/cssom/CSSNumberValue.h"
 #include "core/css/cssom/CSSSimpleLength.h"
 #include "core/css/cssom/CSSStyleValue.h"
@@ -19,37 +20,51 @@
 
 namespace {
 
-CSSStyleValue* styleValueForPrimitiveValue(
+CSSStyleValue* createStyleValueFromPrimitiveValue(
     const CSSPrimitiveValue& primitiveValue) {
   if (primitiveValue.isNumber())
     return CSSNumberValue::create(primitiveValue.getDoubleValue());
   if (primitiveValue.isLength() || primitiveValue.isPercentage())
     return CSSSimpleLength::fromCSSValue(primitiveValue);
-
   return nullptr;
 }
 
-CSSStyleValue* styleValueForProperty(CSSPropertyID propertyID,
-                                     const CSSValue& value) {
+CSSStyleValue* createStyleValueWithPropertyInternal(CSSPropertyID propertyID,
+                                                    const CSSValue& value) {
   switch (propertyID) {
     case CSSPropertyTransform:
       return CSSTransformValue::fromCSSValue(value);
     default:
-      // TODO(meade): Implement other complex properties.
+      // TODO(meade): Implement other properties.
       break;
   }
+  return nullptr;
+}
 
+CSSStyleValue* createStyleValue(const CSSValue& value) {
+  if (value.isCSSWideKeyword() || value.isIdentifierValue() ||
+      value.isCustomIdentValue())
+    return CSSKeywordValue::fromCSSValue(value);
   if (value.isPrimitiveValue())
-    return styleValueForPrimitiveValue(toCSSPrimitiveValue(value));
+    return createStyleValueFromPrimitiveValue(toCSSPrimitiveValue(value));
   if (value.isVariableReferenceValue())
     return CSSUnparsedValue::fromCSSValue(toCSSVariableReferenceValue(value));
-  if (value.isImageValue())
+  if (value.isImageValue()) {
     return CSSURLImageValue::create(
         toCSSImageValue(value).valueWithURLMadeAbsolute());
-
+  }
   return nullptr;
 }
 
+CSSStyleValue* createStyleValueWithProperty(CSSPropertyID propertyID,
+                                            const CSSValue& value) {
+  CSSStyleValue* styleValue =
+      createStyleValueWithPropertyInternal(propertyID, value);
+  if (styleValue)
+    return styleValue;
+  return createStyleValue(value);
+}
+
 CSSStyleValueVector unsupportedCSSValue(const CSSValue& value) {
   CSSStyleValueVector styleValueVector;
   styleValueVector.push_back(CSSUnsupportedStyleValue::create(value.cssText()));
@@ -60,25 +75,26 @@
 
 CSSStyleValueVector StyleValueFactory::cssValueToStyleValueVector(
     CSSPropertyID propertyID,
-    const CSSValue& value) {
+    const CSSValue& cssValue) {
   CSSStyleValueVector styleValueVector;
-  CSSStyleValue* styleValue = styleValueForProperty(propertyID, value);
+
+  CSSStyleValue* styleValue =
+      createStyleValueWithProperty(propertyID, cssValue);
   if (styleValue) {
     styleValueVector.push_back(styleValue);
     return styleValueVector;
   }
 
-  if (!value.isValueList()) {
-    return unsupportedCSSValue(value);
+  if (!cssValue.isValueList()) {
+    return unsupportedCSSValue(cssValue);
   }
 
   // If it's a list, we can try it as a list valued property.
-  const CSSValueList& cssValueList = toCSSValueList(value);
+  const CSSValueList& cssValueList = toCSSValueList(cssValue);
   for (const CSSValue* innerValue : cssValueList) {
-    styleValue = styleValueForProperty(propertyID, *innerValue);
-    if (!styleValue) {
-      return unsupportedCSSValue(value);
-    }
+    styleValue = createStyleValueWithProperty(propertyID, *innerValue);
+    if (!styleValue)
+      return unsupportedCSSValue(cssValue);
     styleValueVector.push_back(styleValue);
   }
   return styleValueVector;
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp
index b605508..19b0fb2 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp
@@ -375,7 +375,9 @@
   double alpha = 0;
   if (!parseDouble(string, end, terminator, alpha))
     return false;
-  value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0));
+  value = negative
+              ? 0
+              : static_cast<int>(std::min(alpha, 1.0) * nextafter(256.0, 0.0));
   string = end;
   return true;
 }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp
index a464c2d..250a261 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/css/parser/CSSParserFastPaths.h"
 
+#include "core/css/CSSColorValue.h"
 #include "core/css/CSSIdentifierValue.h"
 #include "core/css/CSSValueList.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -98,4 +99,12 @@
   ASSERT_EQ(nullptr, value);
 }
 
+TEST(CSSParserFastPathsTest, ParseColorWithLargeAlpha) {
+  CSSValue* value = CSSParserFastPaths::parseColor("rgba(0,0,0,1893205797.13)",
+                                                   HTMLStandardMode);
+  EXPECT_NE(nullptr, value);
+  EXPECT_TRUE(value->isColorValue());
+  EXPECT_EQ(Color::black, toCSSColorValue(*value).value());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp
index 375a0cc..3709e4a 100644
--- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp
@@ -97,9 +97,9 @@
   return effectiveSizeDefaultValue();
 }
 
-unsigned SizesAttributeParser::effectiveSizeDefaultValue() {
+float SizesAttributeParser::effectiveSizeDefaultValue() {
   // Returning the equivalent of "100vw"
-  return m_mediaValues->viewportWidth();
+  return clampTo<float>(m_mediaValues->viewportWidth());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h
index e1fd557..ed96f556 100644
--- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h
+++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h
@@ -27,7 +27,7 @@
   float effectiveSize();
   bool calculateLengthInPixels(CSSParserTokenRange, float& result);
   bool mediaConditionMatches(MediaQuerySet* mediaCondition);
-  unsigned effectiveSizeDefaultValue();
+  float effectiveSizeDefaultValue();
 
   Member<MediaQuerySet> m_mediaCondition;
   Member<MediaValues> m_mediaValues;
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp b/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp
index 3011b67..972e5fd8 100644
--- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp
+++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp
@@ -96,4 +96,85 @@
   }
 }
 
+TEST(SizesAttributeParserTest, FloatViewportWidth) {
+  TestCase testCases[] = {
+      {"screen", 500.5},
+      {"(min-width:500px)", 500.5},
+      {"(min-width:500px) 200px", 200},
+      {"(min-width:500px) 50vw", 250.25},
+      {"(min-width:500px) 200px, 400px", 200},
+      {"400px, (min-width:500px) 200px", 400},
+      {"40vw, (min-width:500px) 201px", 200.2},
+      {"(min-width:500px) 201px, 40vw", 201},
+      {"(min-width:5000px) 40vw, 201px", 201},
+      {"(min-width:500px) calc(201px), calc(40vw)", 201},
+      {"(min-width:5000px) calc(40vw), calc(201px)", 201},
+      {"(min-width:5000px) 200px, 400px", 400},
+      {"(blalbadfsdf) 200px, 400px", 400},
+      {"0", 0},
+      {"-0", 0},
+      {"1", 500.5},
+      {"300px, 400px", 300},
+      {"(min-width:5000px) 200px, (min-width:500px) 400px", 400},
+      {"", 500.5},
+      {"  ", 500.5},
+      {" /**/ ", 500.5},
+      {" /**/ 300px", 300},
+      {"300px /**/ ", 300},
+      {" /**/ (min-width:500px) /**/ 300px", 300},
+      {"-100px, 200px", 200},
+      {"-50vw, 20vw", 100.1},
+      {"50asdf, 200px", 200},
+      {"asdf, 200px", 200},
+      {"(max-width: 3000px) 200w, 400w", 500.5},
+      {",, , /**/ ,200px", 200},
+      {"50vw", 250.25},
+      {"50vh", 300},
+      {"50vmin", 250.25},
+      {"50vmax", 300},
+      {"5em", 80},
+      {"5rem", 80},
+      {"calc(40vw*2)", 400.4},
+      {"(min-width:5000px) calc(5000px/10), (min-width:500px) calc(1200px/3)",
+       400},
+      {"(min-width:500px) calc(1200/3)", 500.5},
+      {"(min-width:500px) calc(1200px/(0px*14))", 500.5},
+      {"(max-width: 3000px) 200px, 400px", 200},
+      {"(max-width: 3000px) 20em, 40em", 320},
+      {"(max-width: 3000px) 0, 40em", 0},
+      {"(max-width: 3000px) 0px, 40em", 0},
+      {"(max-width: 3000px) 50vw, 40em", 250.25},
+      {"(max-width: 3000px) 50px, 40vw", 50},
+      {"((),1px", 500.5},
+      {"{{},1px", 500.5},
+      {"[[],1px", 500.5},
+      {"x(x(),1px", 500.5},
+      {"(max-width: 3000px) 50.5px, 40vw", 50.5},
+      {"not (blabla) 50px, 40vw", 200.2},
+      {"not (max-width: 100px) 50px, 40vw", 50},
+      {0, 0}  // Do not remove the terminator line.
+  };
+
+  MediaValuesCached::MediaValuesCachedData data;
+  data.viewportWidth = 500.5;
+  data.viewportHeight = 600;
+  data.deviceWidth = 500;
+  data.deviceHeight = 500;
+  data.devicePixelRatio = 2.0;
+  data.colorBitsPerComponent = 24;
+  data.monochromeBitsPerComponent = 0;
+  data.primaryPointerType = PointerTypeFine;
+  data.defaultFontSize = 16;
+  data.threeDEnabled = true;
+  data.mediaType = MediaTypeNames::screen;
+  data.strictMode = true;
+  data.displayMode = WebDisplayModeBrowser;
+  MediaValues* mediaValues = MediaValuesCached::create(data);
+
+  for (unsigned i = 0; testCases[i].input; ++i) {
+    SizesAttributeParser parser(mediaValues, testCases[i].input);
+    ASSERT_EQ(testCases[i].effectiveSize, parser.length());
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
index d64c35f..710af22 100644
--- a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
@@ -421,6 +421,11 @@
     case CSSPropertyBoxShadow:
       style->setBoxShadow(toAnimatableShadow(value)->getShadowList());
       return;
+    case CSSPropertyCaretColor:
+      style->setCaretColor(toAnimatableColor(value)->getColor());
+      style->setVisitedLinkCaretColor(
+          toAnimatableColor(value)->visitedLinkColor());
+      return;
     case CSSPropertyClip:
       style->setClip(animatableValueToLengthBox(value, state));
       return;
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn
index 85a48ab..157594e 100644
--- a/third_party/WebKit/Source/core/editing/BUILD.gn
+++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -234,6 +234,7 @@
     "VisibleSelectionTest.cpp",
     "VisibleUnitsTest.cpp",
     "commands/ApplyBlockElementCommandTest.cpp",
+    "commands/ApplyStyleCommandTest.cpp",
     "commands/DeleteSelectionCommandTest.cpp",
     "commands/InsertListCommandTest.cpp",
     "commands/ReplaceSelectionCommandTest.cpp",
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h
index a2ba527..036b8b6 100644
--- a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h
+++ b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h
@@ -38,7 +38,7 @@
 
 enum ShouldIncludeTypingStyle { IncludeTypingStyle, IgnoreTypingStyle };
 
-class ApplyStyleCommand final : public CompositeEditCommand {
+class CORE_EXPORT ApplyStyleCommand final : public CompositeEditCommand {
  public:
   enum EPropertyLevel { PropertyDefault, ForceBlockProperties };
   enum InlineStyleRemovalMode { RemoveIfNeeded, RemoveAlways, RemoveNone };
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp
new file mode 100644
index 0000000..5357b8e3
--- /dev/null
+++ b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/editing/commands/ApplyStyleCommand.h"
+
+#include "core/css/StylePropertySet.h"
+#include "core/dom/Document.h"
+#include "core/editing/EditingTestBase.h"
+#include "core/editing/FrameSelection.h"
+#include "core/frame/LocalFrame.h"
+
+namespace blink {
+
+class ApplyStyleCommandTest : public EditingTestBase {};
+
+// This is a regression test for https://crbug.com/675727
+TEST_F(ApplyStyleCommandTest, RemoveRedundantBlocksWithStarEditableStyle) {
+  // The second <div> below is redundant from Blink's perspective (no siblings
+  // && no attributes) and will be removed by
+  // |DeleteSelectionCommand::removeRedundantBlocks()|.
+  setBodyContent(
+      "<div><div>"
+      "<div></div>"
+      "<ul>"
+      "<li>"
+      "<div></div>"
+      "<input>"
+      "<style> * {-webkit-user-modify: read-write;}</style><div></div>"
+      "</li>"
+      "</ul></div></div>");
+
+  Element* li = document().querySelector("li");
+
+  LocalFrame* frame = document().frame();
+  frame->selection().setSelection(
+      SelectionInDOMTree::Builder()
+          .collapse(Position(li, PositionAnchorType::BeforeAnchor))
+          .build());
+
+  MutableStylePropertySet* style =
+      MutableStylePropertySet::create(HTMLQuirksMode);
+  style->setProperty(CSSPropertyTextAlign, "center");
+  ApplyStyleCommand::create(document(), EditingStyle::create(style),
+                            InputEvent::InputType::FormatJustifyCenter,
+                            ApplyStyleCommand::ForceBlockProperties)
+      ->apply(EditCommandSource::kDOM);
+  // Shouldn't crash.
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
index 2f1bc1a..028d0fb 100644
--- a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
@@ -1022,6 +1022,7 @@
   Element* rootElement = rootEditableElement(*node);
 
   while (node != rootElement) {
+    ABORT_EDITING_COMMAND_IF(!node);
     if (isRemovableBlock(node)) {
       if (node == m_endingPosition.anchorNode())
         updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node);
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
index c7c0ecd..85fbcce8 100644
--- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
@@ -201,6 +201,11 @@
     if (!lastTypingCommand->willAddTypingToOpenCommand(
             source, InputEvent::InputType::DeleteContentBackward))
       return;
+
+    // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+    // needs to be audited.  See http://crbug.com/590369 for more details.
+    document.updateStyleAndLayoutIgnorePendingStylesheets();
+
     // InputMethodController uses this function to delete composition
     // selection.  It won't be aborted.
     lastTypingCommand->deleteSelection(options & SmartDelete,
@@ -230,6 +235,12 @@
         if (!lastTypingCommand->willAddTypingToOpenCommand(
                 source, InputEvent::InputType::DeleteContentBackward))
           return;
+
+        // TODO(editing-dev): The use of
+        // updateStyleAndLayoutIgnorePendingStylesheets needs to be audited.
+        // See http://crbug.com/590369 for more details.
+        document.updateStyleAndLayoutIgnorePendingStylesheets();
+
         EditingState editingState;
         lastTypingCommand->deleteKeyPressed(granularity, options & KillRing,
                                             &editingState);
@@ -259,6 +270,11 @@
       if (!lastTypingCommand->willAddTypingToOpenCommand(
               source, InputEvent::InputType::DeleteContentForward))
         return;
+
+      // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+      // needs to be audited.  See http://crbug.com/590369 for more details.
+      document.updateStyleAndLayoutIgnorePendingStylesheets();
+
       lastTypingCommand->forwardDeleteKeyPressed(
           granularity, options & KillRing, editingState);
       return;
@@ -379,6 +395,11 @@
     if (!lastTypingCommand->willAddTypingToOpenCommand(
             source, InputEvent::InputType::InsertText, newText))
       return;
+
+    // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+    // needs to be audited.  See http://crbug.com/590369 for more details.
+    document.updateStyleAndLayoutIgnorePendingStylesheets();
+
     EditingState editingState;
     lastTypingCommand->insertText(newText, options & SelectInsertedText,
                                   &editingState);
@@ -433,6 +454,11 @@
     if (!lastTypingCommand->willAddTypingToOpenCommand(
             source, InputEvent::InputType::InsertLineBreak))
       return false;
+
+    // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+    // needs to be audited.  See http://crbug.com/590369 for more details.
+    document.updateStyleAndLayoutIgnorePendingStylesheets();
+
     EditingState editingState;
     lastTypingCommand->insertLineBreak(&editingState);
     return !editingState.isAborted();
@@ -449,6 +475,11 @@
     if (!lastTypingCommand->willAddTypingToOpenCommand(
             source, InputEvent::InputType::InsertParagraph))
       return false;
+
+    // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+    // needs to be audited.  See http://crbug.com/590369 for more details.
+    document.updateStyleAndLayoutIgnorePendingStylesheets();
+
     EditingState editingState;
     lastTypingCommand->insertParagraphSeparatorInQuotedContent(&editingState);
     return !editingState.isAborted();
@@ -467,6 +498,11 @@
     if (!lastTypingCommand->willAddTypingToOpenCommand(
             source, InputEvent::InputType::InsertParagraph))
       return false;
+
+    // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
+    // needs to be audited.  See http://crbug.com/590369 for more details.
+    document.updateStyleAndLayoutIgnorePendingStylesheets();
+
     EditingState editingState;
     lastTypingCommand->insertParagraphSeparator(&editingState);
     return !editingState.isAborted();
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index b6f4c38..84c469ea 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -396,7 +396,7 @@
   InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(context, "close",
                                                               true);
 
-  page->chromeClient().closeWindowSoon();
+  page->closeSoon();
 
   // So as to make window.closed return the expected result
   // after window.close(), separately record the to-be-closed
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 9c5cfa2..c47c1e0 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -1323,6 +1323,11 @@
     owner->setNeedsPaintPropertyUpdate();
 }
 
+void FrameView::setSubtreeNeedsPaintPropertyUpdate() {
+  setNeedsPaintPropertyUpdate();
+  layoutView()->setSubtreeNeedsPaintPropertyUpdate();
+}
+
 IntRect FrameView::computeVisibleArea() {
   // Return our clipping bounds in the root frame.
   IntRect us(frameRect());
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h
index 4e0f8ed2..6f8b92d 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.h
+++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -756,17 +756,9 @@
   }
   bool needsPaintPropertyUpdate() const { return m_needsPaintPropertyUpdate; }
 
-  // Set when the whole frame subtree needs full paint invalidation and paint
-  // property update, e.g. when beginning or finishing printing.
-  void setShouldInvalidateAllPaintAndPaintProperties() {
-    m_shouldInvalidateAllPaintAndPaintProperties = true;
-  }
-  void clearShouldInvalidateAllPaintAndPaintProperties() {
-    m_shouldInvalidateAllPaintAndPaintProperties = true;
-  }
-  bool shouldInvalidateAllPaintAndPaintProperties() const {
-    return m_shouldInvalidateAllPaintAndPaintProperties;
-  }
+  // Set when the whole frame subtree needs full paint property update,
+  // e.g. when beginning or finishing printing.
+  void setSubtreeNeedsPaintPropertyUpdate();
 
   // TODO(ojan): Merge this with IntersectionObserver once it lands.
   IntRect computeVisibleArea();
@@ -1147,7 +1139,6 @@
   // Whether the paint properties need to be updated. For more details, see
   // FrameView::needsPaintPropertyUpdate().
   bool m_needsPaintPropertyUpdate;
-  bool m_shouldInvalidateAllPaintAndPaintProperties;
 
   // This is set on the local root frame view only.
   DocumentLifecycle::LifecycleState m_currentUpdateLifecyclePhasesTargetState;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index d688177a..36b7171 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -593,7 +593,7 @@
   }
 
   if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
-    view()->setShouldInvalidateAllPaintAndPaintProperties();
+    view()->setSubtreeNeedsPaintPropertyUpdate();
 
   if (!printing)
     document()->setPrinting(Document::NotPrinting);
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 590b0749..c5d7a1f 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1400,6 +1400,7 @@
     V8PeriodicWave_Constructor = 1721,
     V8StereoPannerNode_Constructor = 1722,
     V8WaveShaperNode_Constructor = 1723,
+    V8Headers_GetAll_Method = 1724,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
index 058f5aa..578582d2 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
@@ -433,6 +433,7 @@
 //                   / "*"
 // host-char         = ALPHA / DIGIT / "-"
 //
+// static
 bool SourceListDirective::parseHost(
     const UChar* begin,
     const UChar* end,
@@ -447,29 +448,34 @@
 
   const UChar* position = begin;
 
+  // Parse "*" or [ "*." ].
   if (skipExactly<UChar>(position, end, '*')) {
     hostWildcard = CSPSource::HasWildcard;
 
-    if (position == end)
+    if (position == end) {
+      // "*"
       return true;
+    }
 
     if (!skipExactly<UChar>(position, end, '.'))
       return false;
   }
-
   const UChar* hostBegin = position;
 
+  // Parse 1*host-hcar.
+  if (!skipExactly<UChar, isHostCharacter>(position, end))
+    return false;
+  skipWhile<UChar, isHostCharacter>(position, end);
+
+  // Parse *( "." 1*host-char ).
   while (position < end) {
+    if (!skipExactly<UChar>(position, end, '.'))
+      return false;
     if (!skipExactly<UChar, isHostCharacter>(position, end))
       return false;
-
     skipWhile<UChar, isHostCharacter>(position, end);
-
-    if (position < end && !skipExactly<UChar>(position, end, '.'))
-      return false;
   }
 
-  DCHECK(position == end);
   host = String(hostBegin, end - hostBegin);
   return true;
 }
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
index c9a78360..425680c 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
@@ -59,6 +59,7 @@
   FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetIntersectNonces);
   FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetIntersectHashes);
   FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetSources);
+  FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, ParseHost);
   FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, GetSourceVector);
   FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, OperativeDirectiveGivenType);
 
@@ -71,10 +72,10 @@
                    CSPSource::WildcardDisposition&,
                    CSPSource::WildcardDisposition&);
   bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
-  bool parseHost(const UChar* begin,
-                 const UChar* end,
-                 String& host,
-                 CSPSource::WildcardDisposition&);
+  static bool parseHost(const UChar* begin,
+                        const UChar* end,
+                        String& host,
+                        CSPSource::WildcardDisposition&);
   bool parsePort(const UChar* begin,
                  const UChar* end,
                  int& port,
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp
index 73697a5..c0cca7e 100644
--- a/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp
@@ -1336,4 +1336,40 @@
   }
 }
 
+TEST_F(SourceListDirectiveTest, ParseHost) {
+  struct TestCase {
+    String sources;
+    bool expected;
+  } cases[] = {
+      // Wildcard.
+      {"*", true},
+      {"*.", false},
+      {"*.a", true},
+      {"a.*.a", false},
+      {"a.*", false},
+
+      // Dots.
+      {"a.b.c", true},
+      {"a.b.", false},
+      {".b.c", false},
+      {"a..c", false},
+
+      // Valid/Invalid characters.
+      {"az09-", true},
+      {"+", false},
+  };
+
+  for (const auto& test : cases) {
+    String host;
+    CSPSource::WildcardDisposition disposition = CSPSource::NoWildcard;
+    Vector<UChar> characters;
+    test.sources.appendTo(characters);
+    const UChar* start = characters.data();
+    const UChar* end = start + characters.size();
+    EXPECT_EQ(test.expected,
+              SourceListDirective::parseHost(start, end, host, disposition))
+        << "SourceListDirective::parseHost fail to parse: " << test.sources;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index b23ea4ea..2bf68aa 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -115,7 +115,7 @@
   DCHECK(ImageBuffer::canCreateImageBuffer(size));
   sk_sp<SkSurface> surface =
       SkSurface::MakeRasterN32Premul(size.width(), size.height());
-  return surface->makeImageSnapshot();
+  return surface ? surface->makeImageSnapshot() : nullptr;
 }
 
 PassRefPtr<Image> createTransparentImage(const IntSize& size) {
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
index bb6717f..aaa9118 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
@@ -103,6 +103,9 @@
   if (isHTMLTemplateElement(*task.parent))
     task.parent = toHTMLTemplateElement(task.parent.get())->content();
 
+  // https://html.spec.whatwg.org/#insert-a-foreign-element
+  // 3.1, (3) Push (pop) an element queue
+  CEReactionsScope reactions;
   if (task.nextChild)
     task.parent->parserInsertBefore(task.child.get(), *task.nextChild);
   else
diff --git a/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp b/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp
index 61a3c8e..50f2d001 100644
--- a/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp
+++ b/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp
@@ -32,9 +32,7 @@
   // Jump to the first interesting column set.
   m_currentColumnSet = flowThread.columnSetAtBlockOffset(
       m_logicalTopInFlowThread, LayoutBox::AssociateWithLatterPage);
-  if (!m_currentColumnSet ||
-      m_currentColumnSet->logicalTopInFlowThread() >=
-          m_logicalBottomInFlowThread) {
+  if (!m_currentColumnSet) {
     setAtEnd();
     return;
   }
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 66b2663d..1c8e33b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1693,22 +1693,37 @@
       screenArea);
 }
 
-void LayoutBox::frameRectChanged() {
+void LayoutBox::locationChanged() {
+  // The location may change because of layout of other objects. Should check
+  // this object for paint invalidation.
+  if (!needsLayout())
+    setMayNeedPaintInvalidation();
+}
+
+void LayoutBox::sizeChanged() {
+  // The size may change because of layout of other objects. Should check this
+  // object for paint invalidation.
+  if (!needsLayout())
+    setMayNeedPaintInvalidation();
+
   if (node() && node()->isElementNode()) {
     Element& element = toElement(*node());
     element.setNeedsResizeObserverUpdate();
   }
-  // The frame rect may change because of layout of other objects.
-  // Should check this object for paint invalidation.
-  if (!needsLayout())
-    setMayNeedPaintInvalidation();
 
-  // The overflow clip paint property depends on the border box rect through
-  // overflowClipRect(). The border box rect's size equals the frame rect's
-  // size, so we trigger a paint property update when the framerect changes.
-  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
-      shouldClipOverflow())
-    setNeedsPaintPropertyUpdate();
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    if (shouldClipOverflow()) {
+      // The overflow clip paint property depends on the border box rect through
+      // overflowClipRect(). The border box rect's size equals the frame rect's
+      // size so we trigger a paint property update when the frame rect changes.
+      setNeedsPaintPropertyUpdate();
+    } else if (styleRef().hasTransform()) {
+      // The transform paint property's origin depends on the frame rect because
+      // transform-origin can be sized with lengths relative to the frame rect.
+      // See: PaintPropertyTreeBuilder::updateTransform(...).
+      setNeedsPaintPropertyUpdate();
+    }
+  }
 }
 
 bool LayoutBox::intersectsVisibleViewport() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index 795abf62..f8a4088 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -212,25 +212,25 @@
     if (x == m_frameRect.x())
       return;
     m_frameRect.setX(x);
-    frameRectChanged();
+    locationChanged();
   }
   void setY(LayoutUnit y) {
     if (y == m_frameRect.y())
       return;
     m_frameRect.setY(y);
-    frameRectChanged();
+    locationChanged();
   }
   void setWidth(LayoutUnit width) {
     if (width == m_frameRect.width())
       return;
     m_frameRect.setWidth(width);
-    frameRectChanged();
+    sizeChanged();
   }
   void setHeight(LayoutUnit height) {
     if (height == m_frameRect.height())
       return;
     m_frameRect.setHeight(height);
-    frameRectChanged();
+    sizeChanged();
   }
 
   LayoutUnit logicalLeft() const {
@@ -324,7 +324,7 @@
     if (location == m_frameRect.location())
       return;
     m_frameRect.setLocation(location);
-    frameRectChanged();
+    locationChanged();
   }
 
   // The ancestor box that this object's location and physicalLocation are
@@ -342,13 +342,13 @@
     if (size == m_frameRect.size())
       return;
     m_frameRect.setSize(size);
-    frameRectChanged();
+    sizeChanged();
   }
   void move(LayoutUnit dx, LayoutUnit dy) {
     if (!dx && !dy)
       return;
     m_frameRect.move(dx, dy);
-    frameRectChanged();
+    locationChanged();
   }
 
   // This function is in the container's coordinate system, meaning
@@ -356,10 +356,8 @@
   // inline-start/block-start margins.
   LayoutRect frameRect() const { return m_frameRect; }
   void setFrameRect(const LayoutRect& rect) {
-    if (rect == m_frameRect)
-      return;
-    m_frameRect = rect;
-    frameRectChanged();
+    setLocation(rect.location());
+    setSize(rect.size());
   }
 
   // Note that those functions have their origin at this box's CSS border box.
@@ -1495,7 +1493,8 @@
   bool isBox() const =
       delete;  // This will catch anyone doing an unnecessary check.
 
-  void frameRectChanged();
+  void locationChanged();
+  void sizeChanged();
 
   virtual bool isInSelfHitTestingPhase(HitTestAction hitTestAction) const {
     return hitTestAction == HitTestForeground;
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index 5beaba00..6333e60 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -430,8 +430,10 @@
 }
 
 void LayoutView::setShouldDoFullPaintInvalidationForViewAndAllDescendants() {
-  DCHECK(!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled());
-  setShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this);
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
+  else
+    setShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this);
 }
 
 void LayoutView::invalidatePaintForViewAndCompositedLayers() {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index c4a4410b..a0be580 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -181,7 +181,8 @@
     NGConstraintSpace* constraint_space,
     NGBreakToken* break_token)
     : NGLayoutAlgorithm(kBlockLayoutAlgorithm),
-      state_(kStateInit),
+      layout_state_(kStateInit),
+      compute_minmax_state_(kStateInit),
       style_(style),
       first_child_(first_child),
       constraint_space_(constraint_space),
@@ -190,20 +191,68 @@
   DCHECK(style_);
 }
 
+NGLayoutAlgorithm::MinAndMaxState
+NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes(
+    MinAndMaxContentSizes* sizes) {
+  switch (compute_minmax_state_) {
+    case kStateInit:
+      pending_minmax_sizes_.min_content = pending_minmax_sizes_.max_content =
+          LayoutUnit();
+      // Size-contained elements don't consider their contents for intrinsic
+      // sizing.
+      if (style_->containsSize())
+        return kSuccess;
+      current_minmax_child_ = first_child_;
+      compute_minmax_state_ = kStateChildLayout;
+    case kStateChildLayout:
+      // TODO: handle floats & orthogonal children
+      if (current_minmax_child_) {
+        Optional<MinAndMaxContentSizes> child_minmax;
+        if (NeedMinAndMaxContentSizesForContentContribution(
+                *current_minmax_child_->Style())) {
+          child_minmax = MinAndMaxContentSizes();
+          if (!current_minmax_child_->ComputeMinAndMaxContentSizes(
+                  &*child_minmax))
+            return kPending;
+        }
+        MinAndMaxContentSizes child_sizes = ComputeMinAndMaxContentContribution(
+            *current_minmax_child_->Style(), child_minmax);
+        pending_minmax_sizes_.min_content = std::max(
+            pending_minmax_sizes_.min_content, child_sizes.min_content);
+        pending_minmax_sizes_.max_content = std::max(
+            pending_minmax_sizes_.max_content, child_sizes.max_content);
+
+        current_minmax_child_ = current_minmax_child_->NextSibling();
+        if (current_minmax_child_)
+          return kPending;
+      }
+
+      *sizes = pending_minmax_sizes_;
+      sizes->max_content = std::max(sizes->min_content, sizes->max_content);
+      compute_minmax_state_ = kStateInit;
+      return kSuccess;
+    default:
+      NOTREACHED();
+      return kSuccess;
+  };
+}
+
 NGLayoutStatus NGBlockLayoutAlgorithm::Layout(
     NGPhysicalFragmentBase* child_fragment,
     NGPhysicalFragmentBase** fragment_out,
     NGLayoutAlgorithm** algorithm_out) {
-  switch (state_) {
+  switch (layout_state_) {
     case kStateInit: {
+      WTF::Optional<MinAndMaxContentSizes> sizes;
+      if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) {
+        sizes = MinAndMaxContentSizes();
+        if (ComputeMinAndMaxContentSizes(&*sizes) == kPending)
+          return kNotFinished;
+      }
+
       border_and_padding_ =
           ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style());
 
-      WTF::Optional<MinAndMaxContentSizes> sizes;
-      if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) {
-        // TODOO(layout-ng): Implement
-        sizes = MinAndMaxContentSizes();
-      }
       LayoutUnit inline_size =
           ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes);
       LayoutUnit adjusted_inline_size =
@@ -238,7 +287,7 @@
       builder_->SetInlineSize(inline_size).SetBlockSize(block_size);
 
       current_child_ = first_child_;
-      state_ = kStatePrepareForChildLayout;
+      layout_state_ = kStatePrepareForChildLayout;
       return kNotFinished;
     }
     case kStatePrepareForChildLayout: {
@@ -255,11 +304,11 @@
         space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
         *algorithm_out = NGLayoutInputNode::AlgorithmForInputNode(
             current_child_, space_for_current_child_);
-        state_ = kStateChildLayout;
+        layout_state_ = kStateChildLayout;
         return kChildAlgorithmRequired;
       }
 
-      state_ = kStateFinalize;
+      layout_state_ = kStateFinalize;
       return kNotFinished;
     }
     case kStateChildLayout: {
@@ -274,7 +323,7 @@
           ConstraintSpace().WritingMode(), ConstraintSpace().Direction(),
           toNGPhysicalFragment(child_fragment)));
       current_child_ = current_child_->NextSibling();
-      state_ = kStatePrepareForChildLayout;
+      layout_state_ = kStatePrepareForChildLayout;
       return kNotFinished;
     }
     case kStateFinalize: {
@@ -288,7 +337,7 @@
           .SetInlineOverflow(max_inline_size_)
           .SetBlockOverflow(content_size_);
       *fragment_out = builder_->ToFragment();
-      state_ = kStateInit;
+      layout_state_ = kStateInit;
       return kNewFragment;
     }
   };
@@ -446,9 +495,8 @@
   // inline axis)
   // We have to keep this commented out for now until we correctly compute
   // min/max content sizes in Layout().
-  // bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock
-  // || CurrentChildStyle().isFloating();
-  bool shrink_to_fit = false;
+  bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock ||
+                       CurrentChildStyle().isFloating();
   DCHECK(current_child_);
   space_builder_
       ->SetIsNewFormattingContext(
@@ -474,6 +522,7 @@
   visitor->trace(space_builder_);
   visitor->trace(space_for_current_child_);
   visitor->trace(current_child_);
+  visitor->trace(current_minmax_child_);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index c9c4a70..9ea2ca16 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -36,6 +36,7 @@
                          NGConstraintSpace* space,
                          NGBreakToken* break_token = nullptr);
 
+  MinAndMaxState ComputeMinAndMaxContentSizes(MinAndMaxContentSizes*) override;
   NGLayoutStatus Layout(NGPhysicalFragmentBase*,
                         NGPhysicalFragmentBase**,
                         NGLayoutAlgorithm**) override;
@@ -98,13 +99,14 @@
 
   const ComputedStyle& Style() const { return *style_; }
 
-  enum State {
+  enum LayoutState {
     kStateInit,
     kStatePrepareForChildLayout,
     kStateChildLayout,
     kStateFinalize
   };
-  State state_;
+  LayoutState layout_state_;
+  LayoutState compute_minmax_state_;
 
   RefPtr<const ComputedStyle> style_;
 
@@ -115,6 +117,8 @@
   Member<NGConstraintSpaceBuilder> space_builder_;
   Member<NGConstraintSpace> space_for_current_child_;
   Member<NGBlockNode> current_child_;
+  Member<NGBlockNode> current_minmax_child_;
+  MinAndMaxContentSizes pending_minmax_sizes_;
 
   NGBoxStrut border_and_padding_;
   LayoutUnit content_size_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
index df0aee4d..ec08a06 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -20,12 +20,14 @@
 
 NGConstraintSpace* ConstructConstraintSpace(NGWritingMode writing_mode,
                                             TextDirection direction,
-                                            NGLogicalSize size) {
+                                            NGLogicalSize size,
+                                            bool shrink_to_fit = false) {
   return NGConstraintSpaceBuilder(writing_mode)
       .SetAvailableSize(size)
       .SetPercentageResolutionSize(size)
       .SetTextDirection(direction)
       .SetWritingMode(writing_mode)
+      .SetIsShrinkToFit(shrink_to_fit)
       .ToConstraintSpace();
 }
 
@@ -49,6 +51,25 @@
     return toNGPhysicalFragment(fragment);
   }
 
+  MinAndMaxContentSizes RunComputeMinAndMax(NGBlockNode* first_child) {
+    // The constraint space is not used for min/max computation, but we need
+    // it to create the algorithm.
+    NGConstraintSpace* space =
+        ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::Ltr,
+                                 NGLogicalSize(LayoutUnit(), LayoutUnit()));
+    NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space);
+    MinAndMaxContentSizes sizes;
+    NGLayoutAlgorithm::MinAndMaxState state;
+    while ((state = algorithm.ComputeMinAndMaxContentSizes(&sizes)) !=
+           NGLayoutAlgorithm::kSuccess) {
+      EXPECT_NE(NGLayoutAlgorithm::kNotImplemented, state);
+      // shouldn't happen but let's avoid an infinite loop
+      if (state == NGLayoutAlgorithm::kNotImplemented)
+        break;
+    }
+    return sizes;
+  }
+
   RefPtr<ComputedStyle> style_;
 };
 
@@ -779,5 +800,52 @@
   EXPECT_EQ(kDiv1Size + kDiv2Size, child3->TopOffset());
 }
 
+// Verifies that we compute the right min and max-content size.
+TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContent) {
+  const int kWidth = 50;
+  const int kWidthChild1 = 20;
+  const int kWidthChild2 = 30;
+
+  // This should have no impact on the min/max content size.
+  style_->setWidth(Length(kWidth, Fixed));
+
+  RefPtr<ComputedStyle> first_style = ComputedStyle::create();
+  first_style->setWidth(Length(kWidthChild1, Fixed));
+  NGBlockNode* first_child = new NGBlockNode(first_style.get());
+
+  RefPtr<ComputedStyle> second_style = ComputedStyle::create();
+  second_style->setWidth(Length(kWidthChild2, Fixed));
+  NGBlockNode* second_child = new NGBlockNode(second_style.get());
+
+  first_child->SetNextSibling(second_child);
+
+  MinAndMaxContentSizes sizes = RunComputeMinAndMax(first_child);
+  EXPECT_EQ(kWidthChild2, sizes.min_content);
+  EXPECT_EQ(kWidthChild2, sizes.max_content);
+}
+
+// Tests that we correctly handle shrink-to-fit
+TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFit) {
+  const int kWidthChild1 = 20;
+  const int kWidthChild2 = 30;
+
+  RefPtr<ComputedStyle> first_style = ComputedStyle::create();
+  first_style->setWidth(Length(kWidthChild1, Fixed));
+  NGBlockNode* first_child = new NGBlockNode(first_style.get());
+
+  RefPtr<ComputedStyle> second_style = ComputedStyle::create();
+  second_style->setWidth(Length(kWidthChild2, Fixed));
+  NGBlockNode* second_child = new NGBlockNode(second_style.get());
+
+  first_child->SetNextSibling(second_child);
+
+  auto* space = ConstructConstraintSpace(
+      kHorizontalTopBottom, TextDirection::Ltr,
+      NGLogicalSize(LayoutUnit(100), NGSizeIndefinite), true);
+  NGPhysicalFragmentBase* frag = RunBlockLayoutAlgorithm(space, first_child);
+
+  EXPECT_EQ(LayoutUnit(30), frag->Width());
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResource.cpp b/third_party/WebKit/Source/core/loader/resource/FontResource.cpp
index c895500..8a32534 100644
--- a/third_party/WebKit/Source/core/loader/resource/FontResource.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/FontResource.cpp
@@ -150,9 +150,11 @@
     float size,
     bool bold,
     bool italic,
-    FontOrientation orientation) {
+    FontOrientation orientation,
+    FontVariationSettings* fontVariationSettings) {
   DCHECK(m_fontData);
-  return m_fontData->fontPlatformData(size, bold, italic, orientation);
+  return m_fontData->fontPlatformData(size, bold, italic, orientation,
+                                      fontVariationSettings);
 }
 
 void FontResource::willReloadAfterDiskCacheMiss() {
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResource.h b/third_party/WebKit/Source/core/loader/resource/FontResource.h
index e1da2335..e7cfae3 100644
--- a/third_party/WebKit/Source/core/loader/resource/FontResource.h
+++ b/third_party/WebKit/Source/core/loader/resource/FontResource.h
@@ -42,6 +42,7 @@
 class FontPlatformData;
 class FontCustomPlatformData;
 class FontResourceClient;
+class FontVariationSettings;
 
 class CORE_EXPORT FontResource final : public Resource {
  public:
@@ -66,7 +67,8 @@
       float size,
       bool bold,
       bool italic,
-      FontOrientation = FontOrientation::Horizontal);
+      FontOrientation = FontOrientation::Horizontal,
+      FontVariationSettings* = nullptr);
 
   // Returns true if the loading priority of the remote font resource can be
   // lowered. The loading priority of the font can be lowered only if the
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp b/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp
index b30d380..988beed 100644
--- a/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp
@@ -23,7 +23,11 @@
 
 namespace blink {
 
-class FontResourceTest : public ::testing::Test {};
+class FontResourceTest : public ::testing::Test {
+  void TearDown() override {
+    Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs();
+  }
+};
 
 // Tests if ResourceFetcher works fine with FontResource that requires defered
 // loading supports.
@@ -138,7 +142,6 @@
   EXPECT_TRUE(client3->fontLoadLongLimitExceededCalled());
 
   Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
-  Platform::current()->getURLLoaderMockFactory()->unregisterURL(url);
   memoryCache()->remove(resource);
 }
 
diff --git a/third_party/WebKit/Source/core/page/FrameTree.cpp b/third_party/WebKit/Source/core/page/FrameTree.cpp
index 6ec8771..85bcb34 100644
--- a/third_party/WebKit/Source/core/page/FrameTree.cpp
+++ b/third_party/WebKit/Source/core/page/FrameTree.cpp
@@ -430,7 +430,7 @@
   // Search the entire tree of each of the other pages in this namespace.
   // FIXME: Is random order OK?
   for (const Page* otherPage : Page::ordinaryPages()) {
-    if (otherPage == page)
+    if (otherPage == page || otherPage->isClosing())
       continue;
     for (Frame* frame = otherPage->mainFrame(); frame;
          frame = frame->tree().traverseNext()) {
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 6a9bc39..13190a75 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -63,13 +63,13 @@
 // Set of all live pages; includes internal Page objects that are
 // not observable from scripts.
 static Page::PageSet& allPages() {
-  DEFINE_STATIC_LOCAL(Page::PageSet, allPages, ());
-  return allPages;
+  DEFINE_STATIC_LOCAL(Page::PageSet, pages, ());
+  return pages;
 }
 
 Page::PageSet& Page::ordinaryPages() {
-  DEFINE_STATIC_LOCAL(Page::PageSet, ordinaryPages, ());
-  return ordinaryPages;
+  DEFINE_STATIC_LOCAL(Page::PageSet, pages, ());
+  return pages;
 }
 
 void Page::networkStateChanged(bool online) {
@@ -150,6 +150,17 @@
   ASSERT(!m_mainFrame);
 }
 
+void Page::closeSoon() {
+  // Make sure this Page can no longer be found by JS.
+  m_isClosing = true;
+
+  // TODO(dcheng): Try to remove this in a followup, it's not obviously needed.
+  if (m_mainFrame->isLocalFrame())
+    toLocalFrame(m_mainFrame)->loader().stopAllLoaders();
+
+  chromeClient().closeWindowSoon();
+}
+
 ViewportDescription Page::viewportDescription() const {
   return mainFrame() && mainFrame()->isLocalFrame() &&
                  deprecatedLocalMainFrame()->document()
@@ -504,10 +515,6 @@
     m_scrollingCoordinator->willCloseLayerTreeView(layerTreeView);
 }
 
-void Page::willBeClosed() {
-  ordinaryPages().remove(this);
-}
-
 void Page::willBeDestroyed() {
   Frame* mainFrame = m_mainFrame;
 
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index fe469260..918050f7 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -60,6 +60,7 @@
 class FrameHost;
 class PluginData;
 class PointerLockController;
+class ScopedPageSuspender;
 class ScrollingCoordinator;
 class Settings;
 class SpellCheckerClient;
@@ -104,7 +105,8 @@
 
   ~Page() override;
 
-  void willBeClosed();
+  void closeSoon();
+  bool isClosing() const { return m_isClosing; }
 
   using PageSet = PersistentHeapHashSet<WeakMember<Page>>;
 
@@ -186,10 +188,13 @@
     return m_tabKeyCyclesThroughElements;
   }
 
-  // DefersLoading is used to delay loads during modal dialogs.
-  // Modal dialogs are supposed to freeze all background processes
-  // in the page, including prevent additional loads from staring/continuing.
-  void setSuspended(bool);
+  // Suspension is used to implement the "Optionally, pause while waiting for
+  // the user to acknowledge the message" step of simple dialog processing:
+  // https://html.spec.whatwg.org/multipage/webappapis.html#simple-dialogs
+  //
+  // Per https://html.spec.whatwg.org/multipage/webappapis.html#pause, no loads
+  // are allowed to start/continue in this state, and all background processing
+  // is also suspended.
   bool suspended() const { return m_suspended; }
 
   void setPageScaleFactor(float);
@@ -227,6 +232,8 @@
   void willBeDestroyed();
 
  private:
+  friend class ScopedPageSuspender;
+
   explicit Page(PageClients&);
 
   void initGroup();
@@ -234,6 +241,9 @@
   // SettingsDelegate overrides.
   void settingsChanged(SettingsDelegate::ChangeType) override;
 
+  // ScopedPageSuspender helpers.
+  void setSuspended(bool);
+
   Member<PageAnimator> m_animator;
   const Member<AutoscrollController> m_autoscrollController;
   Member<ChromeClient> m_chromeClient;
@@ -269,6 +279,12 @@
   HostsUsingFeatures m_hostsUsingFeatures;
 
   bool m_openedByDOM;
+  // Set to true when window.close() has been called and the Page will be
+  // destroyed. The browsing contexts in this page should no longer be
+  // discoverable via JS.
+  // TODO(dcheng): Try to remove |DOMWindow::m_windowIsClosing| in favor of
+  // this. However, this depends on resolving https://crbug.com/674641
+  bool m_isClosing;
 
   bool m_tabKeyCyclesThroughElements;
   bool m_suspended;
diff --git a/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp b/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp
index f03dd243..f169e32 100644
--- a/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp
+++ b/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp
@@ -35,15 +35,6 @@
 
 unsigned s_suspensionCount = 0;
 
-void setSuspended(bool isSuspended) {
-  // Make a copy of the collection. Undeferring loads can cause script to run,
-  // which would mutate ordinaryPages() in the middle of iteration.
-  HeapVector<Member<Page>> pages;
-  copyToVector(Page::ordinaryPages(), pages);
-  for (const auto& page : pages)
-    page->setSuspended(isSuspended);
-}
-
 }  // namespace
 
 ScopedPageSuspender::ScopedPageSuspender() {
@@ -62,6 +53,16 @@
   Platform::current()->currentThread()->scheduler()->resumeTimerQueue();
 }
 
+void ScopedPageSuspender::setSuspended(bool isSuspended) {
+  // Make a copy of the collection. Undeferring loads can cause script to run,
+  // which would mutate ordinaryPages() in the middle of iteration.
+  HeapVector<Member<Page>> pages;
+  copyToVector(Page::ordinaryPages(), pages);
+
+  for (const auto& page : pages)
+    page->setSuspended(isSuspended);
+}
+
 bool ScopedPageSuspender::isActive() {
   return s_suspensionCount > 0;
 }
diff --git a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
index 5b30bff..2889f8a 100644
--- a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
+++ b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
@@ -26,6 +26,8 @@
 
 namespace blink {
 
+class Page;
+
 class CORE_EXPORT ScopedPageSuspender final {
   WTF_MAKE_NONCOPYABLE(ScopedPageSuspender);
   USING_FAST_MALLOC(ScopedPageSuspender);
@@ -34,6 +36,10 @@
   explicit ScopedPageSuspender();
   ~ScopedPageSuspender();
 
+ private:
+  friend class Page;
+
+  static void setSuspended(bool);
   static bool isActive();
 };
 
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 32c4b10..47ef71c7 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -313,10 +313,7 @@
       context.forcedSubtreeInvalidationFlags &= PaintInvalidatorContext::
           ForcedSubtreeFullInvalidationForStackedContents;
     } else {
-      // For SPv1, subtree flags don't cross paint invalidation container
-      // boundary except for ForcedWholeTreeFullInvalidation.
-      context.forcedSubtreeInvalidationFlags &=
-          PaintInvalidatorContext::ForcedWholeTreeFullInvalidation;
+      context.forcedSubtreeInvalidationFlags = 0;
     }
   }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.h b/third_party/WebKit/Source/core/paint/PaintInvalidator.h
index 873b09b..e93ec3c 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.h
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.h
@@ -49,11 +49,6 @@
     // TODO(crbug.com/637313): This is temporary before we support filters in
     // paint property tree.
     ForcedSubtreeSlowPathRect = 1 << 5,
-    // For SPv1, all of the above flags don't cross paint invalidation container
-    // boundary. ForceWholeTreeFullInvalidation does.
-    // TODO(wangxianzhu): Combine this with ForcedSubtreeFullInvalidation when
-    // removing non-SPv2 code.
-    ForcedWholeTreeFullInvalidation = 1 << 6,
   };
   unsigned forcedSubtreeInvalidationFlags = 0;
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 8abb550..d566bce6 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -268,15 +268,6 @@
   }
 }
 
-static FloatPoint3D transformOrigin(const LayoutBox& box) {
-  const ComputedStyle& style = box.styleRef();
-  FloatSize borderBoxSize(box.size());
-  return FloatPoint3D(
-      floatValueForLength(style.transformOriginX(), borderBoxSize.width()),
-      floatValueForLength(style.transformOriginY(), borderBoxSize.height()),
-      style.transformOriginZ());
-}
-
 // SVG does not use the general transform update of |updateTransform|, instead
 // creating a transform node for SVG-specific transforms without 3D.
 void PaintPropertyTreeBuilder::updateTransformForNonRootSVG(
@@ -330,6 +321,18 @@
   return compositingReasons;
 }
 
+static FloatPoint3D transformOrigin(const LayoutBox& box) {
+  const ComputedStyle& style = box.styleRef();
+  // Transform origin has no effect without a transform or motion path.
+  if (!style.hasTransform())
+    return FloatPoint3D();
+  FloatSize borderBoxSize(box.size());
+  return FloatPoint3D(
+      floatValueForLength(style.transformOriginX(), borderBoxSize.width()),
+      floatValueForLength(style.transformOriginY(), borderBoxSize.height()),
+      style.transformOriginZ());
+}
+
 void PaintPropertyTreeBuilder::updateTransform(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
@@ -350,10 +353,10 @@
     // descendants.
     if (object.isBox() && (style.hasTransform() || style.preserves3D() ||
                            compositingReasons != CompositingReasonNone)) {
+      auto& box = toLayoutBox(object);
       TransformationMatrix matrix;
       style.applyTransform(
-          matrix, toLayoutBox(object).size(),
-          ComputedStyle::ExcludeTransformOrigin,
+          matrix, box.size(), ComputedStyle::ExcludeTransformOrigin,
           ComputedStyle::IncludeMotionPath,
           ComputedStyle::IncludeIndependentTransformProperties);
 
@@ -367,8 +370,7 @@
 
       auto& properties = object.getMutableForPainting().ensurePaintProperties();
       context.forceSubtreeUpdate |= properties.updateTransform(
-          context.current.transform, matrix,
-          transformOrigin(toLayoutBox(object)),
+          context.current.transform, matrix, transformOrigin(box),
           context.current.shouldFlattenInheritedTransform, renderingContextID,
           compositingReasons);
     } else {
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index e4d02a0..969f3fa 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -465,8 +465,10 @@
       transform->layoutObject()->paintProperties();
 
   EXPECT_EQ(TransformationMatrix(), transformProperties->transform()->matrix());
-  EXPECT_EQ(FloatPoint3D(200, 150, 0),
-            transformProperties->transform()->origin());
+  EXPECT_EQ(TransformationMatrix().translate(0, 0),
+            transformProperties->transform()->matrix());
+  // The value is zero without a transform property that needs transform-offset.
+  EXPECT_EQ(FloatPoint3D(0, 0, 0), transformProperties->transform()->origin());
   EXPECT_EQ(nullptr, transformProperties->paintOffsetTranslation());
   EXPECT_TRUE(transformProperties->transform()->hasDirectCompositingReasons());
 
@@ -3085,4 +3087,73 @@
   EXPECT_EQ(filterProperties->effect(), childPaintState.effect());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, TransformOriginWithAndWithoutTransform) {
+  setBodyInnerHTML(
+      "<style>"
+      "  body { margin: 0 }"
+      "  div {"
+      "    width: 400px;"
+      "    height: 100px;"
+      "  }"
+      "  #transform {"
+      "    transform: translate(100px, 200px);"
+      "    transform-origin: 75% 75% 0;"
+      "  }"
+      "  #willChange {"
+      "    will-change: opacity;"
+      "    transform-origin: 75% 75% 0;"
+      "  }"
+      "</style>"
+      "<div id='transform'></div>"
+      "<div id='willChange'></div>");
+
+  auto* transform = document().getElementById("transform")->layoutObject();
+  EXPECT_EQ(TransformationMatrix().translate3d(100, 200, 0),
+            transform->paintProperties()->transform()->matrix());
+  EXPECT_EQ(FloatPoint3D(300, 75, 0),
+            transform->paintProperties()->transform()->origin());
+
+  auto* willChange = document().getElementById("willChange")->layoutObject();
+  EXPECT_EQ(TransformationMatrix().translate3d(0, 0, 0),
+            willChange->paintProperties()->transform()->matrix());
+  EXPECT_EQ(FloatPoint3D(0, 0, 0),
+            willChange->paintProperties()->transform()->origin());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, TransformOriginWithAndWithoutMotionPath) {
+  setBodyInnerHTML(
+      "<style>"
+      "  body { margin: 0 }"
+      "  div {"
+      "    width: 100px;"
+      "    height: 100px;"
+      "  }"
+      "  #motionPath {"
+      "    position: absolute;"
+      "    motion-path: path('M0 0 L 200 400');"
+      "    motion-offset: 50%;"
+      "    motion-rotation: 0deg;"
+      "    transform-origin: 50% 50% 0;"
+      "  }"
+      "  #willChange {"
+      "    will-change: opacity;"
+      "    transform-origin: 50% 50% 0;"
+      "  }"
+      "</style>"
+      "<div id='motionPath'></div>"
+      "<div id='willChange'></div>");
+
+  auto* motionPath = document().getElementById("motionPath")->layoutObject();
+  EXPECT_EQ(TransformationMatrix().translate3d(50, 150, 0),
+            motionPath->paintProperties()->transform()->matrix());
+  EXPECT_EQ(FloatPoint3D(50, 50, 0),
+            motionPath->paintProperties()->transform()->origin());
+
+  auto* willChange = document().getElementById("willChange")->layoutObject();
+  EXPECT_EQ(TransformationMatrix().translate3d(0, 0, 0),
+            willChange->paintProperties()->transform()->matrix());
+  EXPECT_EQ(FloatPoint3D(0, 0, 0),
+            willChange->paintProperties()->transform()->origin());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
index f22a67d..7d80302 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -53,14 +53,6 @@
   }
 
   PrePaintTreeWalkContext context(parentContext);
-
-  if (frameView.shouldInvalidateAllPaintAndPaintProperties()) {
-    context.treeBuilderContext.forceSubtreeUpdate = true;
-    context.paintInvalidatorContext.forcedSubtreeInvalidationFlags |=
-        PaintInvalidatorContext::ForcedWholeTreeFullInvalidation;
-    frameView.clearShouldInvalidateAllPaintAndPaintProperties();
-  }
-
   m_propertyTreeBuilder.updateProperties(frameView, context.treeBuilderContext);
   m_paintInvalidator.invalidatePaintIfNeeded(frameView,
                                              context.paintInvalidatorContext);
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index ee09e1a..4494a854 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -2089,7 +2089,8 @@
           visitedLink ? visitedLinkCaretColor() : caretColor();
       // TODO(rego): We may want to adjust the caret color if it's the same than
       // the background to ensure good visibility and contrast.
-      result = autoColor.toStyleColor();
+      result = autoColor.isAutoColor() ? StyleColor::currentColor()
+                                       : autoColor.toStyleColor();
       break;
     }
     case CSSPropertyColor:
diff --git a/third_party/WebKit/Source/core/svg/SVGTransform.idl b/third_party/WebKit/Source/core/svg/SVGTransform.idl
index 874f04b..9353ece 100644
--- a/third_party/WebKit/Source/core/svg/SVGTransform.idl
+++ b/third_party/WebKit/Source/core/svg/SVGTransform.idl
@@ -35,10 +35,11 @@
     const unsigned short SVG_TRANSFORM_SKEWY = 6;
 
     [ImplementedAs=transformType] readonly attribute unsigned short type;
-    // TODO(foolip): SVGMatrix should be DOMMatrixReadOnly.
+    // TODO(foolip): SVGMatrix should be DOMMatrix.
     readonly attribute SVGMatrix matrix;
     readonly attribute float angle;
 
+    // TODO(foolip): SVGMatrix should be DOMMatrixReadOnly.
     [RaisesException] void setMatrix(SVGMatrix matrix);
     [RaisesException] void setTranslate(float tx, float ty);
     [RaisesException] void setScale(float sx, float sy);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
index 9f70886..66c7252 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -298,11 +298,13 @@
     this._dataProvider = new Timeline.TimelineFlameChartDataProvider(this._model, frameModel, irModel, filters);
     var mainViewGroupExpansionSetting = Common.settings.createSetting('timelineFlamechartMainViewGroupExpansion', {});
     this._mainView = new UI.FlameChart(this._dataProvider, this, mainViewGroupExpansionSetting);
+    this._mainView.alwaysShowVerticalScroll();
 
     var networkViewGroupExpansionSetting =
         Common.settings.createSetting('timelineFlamechartNetworkViewGroupExpansion', {});
     this._networkDataProvider = new Timeline.TimelineFlameChartNetworkDataProvider(this._model);
     this._networkView = new UI.FlameChart(this._networkDataProvider, this, networkViewGroupExpansionSetting);
+    this._networkView.alwaysShowVerticalScroll();
     networkViewGroupExpansionSetting.addChangeListener(this.resizeToPreferredHeights.bind(this));
 
     this._splitWidget.setMainWidget(this._mainView);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 790bef84..3ae124b 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -556,8 +556,7 @@
     for (var i = 0; i < this._overviewControls.length; ++i)
       this._overviewControls[i].timelineStarted();
 
-    if (userInitiated)
-      Host.userMetrics.actionTaken(Host.UserMetrics.Action.TimelineStarted);
+    Host.userMetrics.actionTaken(Host.UserMetrics.Action.TimelineStarted);
     this._setUIControlsEnabled(false);
     this._hideLandingPage();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js
index 10413d1..32ee51c 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js
@@ -19,6 +19,7 @@
         this.viewportElement, this._startRangeSelection.bind(this), this._rangeSelectionDragging.bind(this),
         this._endRangeSelection.bind(this), 'text', null);
 
+    this._alwaysShowVerticalScroll = false;
     this._vScrollElement = this.contentElement.createChild('div', 'flame-chart-v-scroll');
     this._vScrollContent = this._vScrollElement.createChild('div');
     this._vScrollElement.addEventListener('scroll', this._onScroll.bind(this), false);
@@ -29,6 +30,11 @@
     this.reset();
   }
 
+  alwaysShowVerticalScroll() {
+    this._alwaysShowVerticalScroll = true;
+    this._vScrollElement.classList.add('always-show-scrollbar');
+  }
+
   /**
    * @return {boolean}
    */
@@ -48,7 +54,7 @@
    * @private
    */
   _updateScrollBar() {
-    var showScroll = this._totalHeight > this._offsetHeight;
+    const showScroll = this._alwaysShowVerticalScroll || this._totalHeight > this._offsetHeight;
     if (this._vScrollElement.classList.contains('hidden') !== showScroll)
       return;
     this._vScrollElement.classList.toggle('hidden', !showScroll);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css b/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css
index 0cd1ac1..4cebc9b 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css
@@ -38,6 +38,10 @@
     padding-left: 1px;
 }
 
+.flame-chart-v-scroll.always-show-scrollbar {
+    overflow-y: scroll;
+}
+
 :host-context(.platform-mac) .flame-chart-v-scroll {
     right: 2px;
     top: 3px;
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.idl b/third_party/WebKit/Source/modules/fetch/Headers.idl
index 1e9c064..6b2b91af 100644
--- a/third_party/WebKit/Source/modules/fetch/Headers.idl
+++ b/third_party/WebKit/Source/modules/fetch/Headers.idl
@@ -16,7 +16,7 @@
     [RaisesException] void append(ByteString name, ByteString value);
     [ImplementedAs=remove, RaisesException] void delete(ByteString key);
     [RaisesException] ByteString? get(ByteString key);
-    [RaisesException] sequence<ByteString> getAll(ByteString name);
+    [RaisesException, Measure] sequence<ByteString> getAll(ByteString name);
     [RaisesException] boolean has(ByteString key);
     [RaisesException] void set(ByteString key, ByteString value);
     iterable<ByteString, ByteString>;
diff --git a/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp b/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp
index e20faa99..ad60c05 100644
--- a/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp
@@ -86,6 +86,8 @@
 }
 
 void FileWriter::write(Blob* data, ExceptionState& exceptionState) {
+  if (!getExecutionContext())
+    return;
   ASSERT(data);
   ASSERT(writer());
   ASSERT(m_truncateLength == -1);
@@ -115,6 +117,8 @@
 }
 
 void FileWriter::seek(long long position, ExceptionState& exceptionState) {
+  if (!getExecutionContext())
+    return;
   ASSERT(writer());
   if (m_readyState == kWriting) {
     setError(FileError::kInvalidStateErr, exceptionState);
@@ -127,6 +131,8 @@
 }
 
 void FileWriter::truncate(long long position, ExceptionState& exceptionState) {
+  if (!getExecutionContext())
+    return;
   ASSERT(writer());
   ASSERT(m_truncateLength == -1);
   if (m_readyState == kWriting || position < 0) {
@@ -154,6 +160,8 @@
 }
 
 void FileWriter::abort(ExceptionState& exceptionState) {
+  if (!getExecutionContext())
+    return;
   ASSERT(writer());
   if (m_readyState != kWriting)
     return;
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp
index fadbc47..9c31574 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp
@@ -117,8 +117,17 @@
     else if (initializer.source().isMessagePort())
       m_sourceAsMessagePort = initializer.source().getAsMessagePort();
   }
-  if (initializer.hasPorts())
-    m_ports = new MessagePortArray(initializer.ports());
+  if (initializer.hasPorts()) {
+    // TODO(sof): remove the extra same-heap checks once crbug.com/655926
+    // has been resolved.
+    const MessagePortArray& ports = initializer.ports();
+    m_ports = new MessagePortArray;
+    m_ports->reserveInitialCapacity(ports.size());
+    for (const auto& port : ports) {
+      CHECK(ThreadState::current()->isOnThreadHeap(port.get()));
+      m_ports->append(port);
+    }
+  }
 }
 
 ExtendableMessageEvent::ExtendableMessageEvent(
@@ -135,6 +144,14 @@
       m_ports(ports) {
   if (m_serializedData)
     m_serializedData->registerMemoryAllocatedWithCurrentScriptContext();
+
+  // TODO(sof): remove the same-heap verification once crbug.com/655926 has
+  // been resolved.
+  if (m_ports) {
+    for (const auto& port : *m_ports) {
+      CHECK(ThreadState::current()->isOnThreadHeap(port.get()));
+    }
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h
index a3a05a1..1086077 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h
@@ -71,7 +71,8 @@
   Member<ServiceWorkerClient> m_sourceAsClient;
   Member<ServiceWorker> m_sourceAsServiceWorker;
   Member<MessagePort> m_sourceAsMessagePort;
-  Member<MessagePortArray> m_ports;
+  // TODO(sof): remove once crbug.com/655926 has been addressed.
+  SameThreadCheckedMember<MessagePortArray> m_ports;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h
index cf6f254..b2a15213 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h
@@ -74,7 +74,8 @@
 
  private:
   Member<RespondWithObserver> m_observer;
-  Member<Request> m_request;
+  // TODO(sof): remove once crbug.com/655926 has been diagnosed.
+  SameThreadCheckedMember<Request> m_request;
   Member<PreloadResponseProperty> m_preloadResponseProperty;
   String m_clientId;
   bool m_isReload;
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
index 89d723d..c1b5b5f 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
@@ -37,7 +37,7 @@
 }
 
 SpeechSynthesis::SpeechSynthesis(ExecutionContext* context)
-    : m_executionContext(context),
+    : ContextClient(context),
       m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this)),
       m_isPaused(false) {}
 
@@ -48,7 +48,7 @@
 
 void SpeechSynthesis::voicesDidChange() {
   m_voiceList.clear();
-  if (!m_executionContext->isContextDestroyed())
+  if (getExecutionContext())
     dispatchEvent(Event::create(EventTypeNames::voiceschanged));
 }
 
@@ -126,12 +126,13 @@
                                 SpeechSynthesisUtterance* utterance,
                                 unsigned long charIndex,
                                 const String& name) {
-  if (!m_executionContext->isContextDestroyed()) {
-    double elapsedTimeMillis =
-        (monotonicallyIncreasingTime() - utterance->startTime()) * 1000.0;
-    utterance->dispatchEvent(SpeechSynthesisEvent::create(
-        type, utterance, charIndex, elapsedTimeMillis, name));
-  }
+  if (!getExecutionContext())
+    return;
+
+  double elapsedTimeMillis =
+      (monotonicallyIncreasingTime() - utterance->startTime()) * 1000.0;
+  utterance->dispatchEvent(SpeechSynthesisEvent::create(
+      type, utterance, charIndex, elapsedTimeMillis, name));
 }
 
 void SpeechSynthesis::handleSpeakingCompleted(
@@ -234,11 +235,11 @@
 }
 
 DEFINE_TRACE(SpeechSynthesis) {
-  visitor->trace(m_executionContext);
   visitor->trace(m_platformSpeechSynthesizer);
   visitor->trace(m_voiceList);
   visitor->trace(m_utteranceQueue);
   PlatformSpeechSynthesizerClient::trace(visitor);
+  ContextClient::trace(visitor);
   EventTargetWithInlineData::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
index 6363225..ce8e09f 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
@@ -26,6 +26,7 @@
 #ifndef SpeechSynthesis_h
 #define SpeechSynthesis_h
 
+#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/EventTargetModules.h"
 #include "modules/ModulesExport.h"
 #include "modules/speech/SpeechSynthesisUtterance.h"
@@ -40,6 +41,7 @@
 
 class MODULES_EXPORT SpeechSynthesis final
     : public EventTargetWithInlineData,
+      public ContextClient,
       public PlatformSpeechSynthesizerClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesis);
@@ -64,7 +66,7 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(voiceschanged);
 
   ExecutionContext* getExecutionContext() const override {
-    return m_executionContext;
+    return ContextClient::getExecutionContext();
   }
 
   DECLARE_VIRTUAL_TRACE();
@@ -93,7 +95,6 @@
   // Returns the utterance at the front of the queue.
   SpeechSynthesisUtterance* currentSpeechUtterance() const;
 
-  Member<ExecutionContext> m_executionContext;
   Member<PlatformSpeechSynthesizer> m_platformSpeechSynthesizer;
   HeapVector<Member<SpeechSynthesisVoice>> m_voiceList;
   HeapDeque<Member<SpeechSynthesisUtterance>> m_utteranceQueue;
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
index 68a8339..f7c3d31 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
@@ -37,17 +37,13 @@
 
 SpeechSynthesisUtterance::SpeechSynthesisUtterance(ExecutionContext* context,
                                                    const String& text)
-    : m_executionContext(context),
+    : ContextClient(context),
       m_platformUtterance(PlatformSpeechSynthesisUtterance::create(this)) {
   m_platformUtterance->setText(text);
 }
 
 SpeechSynthesisUtterance::~SpeechSynthesisUtterance() {}
 
-ExecutionContext* SpeechSynthesisUtterance::getExecutionContext() const {
-  return m_executionContext;
-}
-
 const AtomicString& SpeechSynthesisUtterance::interfaceName() const {
   return EventTargetNames::SpeechSynthesisUtterance;
 }
@@ -67,9 +63,9 @@
 }
 
 DEFINE_TRACE(SpeechSynthesisUtterance) {
-  visitor->trace(m_executionContext);
   visitor->trace(m_platformUtterance);
   visitor->trace(m_voice);
+  ContextClient::trace(visitor);
   EventTargetWithInlineData::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
index bad1e60..6cb1f62 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
@@ -26,6 +26,7 @@
 #ifndef SpeechSynthesisUtterance_h
 #define SpeechSynthesisUtterance_h
 
+#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/EventTargetModules.h"
 #include "modules/speech/SpeechSynthesisVoice.h"
 #include "platform/heap/Handle.h"
@@ -35,6 +36,7 @@
 
 class SpeechSynthesisUtterance final
     : public EventTargetWithInlineData,
+      public ContextClient,
       public PlatformSpeechSynthesisUtteranceClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesisUtterance);
@@ -75,7 +77,9 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(mark);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(boundary);
 
-  ExecutionContext* getExecutionContext() const override;
+  ExecutionContext* getExecutionContext() const override {
+    return ContextClient::getExecutionContext();
+  }
 
   PlatformSpeechSynthesisUtterance* platformUtterance() const {
     return m_platformUtterance;
@@ -89,7 +93,6 @@
   // EventTarget
   const AtomicString& interfaceName() const override;
 
-  Member<ExecutionContext> m_executionContext;
   Member<PlatformSpeechSynthesisUtterance> m_platformUtterance;
   Member<SpeechSynthesisVoice> m_voice;
 };
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
index b70bc67..efda79be 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
@@ -13,6 +13,9 @@
   deps = [
     "//device/time_zone_monitor/public/interfaces:interfaces_blink",
     "//mojo/public/cpp/bindings",
+    "//services/device/public/interfaces:interfaces_blink",
+    "//services/service_manager/public/interfaces:interfaces_blink",
+    "//third_party/WebKit/Source/platform",
     "//third_party/icu:icui18n",
   ]
 }
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
index 91badb2..212c63f5 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
     "+device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom-blink.h",
     "+mojo/public/cpp/bindings/binding.h",
+    "+services/device/public/interfaces/constants.mojom-blink.h",
     "+third_party/icu/source/i18n/unicode/timezone.h",
 ]
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp
index 18e2ff1..a7c0ab4 100644
--- a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp
+++ b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp
@@ -10,8 +10,9 @@
 #include "core/dom/ExecutionContextTask.h"
 #include "core/workers/WorkerBackingThread.h"
 #include "core/workers/WorkerThread.h"
-#include "public/platform/InterfaceProvider.h"
+#include "platform/ServiceConnector.h"
 #include "public/platform/Platform.h"
+#include "services/device/public/interfaces/constants.mojom-blink.h"
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 #include <v8.h>
 
@@ -36,8 +37,8 @@
   DEFINE_STATIC_LOCAL(TimeZoneMonitorClient, instance, ());
 
   device::mojom::blink::TimeZoneMonitorPtr monitor;
-  Platform::current()->interfaceProvider()->getInterface(
-      mojo::MakeRequest(&monitor));
+  ServiceConnector::instance().connectToInterface(
+      device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor));
   monitor->AddClient(instance.m_binding.CreateInterfacePtrAndBind());
 }
 
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp
index 189aaa8..182465b 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp
@@ -730,7 +730,7 @@
         for (; writeIndex < fillToFrame; ++writeIndex)
           values[writeIndex] = value;
       } else {
-        float numSampleFrames = deltaTime * sampleRate;
+        double numSampleFrames = deltaTime * sampleRate;
         // The value goes exponentially from value1 to value2 in a duration of
         // deltaTime seconds according to
         //
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index d45fa60..a974f2ab 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -333,6 +333,7 @@
     "SecureTextInput.cpp",
     "SecureTextInput.h",
     "SerializedResource.h",
+    "ServiceConnector.h",
     "SharedBuffer.cpp",
     "SharedBuffer.h",
     "SharedBufferChunkReader.cpp",
@@ -672,6 +673,7 @@
     "fonts/mac/FontFamilyMatcherMac.h",
     "fonts/mac/FontFamilyMatcherMac.mm",
     "fonts/mac/FontPlatformDataMac.mm",
+    "fonts/opentype/FontSettings.cpp",
     "fonts/opentype/FontSettings.h",
     "fonts/opentype/OpenTypeCapsSupport.cpp",
     "fonts/opentype/OpenTypeCapsSupport.h",
@@ -1442,6 +1444,7 @@
     "//gpu/command_buffer/common:common",
     "//media",
     "//net",
+    "//services/service_manager/public/interfaces:interfaces_blink",
     "//skia",
     "//third_party:jpeg",
     "//third_party/WebKit/Source/wtf",
@@ -1706,6 +1709,7 @@
     "fonts/UnicodeRangeSetTest.cpp",
     "fonts/android/FontCacheAndroidTest.cpp",
     "fonts/mac/FontFamilyMatcherMacTest.mm",
+    "fonts/opentype/FontSettingsTest.cpp",
     "fonts/opentype/OpenTypeVerticalDataTest.cpp",
     "fonts/shaping/CachingWordShaperTest.cpp",
     "fonts/shaping/HarfBuzzShaperTest.cpp",
@@ -1793,6 +1797,7 @@
     "scheduler/renderer/web_view_scheduler_impl_unittest.cc",
     "scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc",
     "scroll/ScrollableAreaTest.cpp",
+    "scroll/ScrollbarThemeOverlayTest.cpp",
     "testing/ArenaTestHelpers.h",
     "testing/TreeTestHelpers.cpp",
     "testing/TreeTestHelpers.h",
diff --git a/third_party/WebKit/Source/platform/DEPS b/third_party/WebKit/Source/platform/DEPS
index c15cda8..0595f59 100644
--- a/third_party/WebKit/Source/platform/DEPS
+++ b/third_party/WebKit/Source/platform/DEPS
@@ -33,6 +33,7 @@
     "+mozilla",
     "+platform",
     "+public/platform",
+    "+services/service_manager/public/interfaces",
     "+skia/ext",
     "+third_party/ced/src/compact_enc_det/compact_enc_det.h",
     "+third_party/khronos",
diff --git a/third_party/WebKit/Source/platform/ServiceConnector.h b/third_party/WebKit/Source/platform/ServiceConnector.h
new file mode 100644
index 0000000..6d6b56b
--- /dev/null
+++ b/third_party/WebKit/Source/platform/ServiceConnector.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ServiceConnector_h
+#define ServiceConnector_h
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "public/platform/Platform.h"
+#include "services/service_manager/public/interfaces/connector.mojom-blink.h"
+#include "wtf/StdLibExtras.h"
+
+namespace blink {
+
+// ServiceConnector supports connecting to Mojo services by name.
+class ServiceConnector {
+ public:
+  static ServiceConnector& instance() {
+    DEFINE_STATIC_LOCAL(ServiceConnector, instance, ());
+    return instance;
+  }
+
+  template <typename Interface>
+  void connectToInterface(const char* serviceName,
+                          mojo::InterfaceRequest<Interface> request) {
+    if (!m_connector.is_bound()) {
+      Platform::current()->bindServiceConnector(
+          mojo::MakeRequest(&m_connector).PassMessagePipe());
+    }
+
+    if (m_connector.encountered_error())
+      return;
+
+    service_manager::mojom::blink::IdentityPtr remoteIdentity(
+        service_manager::mojom::blink::Identity::New());
+    remoteIdentity->name = serviceName;
+    remoteIdentity->user_id = service_manager::mojom::blink::kInheritUserID;
+    remoteIdentity->instance = "";
+
+    service_manager::mojom::blink::InterfaceProviderPtr remoteInterfaces;
+    m_connector->Connect(std::move(remoteIdentity),
+                         MakeRequest(&remoteInterfaces), nullptr,
+                         base::Bind(&ServiceConnector::onConnectionCompleted));
+
+    remoteInterfaces->GetInterface(Interface::Name_, request.PassMessagePipe());
+  }
+
+ private:
+  ServiceConnector() {}
+
+  static void onConnectionCompleted(
+      service_manager::mojom::ConnectResult result,
+      const WTF::String& targetUserID) {}
+
+  service_manager::mojom::blink::ConnectorPtr m_connector;
+};
+
+}  // namespace blink
+
+#endif  // ServiceConnector_h
diff --git a/third_party/WebKit/Source/platform/exported/Platform.cpp b/third_party/WebKit/Source/platform/exported/Platform.cpp
index 70fee4a..2f69919a 100644
--- a/third_party/WebKit/Source/platform/exported/Platform.cpp
+++ b/third_party/WebKit/Source/platform/exported/Platform.cpp
@@ -151,4 +151,7 @@
   return InterfaceProvider::getEmptyInterfaceProvider();
 }
 
+void Platform::bindServiceConnector(
+    mojo::ScopedMessagePipeHandle remoteHandle) {}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/FontCacheKey.h b/third_party/WebKit/Source/platform/fonts/FontCacheKey.h
index 89c1232..ae1d8d2 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCacheKey.h
+++ b/third_party/WebKit/Source/platform/fonts/FontCacheKey.h
@@ -32,6 +32,7 @@
 #define FontCacheKey_h
 
 #include "FontFaceCreationParams.h"
+#include "platform/fonts/opentype/FontSettings.h"
 #include "wtf/Allocator.h"
 #include "wtf/HashMap.h"
 #include "wtf/HashTableDeletedValueType.h"
@@ -51,21 +52,27 @@
   FontCacheKey() : m_creationParams(), m_fontSize(0), m_options(0) {}
   FontCacheKey(FontFaceCreationParams creationParams,
                float fontSize,
-               unsigned options)
+               unsigned options,
+               PassRefPtr<FontVariationSettings> variationSettings)
       : m_creationParams(creationParams),
         m_fontSize(fontSize * s_fontSizePrecisionMultiplier),
-        m_options(options) {}
+        m_options(options),
+        m_variationSettings(variationSettings) {}
+
   FontCacheKey(WTF::HashTableDeletedValueType)
       : m_fontSize(hashTableDeletedSize()) {}
 
   unsigned hash() const {
-    unsigned hashCodes[3] = {m_creationParams.hash(), m_fontSize, m_options};
+    unsigned hashCodes[4] = {
+        m_creationParams.hash(), m_fontSize, m_options,
+        m_variationSettings ? m_variationSettings->hash() : 0};
     return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
   }
 
   bool operator==(const FontCacheKey& other) const {
     return m_creationParams == other.m_creationParams &&
-           m_fontSize == other.m_fontSize && m_options == other.m_options;
+           m_fontSize == other.m_fontSize && m_options == other.m_options &&
+           m_variationSettings == other.m_variationSettings;
   }
 
   bool isHashTableDeletedValue() const {
@@ -84,6 +91,7 @@
   FontFaceCreationParams m_creationParams;
   unsigned m_fontSize;
   unsigned m_options;
+  RefPtr<FontVariationSettings> m_variationSettings;
 };
 
 struct FontCacheKeyHash {
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
index 67a56fe7..7b25a9a 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
@@ -37,6 +37,7 @@
 #include "platform/fonts/FontCache.h"
 #include "platform/fonts/FontPlatformData.h"
 #include "platform/fonts/WebFontDecoder.h"
+#include "platform/fonts/opentype/FontSettings.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "wtf/PtrUtil.h"
@@ -46,7 +47,7 @@
 
 FontCustomPlatformData::FontCustomPlatformData(sk_sp<SkTypeface> typeface,
                                                size_t dataSize)
-    : m_typeface(typeface), m_dataSize(dataSize) {}
+    : m_baseTypeface(typeface), m_dataSize(dataSize) {}
 
 FontCustomPlatformData::~FontCustomPlatformData() {}
 
@@ -54,10 +55,47 @@
     float size,
     bool bold,
     bool italic,
-    FontOrientation orientation) {
-  ASSERT(m_typeface);
-  return FontPlatformData(m_typeface, "", size, bold && !m_typeface->isBold(),
-                          italic && !m_typeface->isItalic(), orientation);
+    FontOrientation orientation,
+    const FontVariationSettings* variationSettings) {
+  DCHECK(m_baseTypeface);
+
+  sk_sp<SkTypeface> returnTypeface = m_baseTypeface;
+
+  // Maximum axis count is maximum value for the OpenType USHORT, which is a
+  // 16bit unsigned.  https://www.microsoft.com/typography/otspec/fvar.htm
+  // Variation settings coming from CSS can have duplicate assignments and the
+  // list can be longer than UINT16_MAX, but ignoring this for now, going with a
+  // reasonable upper limit and leaving the deduplication for TODO(drott),
+  // crbug.com/674878 second duplicate value should supersede first..
+  if (variationSettings && variationSettings->size() < UINT16_MAX) {
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    Vector<SkFontMgr::FontParameters::Axis, 0> axes;
+    axes.reserveCapacity(variationSettings->size());
+    for (size_t i = 0; i < variationSettings->size(); ++i) {
+      SkFontMgr::FontParameters::Axis axis = {
+          atomicStringToFourByteTag(variationSettings->at(i).tag()),
+          SkFloatToScalar(variationSettings->at(i).value())};
+      axes.append(axis);
+    }
+
+    sk_sp<SkTypeface> skVariationFont(fm->createFromStream(
+        m_baseTypeface->openStream(nullptr)->duplicate(),
+        SkFontMgr::FontParameters().setAxes(axes.data(), axes.size())));
+
+    if (skVariationFont) {
+      returnTypeface = skVariationFont;
+    } else {
+      SkString familyName;
+      m_baseTypeface->getFamilyName(&familyName);
+      // TODO: Surface this as a console message?
+      LOG(ERROR) << "Unable for apply variation axis properties for font: "
+                 << familyName.c_str();
+    }
+  }
+
+  return FontPlatformData(returnTypeface, "", size,
+                          bold && !m_baseTypeface->isBold(),
+                          italic && !m_baseTypeface->isItalic(), orientation);
 }
 
 std::unique_ptr<FontCustomPlatformData> FontCustomPlatformData::create(
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
index 055ded0..80eb0cf 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
+++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
@@ -47,6 +47,7 @@
 
 class FontPlatformData;
 class SharedBuffer;
+class FontVariationSettings;
 
 class PLATFORM_EXPORT FontCustomPlatformData {
   USING_FAST_MALLOC(FontCustomPlatformData);
@@ -62,14 +63,15 @@
       float size,
       bool bold,
       bool italic,
-      FontOrientation = FontOrientation::Horizontal);
+      FontOrientation = FontOrientation::Horizontal,
+      const FontVariationSettings* = nullptr);
 
   size_t dataSize() const { return m_dataSize; }
   static bool supportsFormat(const String&);
 
  private:
   FontCustomPlatformData(sk_sp<SkTypeface>, size_t dataSize);
-  sk_sp<SkTypeface> m_typeface;
+  sk_sp<SkTypeface> m_baseTypeface;
   size_t m_dataSize;
 };
 
diff --git a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
index f0012be..b8c4da4 100644
--- a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
@@ -224,7 +224,8 @@
       static_cast<unsigned>(m_fields.m_subpixelTextPosition);   // bit 1
 
   return FontCacheKey(creationParams, effectiveFontSize(),
-                      options | fontTraits.bitfield() << 8);
+                      options | fontTraits.bitfield() << 8,
+                      m_variationSettings);
 }
 
 void FontDescription::setDefaultTypesettingFeatures(
@@ -311,16 +312,9 @@
       addToHash(hash, settings->at(i).value());
     }
   }
-  const FontVariationSettings* varSettings = variationSettings();
-  if (varSettings) {
-    unsigned numFeatures = varSettings->size();
-    for (unsigned i = 0; i < numFeatures; ++i) {
-      const AtomicString& tag = varSettings->at(i).tag();
-      for (unsigned j = 0; j < tag.length(); j++)
-        stringHasher.addCharacter(tag[j]);
-      addToHash(hash, varSettings->at(i).value());
-    }
-  }
+
+  if (variationSettings())
+    addToHash(hash, variationSettings()->hash());
 
   if (m_locale) {
     const AtomicString& locale = m_locale->localeString();
diff --git a/third_party/WebKit/Source/platform/fonts/FontPlatformData.h b/third_party/WebKit/Source/platform/fonts/FontPlatformData.h
index 862af84..e2725206 100644
--- a/third_party/WebKit/Source/platform/fonts/FontPlatformData.h
+++ b/third_party/WebKit/Source/platform/fonts/FontPlatformData.h
@@ -74,6 +74,7 @@
 
 class Font;
 class HarfBuzzFace;
+class FontVariationSettings;
 
 class PLATFORM_EXPORT FontPlatformData {
   USING_FAST_MALLOC(FontPlatformData);
@@ -95,9 +96,10 @@
 #if OS(MACOSX)
   FontPlatformData(NSFont*,
                    float size,
-                   bool syntheticBold = false,
-                   bool syntheticItalic = false,
-                   FontOrientation = FontOrientation::Horizontal);
+                   bool syntheticBold,
+                   bool syntheticItalic,
+                   FontOrientation,
+                   FontVariationSettings*);
 #endif
   FontPlatformData(sk_sp<SkTypeface>,
                    const char* name,
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
index 41dc288..73a0cc3 100644
--- a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
+++ b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
@@ -99,6 +99,17 @@
   const uint32_t gdefTag = OTS_TAG('G', 'D', 'E', 'F');
   const uint32_t gposTag = OTS_TAG('G', 'P', 'O', 'S');
   const uint32_t gsubTag = OTS_TAG('G', 'S', 'U', 'B');
+
+  // Font Variations related tables
+  // See "Variation Tables" in Terminology section of
+  // https://www.microsoft.com/typography/otspec/otvaroverview.htm
+  const uint32_t avarTag = OTS_TAG('a', 'v', 'a', 'r');
+  const uint32_t cvarTag = OTS_TAG('c', 'v', 'a', 'r');
+  const uint32_t fvarTag = OTS_TAG('f', 'v', 'a', 'r');
+  const uint32_t gvarTag = OTS_TAG('g', 'v', 'a', 'r');
+  const uint32_t hvarTag = OTS_TAG('H', 'V', 'A', 'R');
+  const uint32_t mvarTag = OTS_TAG('M', 'V', 'A', 'R');
+  const uint32_t vvarTag = OTS_TAG('V', 'V', 'A', 'R');
 #endif
 
   switch (tag) {
@@ -110,6 +121,13 @@
     case cpalTag:
 #if HB_VERSION_ATLEAST(1, 0, 0)
     // Let HarfBuzz handle how to deal with broken tables.
+    case avarTag:
+    case cvarTag:
+    case fvarTag:
+    case gvarTag:
+    case hvarTag:
+    case mvarTag:
+    case vvarTag:
     case gdefTag:
     case gposTag:
     case gsubTag:
diff --git a/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp b/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp
index d6b97cd..fcc5e56 100644
--- a/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp
+++ b/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp
@@ -45,6 +45,12 @@
   paint->setTypeface(m_typeface);
   paint->setFakeBoldText(m_syntheticBold);
   paint->setTextSkewX(m_syntheticItalic ? -SK_Scalar1 / 4 : 0);
+
+  // TODO(drott): Due to Skia bug 5917
+  // https://bugs.chromium.org/p/skia/issues/detail?id=5917 correct advance
+  // width scaling with FreeType for font sizes under 257px currently only works
+  // with:
+  // paint->setHinting(SkPaint::kNo_Hinting);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
index 51655f7b..469f2d48 100644
--- a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
+++ b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
@@ -216,7 +216,8 @@
       substituteFont, platformData.size(), syntheticBold,
       (traits & NSFontItalicTrait) &&
           !(substituteFontTraits & NSFontItalicTrait),
-      platformData.orientation());
+      platformData.orientation(),
+      nullptr);  // No variation paramaters in fallback.
 
   return fontDataFromFontPlatformData(&alternateFont, DoNotRetain);
 }
@@ -281,9 +282,10 @@
   // font loading failing.  Out-of-process loading occurs for registered fonts
   // stored in non-system locations.  When loading fails, we do not want to use
   // the returned FontPlatformData since it will not have a valid SkTypeface.
-  std::unique_ptr<FontPlatformData> platformData = WTF::wrapUnique(
-      new FontPlatformData(platformFont, size, syntheticBold, syntheticItalic,
-                           fontDescription.orientation()));
+  std::unique_ptr<FontPlatformData> platformData =
+      WTF::makeUnique<FontPlatformData>(
+          platformFont, size, syntheticBold, syntheticItalic,
+          fontDescription.orientation(), fontDescription.variationSettings());
   if (!platformData->typeface()) {
     return nullptr;
   }
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
index 27a754dd..d09bc6d 100644
--- a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
+++ b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
@@ -25,6 +25,7 @@
 
 #import "platform/LayoutTestSupport.h"
 #import "platform/fonts/Font.h"
+#import "platform/fonts/opentype/FontSettings.h"
 #import "platform/fonts/shaping/HarfBuzzFace.h"
 #import "platform/graphics/skia/SkiaUtils.h"
 #import "public/platform/Platform.h"
@@ -163,7 +164,8 @@
                                    float size,
                                    bool syntheticBold,
                                    bool syntheticItalic,
-                                   FontOrientation orientation)
+                                   FontOrientation orientation,
+                                   FontVariationSettings* variationSettings)
     : m_textSize(size),
       m_syntheticBold(syntheticBold),
       m_syntheticItalic(syntheticItalic),
@@ -178,6 +180,21 @@
     // and ~/Library Fonts, see crbug.com/72727 or crbug.com/108645.
     m_typeface = loadFromBrowserProcess(nsFont, size);
   }
+
+  if (variationSettings && variationSettings->size() < UINT16_MAX) {
+    SkFontMgr::FontParameters::Axis axes[variationSettings->size()];
+    for (size_t i = 0; i < variationSettings->size(); ++i) {
+      AtomicString featureTag = variationSettings->at(i).tag();
+      axes[i] = {atomicStringToFourByteTag(featureTag),
+                 SkFloatToScalar(variationSettings->at(i).value())};
+    }
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    // TODO crbug.com/670246: Refactor this to a future Skia API that acccepts
+    // axis parameters on system fonts directly.
+    m_typeface = sk_sp<SkTypeface>(fm->createFromStream(
+        m_typeface->openStream(nullptr)->duplicate(),
+        SkFontMgr::FontParameters().setAxes(axes, variationSettings->size())));
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp
new file mode 100644
index 0000000..6759f231
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/fonts/opentype/FontSettings.h"
+
+#include "wtf/HashFunctions.h"
+#include "wtf/StringHasher.h"
+#include "wtf/text/AtomicStringHash.h"
+#include "wtf/text/StringHash.h"
+
+namespace blink {
+
+uint32_t atomicStringToFourByteTag(AtomicString tag) {
+  DCHECK_EQ(tag.length(), 4u);
+  return (((tag[0]) << 24) | ((tag[1]) << 16) | ((tag[2]) << 8) | (tag[3]));
+}
+
+static inline void addToHash(unsigned& hash, unsigned key) {
+  hash = ((hash << 5) + hash) + key;  // Djb2
+};
+
+static inline void addFloatToHash(unsigned& hash, float value) {
+  addToHash(hash, WTF::FloatHash<float>::hash(value));
+};
+
+unsigned FontVariationSettings::hash() const {
+  unsigned computedHash = size() ? 5381 : 0;
+  unsigned numFeatures = size();
+  for (unsigned i = 0; i < numFeatures; ++i) {
+    StringHasher stringHasher;
+    const AtomicString& tag = at(i).tag();
+    for (unsigned j = 0; j < tag.length(); j++) {
+      stringHasher.addCharacter(tag[j]);
+    }
+    addToHash(computedHash, stringHasher.hash());
+    addFloatToHash(computedHash, at(i).value());
+  }
+  return computedHash;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
index d57d2eec..a8b7e48ea 100644
--- a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
@@ -15,6 +15,8 @@
 
 namespace blink {
 
+uint32_t atomicStringToFourByteTag(AtomicString tag);
+
 template <typename T>
 class FontTagValuePair {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
@@ -51,7 +53,7 @@
   FontSettings(){};
 
  private:
-  Vector<T> m_list;
+  Vector<T, 0> m_list;
 };
 
 using FontFeature = FontTagValuePair<int>;
@@ -81,6 +83,8 @@
     return adoptRef(new FontVariationSettings());
   }
 
+  unsigned hash() const;
+
  private:
   FontVariationSettings() = default;
 };
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp b/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp
new file mode 100644
index 0000000..1914118
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/fonts/opentype/FontSettings.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/PassRefPtr.h"
+
+namespace blink {
+
+PassRefPtr<FontVariationSettings> makeFontVariationSettings(
+    std::initializer_list<FontVariationAxis> variationAxes) {
+  RefPtr<FontVariationSettings> variationSettings =
+      FontVariationSettings::create();
+
+  for (auto axis = variationAxes.begin(); axis != variationAxes.end(); ++axis) {
+    variationSettings->append(*axis);
+  }
+  return variationSettings;
+}
+
+TEST(FontSettingsTest, HashTest) {
+  RefPtr<FontVariationSettings> oneAxisA =
+      makeFontVariationSettings({FontVariationAxis{"a   ", 0}});
+  RefPtr<FontVariationSettings> oneAxisB =
+      makeFontVariationSettings({FontVariationAxis{"b   ", 0}});
+  RefPtr<FontVariationSettings> twoAxes = makeFontVariationSettings(
+      {FontVariationAxis{"a   ", 0}, FontVariationAxis{"b   ", 0}});
+  RefPtr<FontVariationSettings> twoAxesDifferentValue =
+      makeFontVariationSettings(
+          {FontVariationAxis{"a   ", 0}, FontVariationAxis{"b   ", 1}});
+
+  RefPtr<FontVariationSettings> emptyVariationSettings =
+      FontVariationSettings::create();
+
+  CHECK_NE(oneAxisA->hash(), oneAxisB->hash());
+  CHECK_NE(oneAxisA->hash(), twoAxes->hash());
+  CHECK_NE(oneAxisA->hash(), twoAxesDifferentValue->hash());
+  CHECK_NE(emptyVariationSettings->hash(), oneAxisA->hash());
+  CHECK_EQ(emptyVariationSettings->hash(), 0u);
+};
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
index 1e5daf32..a364f53 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
@@ -386,6 +386,9 @@
   m_harfBuzzFontData->m_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
   m_harfBuzzFontData->m_rangeSet = rangeSet;
   m_harfBuzzFontData->updateSimpleFontData(m_platformData);
+
+  // TODO crbug.com/674879 - Connect variation axis parameters to future
+  // HarfBuzz API here.
   ASSERT(m_harfBuzzFontData->m_simpleFontData);
   int scale = SkiaScalarToHarfBuzzPosition(m_platformData->size());
   hb_font_set_scale(m_unscaledFont, scale, scale);
diff --git a/third_party/WebKit/Source/platform/heap/GarbageCollected.h b/third_party/WebKit/Source/platform/heap/GarbageCollected.h
index e814c8c0..58c9313c 100644
--- a/third_party/WebKit/Source/platform/heap/GarbageCollected.h
+++ b/third_party/WebKit/Source/platform/heap/GarbageCollected.h
@@ -14,10 +14,8 @@
 
 template <typename T>
 class GarbageCollected;
-class HeapObjectHeader;
 class InlinedGlobalMarkingVisitor;
 class TraceWrapperBase;
-class WrapperVisitor;
 
 // GC_PLUGIN_IGNORE is used to make the plugin ignore a particular class or
 // field when checking for proper usage.  When using GC_PLUGIN_IGNORE
@@ -77,11 +75,7 @@
   virtual void trace(Visitor*) {}
   virtual void adjustAndMark(InlinedGlobalMarkingVisitor) const = 0;
   virtual void trace(InlinedGlobalMarkingVisitor);
-  virtual void adjustAndMarkAndTraceWrapper(const WrapperVisitor*) const = 0;
-  virtual void adjustAndMarkWrapperNoTracing(const WrapperVisitor*) const = 0;
-  virtual void adjustAndTraceMarkedWrapper(const WrapperVisitor*) const = 0;
   virtual bool isHeapObjectAlive() const = 0;
-  virtual HeapObjectHeader* adjustAndGetHeapObjectHeader() const = 0;
 };
 
 #define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(VISITOR, TYPE)                 \
@@ -104,47 +98,6 @@
                                                                               \
  private:
 
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_WRAPPER_METHODS(TYPE)                 \
- private:                                                                    \
-  typedef WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type,  \
-                                    blink::GarbageCollected>                 \
-      IsSubclassOfGarbageCollected;                                          \
-                                                                             \
- public:                                                                     \
-  void adjustAndMarkAndTraceWrapper(const WrapperVisitor* visitor)           \
-      const override {                                                       \
-    static_assert(                                                           \
-        IsSubclassOfGarbageCollected::value,                                 \
-        "only garbage collected objects can have garbage collected mixins"); \
-    return AdjustAndMarkTrait<TYPE>::markAndTraceWrapper(                    \
-        visitor, static_cast<const TYPE*>(this));                            \
-  }                                                                          \
-  void adjustAndMarkWrapperNoTracing(const WrapperVisitor* visitor)          \
-      const override {                                                       \
-    static_assert(                                                           \
-        IsSubclassOfGarbageCollected::value,                                 \
-        "only garbage collected objects can have garbage collected mixins"); \
-    return AdjustAndMarkTrait<TYPE>::markWrapperNoTracing(                   \
-        visitor, static_cast<const TYPE*>(this));                            \
-  }                                                                          \
-  void adjustAndTraceMarkedWrapper(const WrapperVisitor* visitor)            \
-      const override {                                                       \
-    static_assert(                                                           \
-        IsSubclassOfGarbageCollected::value,                                 \
-        "only garbage collected objects can have garbage collected mixins"); \
-    AdjustAndMarkTrait<TYPE>::traceMarkedWrapper(                            \
-        visitor, static_cast<const TYPE*>(this));                            \
-  }                                                                          \
-  HeapObjectHeader* adjustAndGetHeapObjectHeader() const override {          \
-    static_assert(                                                           \
-        IsSubclassOfGarbageCollected::value,                                 \
-        "only garbage collected objects can have garbage collected mixins"); \
-    return AdjustAndMarkTrait<TYPE>::heapObjectHeader(                       \
-        static_cast<const TYPE*>(this));                                     \
-  }                                                                          \
-                                                                             \
- private:
-
 // A C++ object's vptr will be initialized to its leftmost base's vtable after
 // the constructors of all its subclasses have run, so if a subclass constructor
 // tries to access any of the vtbl entries of its leftmost base prematurely,
@@ -204,7 +157,6 @@
   DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE)              \
   DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::InlinedGlobalMarkingVisitor, \
                                          TYPE)                               \
-  DEFINE_GARBAGE_COLLECTED_MIXIN_WRAPPER_METHODS(TYPE)                       \
   DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)                    \
  public:                                                                     \
   bool isHeapObjectAlive() const override {                                  \
diff --git a/third_party/WebKit/Source/platform/heap/Member.h b/third_party/WebKit/Source/platform/heap/Member.h
index 1a6502d..db16863 100644
--- a/third_party/WebKit/Source/platform/heap/Member.h
+++ b/third_party/WebKit/Source/platform/heap/Member.h
@@ -136,12 +136,11 @@
       // constructor wasn't called.
       if (m_creationThreadState) {
         // Member should point to objects that belong in the same ThreadHeap.
-        DCHECK_EQ(&ThreadState::fromObject(m_raw)->heap(),
-                  &m_creationThreadState->heap());
+        DCHECK(m_creationThreadState->isOnThreadHeap(m_raw));
         // Member should point to objects that belong in the same ThreadHeap.
         DCHECK_EQ(&current->heap(), &m_creationThreadState->heap());
       } else {
-        DCHECK_EQ(&ThreadState::fromObject(m_raw)->heap(), &current->heap());
+        DCHECK(current->isOnThreadHeap(m_raw));
       }
     }
 
@@ -361,12 +360,11 @@
     // constructor wasn't called.
     if (m_creationThreadState) {
       // Member should point to objects that belong in the same ThreadHeap.
-      CHECK_EQ(&ThreadState::fromObject(this->m_raw)->heap(),
-               &m_creationThreadState->heap());
+      CHECK(m_creationThreadState->isOnThreadHeap(this->m_raw));
       // Member should point to objects that belong in the same ThreadHeap.
       CHECK_EQ(&current->heap(), &m_creationThreadState->heap());
     } else {
-      CHECK_EQ(&ThreadState::fromObject(this->m_raw)->heap(), &current->heap());
+      CHECK(current->isOnThreadHeap(this->m_raw));
     }
   }
 
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.h b/third_party/WebKit/Source/platform/heap/ThreadState.h
index a8c771b8..284a3de 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.h
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.h
@@ -573,6 +573,15 @@
 
   static const char* gcReasonString(BlinkGC::GCReason);
 
+  // Returns |true| if |object| resides on this thread's heap.
+  // It is well-defined to call this method on any heap allocated
+  // reference, provided its associated heap hasn't been detached
+  // and shut down. Its behavior is undefined for any other pointer
+  // value.
+  bool isOnThreadHeap(const void* object) const {
+    return &fromObject(object)->heap() == &heap();
+  }
+
  private:
   template <typename T>
   friend class PrefinalizerRegistration;
diff --git a/third_party/WebKit/Source/platform/heap/TraceTraits.h b/third_party/WebKit/Source/platform/heap/TraceTraits.h
index 01bfdd9..cf0baa0 100644
--- a/third_party/WebKit/Source/platform/heap/TraceTraits.h
+++ b/third_party/WebKit/Source/platform/heap/TraceTraits.h
@@ -47,28 +47,6 @@
   STATIC_ONLY(AdjustAndMarkTrait);
 
  public:
-  static void markAndTraceWrapper(const WrapperVisitor* visitor, const T* t) {
-    if (visitor->markWrapperHeader(heapObjectHeader(t))) {
-      visitor->markWrappersInAllWorlds(t);
-      visitor->dispatchTraceWrappers(t);
-    }
-  }
-  static void markWrapperNoTracing(const WrapperVisitor* visitor, const T* t) {
-    DCHECK(!heapObjectHeader(t)->isWrapperHeaderMarked());
-    visitor->markWrapperHeader(heapObjectHeader(t));
-  }
-  static void traceMarkedWrapper(const WrapperVisitor* visitor, const T* t) {
-    DCHECK(heapObjectHeader(t)->isWrapperHeaderMarked());
-    // The term *mark* is misleading here as we effectively trace through the
-    // API boundary, i.e., tell V8 that an object is alive. Actual marking
-    // will be done in V8.
-    visitor->markWrappersInAllWorlds(t);
-    visitor->dispatchTraceWrappers(t);
-  }
-  static HeapObjectHeader* heapObjectHeader(const T* t) {
-    return HeapObjectHeader::fromPayload(t);
-  }
-
   template <typename VisitorDispatcher>
   static void mark(VisitorDispatcher visitor, const T* t) {
 #if ENABLE(ASSERT)
@@ -111,19 +89,6 @@
   STATIC_ONLY(AdjustAndMarkTrait);
 
  public:
-  static void markAndTraceWrapper(const WrapperVisitor* visitor, const T* t) {
-    t->adjustAndMarkAndTraceWrapper(visitor);
-  }
-  static void markWrapperNoTracing(const WrapperVisitor* visitor, const T* t) {
-    t->adjustAndMarkWrapperNoTracing(visitor);
-  }
-  static void traceMarkedWrapper(const WrapperVisitor* visitor, const T* t) {
-    t->adjustAndTraceMarkedWrapper(visitor);
-  }
-  static HeapObjectHeader* heapObjectHeader(const T* t) {
-    return t->adjustAndGetHeapObjectHeader();
-  }
-
   template <typename VisitorDispatcher>
   static void mark(VisitorDispatcher visitor, const T* self) {
     if (!self)
@@ -223,41 +188,28 @@
   static void trace(Visitor*, void* self);
   static void trace(InlinedGlobalMarkingVisitor, void* self);
 
-  static void markAndTraceWrapper(const WrapperVisitor* visitor,
-                                  const void* t) {
-    static_assert(CanTraceWrappers<T>::value,
-                  "T should be able to trace wrappers. See "
-                  "dispatchTraceWrappers in WrapperVisitor.h");
-    AdjustAndMarkTrait<T>::markAndTraceWrapper(visitor,
-                                               reinterpret_cast<const T*>(t));
-  }
-  static void markWrapperNoTracing(const WrapperVisitor* visitor,
-                                   const void* t) {
-    static_assert(CanTraceWrappers<T>::value,
-                  "T should be able to trace wrappers. See "
-                  "dispatchTraceWrappers in WrapperVisitor.h");
-    AdjustAndMarkTrait<T>::markWrapperNoTracing(visitor,
-                                                reinterpret_cast<const T*>(t));
-  }
-  static void traceMarkedWrapper(const WrapperVisitor* visitor, const void* t) {
-    static_assert(CanTraceWrappers<T>::value,
-                  "T should be able to trace wrappers. See "
-                  "dispatchTraceWrappers in WrapperVisitor.h");
-    AdjustAndMarkTrait<T>::traceMarkedWrapper(visitor,
-                                              reinterpret_cast<const T*>(t));
-  }
-  static HeapObjectHeader* heapObjectHeader(const void* t) {
-    static_assert(CanTraceWrappers<T>::value,
-                  "T should be able to trace wrappers. See "
-                  "dispatchTraceWrappers in WrapperVisitor.h");
-    return AdjustAndMarkTrait<T>::heapObjectHeader(
-        reinterpret_cast<const T*>(t));
-  }
+  static void markAndTraceWrapper(const WrapperVisitor*, const void*);
+  static void markWrapperNoTracing(const WrapperVisitor*, const void*);
+  static void traceMarkedWrapper(const WrapperVisitor*, const void*);
+  static HeapObjectHeader* heapObjectHeader(const void*);
 
   template <typename VisitorDispatcher>
   static void mark(VisitorDispatcher visitor, const T* t) {
     AdjustAndMarkTrait<T>::mark(visitor, t);
   }
+
+ private:
+  static const T* ToWrapperTracingType(const void* t) {
+    static_assert(CanTraceWrappers<T>::value,
+                  "T should be able to trace wrappers. See "
+                  "dispatchTraceWrappers in WrapperVisitor.h");
+    static_assert(!NeedsAdjustAndMark<T>::value,
+                  "wrapper tracing is not supported within mixins");
+#if DCHECK_IS_ON()
+    DCHECK(HeapObjectHeader::fromPayload(t)->checkHeader());
+#endif
+    return reinterpret_cast<const T*>(t);
+  }
 };
 
 template <typename T>
@@ -281,6 +233,41 @@
   static_cast<T*>(self)->trace(visitor);
 }
 
+template <typename T>
+void TraceTrait<T>::markAndTraceWrapper(const WrapperVisitor* visitor,
+                                        const void* t) {
+  const T* traceable = ToWrapperTracingType(t);
+  if (visitor->markWrapperHeader(heapObjectHeader(traceable))) {
+    visitor->markWrappersInAllWorlds(traceable);
+    visitor->dispatchTraceWrappers(traceable);
+  }
+}
+
+template <typename T>
+void TraceTrait<T>::markWrapperNoTracing(const WrapperVisitor* visitor,
+                                         const void* t) {
+  const T* traceable = ToWrapperTracingType(t);
+  DCHECK(!heapObjectHeader(traceable)->isWrapperHeaderMarked());
+  visitor->markWrapperHeader(heapObjectHeader(traceable));
+}
+
+template <typename T>
+void TraceTrait<T>::traceMarkedWrapper(const WrapperVisitor* visitor,
+                                       const void* t) {
+  const T* traceable = ToWrapperTracingType(t);
+  DCHECK(heapObjectHeader(t)->isWrapperHeaderMarked());
+  // The term *mark* is misleading here as we effectively trace through the
+  // API boundary, i.e., tell V8 that an object is alive. Actual marking
+  // will be done in V8.
+  visitor->markWrappersInAllWorlds(traceable);
+  visitor->dispatchTraceWrappers(traceable);
+}
+
+template <typename T>
+HeapObjectHeader* TraceTrait<T>::heapObjectHeader(const void* t) {
+  return HeapObjectHeader::fromPayload(ToWrapperTracingType(t));
+}
+
 template <typename T, typename Traits>
 struct TraceTrait<HeapVectorBacking<T, Traits>> {
   STATIC_ONLY(TraceTrait);
diff --git a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
index 6315aad..a4fc977 100644
--- a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
+++ b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
@@ -154,14 +154,6 @@
 
 #undef DECLARE_DISPATCH_TRACE_WRAPPERS
 
-  void dispatchTraceWrappers(const void*) const {
-    // This call should never be reached as we access all tracing through
-    // TraceTraits, which will check that we can dispatch at compile time.
-    // This handler is merely here to make adjustAndMarkWrapper compile
-    // for GarbageCollectedMixin objects.
-    NOTREACHED();
-  }
-
   virtual bool markWrapperHeader(HeapObjectHeader*) const = 0;
 
   virtual void markWrappersInAllWorlds(const ScriptWrappable*) const = 0;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
index fda7d476..30442f5 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -105,11 +105,13 @@
 }
 
 void WillBeginFrameIdleTask(RendererScheduler* scheduler,
+                            uint64_t sequence_number,
                             base::SimpleTestTickClock* clock,
                             base::TimeTicks deadline) {
   scheduler->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, sequence_number, clock->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
 }
 
 void UpdateClockToDeadlineIdleTestTask(base::SimpleTestTickClock* clock,
@@ -314,8 +316,9 @@
 
   void DoMainFrame() {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = false;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidCommitFrameToCompositor();
@@ -323,8 +326,9 @@
 
   void DoMainFrameOnCriticalPath() {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
   }
@@ -667,6 +671,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_;
   bool simulate_timer_task_ran_;
   bool simulate_compositor_task_ran_;
+  uint64_t next_begin_frame_number_ = cc::BeginFrameArgs::kStartingFrameNumber;
 
   DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImplTest);
 };
@@ -715,8 +720,9 @@
   EXPECT_EQ(0, run_count);  // Shouldn't run yet as no WillBeginFrame.
 
   scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
   RunUntilIdle();
   EXPECT_EQ(0, run_count);  // Shouldn't run as no DidCommitFrameToCompositor.
 
@@ -726,8 +732,9 @@
   EXPECT_EQ(0, run_count);  // We missed the deadline.
 
   scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
   clock_->Advance(base::TimeDelta::FromMilliseconds(800));
   scheduler_->DidCommitFrameToCompositor();
   RunUntilIdle();
@@ -787,16 +794,18 @@
 
   // Trigger the beginning of an idle period for 1000ms.
   scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
   DoMainFrame();
 
   // End the idle period early (after 500ms), and send a WillBeginFrame which
   // specifies that the next idle period should end 1000ms from now.
   clock_->Advance(base::TimeDelta::FromMilliseconds(500));
   scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
 
   RunUntilIdle();
   EXPECT_EQ(0, run_count);  // Not currently in an idle period.
@@ -2079,8 +2088,9 @@
                  base::RetainedRef(idle_task_runner_), &run_count, clock_.get(),
                  idle_task_runtime, &actual_deadlines));
   idle_task_runner_->PostIdleTask(
-      FROM_HERE, base::Bind(&WillBeginFrameIdleTask,
-                            base::Unretained(scheduler_.get()), clock_.get()));
+      FROM_HERE,
+      base::Bind(&WillBeginFrameIdleTask, base::Unretained(scheduler_.get()),
+                 next_begin_frame_number_++, clock_.get()));
   RunUntilIdle();
   EXPECT_EQ(4, run_count);
 }
@@ -2159,8 +2169,9 @@
   // Next long idle period will be for the maximum time, so
   // CanExceedIdleDeadlineIfRequired should return true.
   scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL));
   EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired());
 }
 
@@ -2371,8 +2382,9 @@
   ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath());
 
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+      cc::BeginFrameArgs::NORMAL);
   scheduler_->WillBeginFrame(begin_frame_args);
   ASSERT_TRUE(scheduler_->BeginMainFrameOnCriticalPath());
 
@@ -2748,8 +2760,9 @@
     simulate_timer_task_ran_ = false;
 
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = false;
     scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -2786,8 +2799,9 @@
     simulate_timer_task_ran_ = false;
 
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = false;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidAnimateForInputOnCompositorThread();
@@ -2827,8 +2841,9 @@
     simulate_timer_task_ran_ = false;
 
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = false;
     scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -2891,8 +2906,9 @@
   SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
                                  blink::WebInputEvent::GestureScrollUpdate);
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+      cc::BeginFrameArgs::NORMAL);
   begin_frame_args.on_critical_path = false;
   scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -2914,8 +2930,9 @@
     RendererSchedulerImplTest,
     EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+      cc::BeginFrameArgs::NORMAL);
   begin_frame_args.on_critical_path = false;
   scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -2938,8 +2955,9 @@
   SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
 
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+      cc::BeginFrameArgs::NORMAL);
   begin_frame_args.on_critical_path = true;
   scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -3049,8 +3067,9 @@
   SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
 
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+      cc::BeginFrameArgs::NORMAL);
   begin_frame_args.on_critical_path = true;
   scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -3094,8 +3113,9 @@
 
   for (int i = 0; i < 1000; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3157,8 +3177,9 @@
   bool suspended = false;
   for (int i = 0; i < 1000; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3214,8 +3235,9 @@
 
   for (int i = 0; i < 1000; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3253,8 +3275,9 @@
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
 
   cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-      base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+      cc::BeginFrameArgs::NORMAL);
   begin_frame_args.on_critical_path = true;
   scheduler_->WillBeginFrame(begin_frame_args);
 
@@ -3314,8 +3337,9 @@
 
   for (int i = 0; i < 100; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3355,8 +3379,9 @@
 
   for (int i = 0; i < 100; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3398,8 +3423,9 @@
 
   for (int i = 0; i < 100; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
@@ -3522,8 +3548,9 @@
 
   for (int i = 0; i < 1000; i++) {
     cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(),
-        base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL);
+        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(),
+        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+        cc::BeginFrameArgs::NORMAL);
     begin_frame_args.on_critical_path = true;
     scheduler_->WillBeginFrame(begin_frame_args);
     scheduler_->DidHandleInputEventOnCompositorThread(
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
index a246461..1c616d32 100644
--- a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
+++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
@@ -531,7 +531,9 @@
     return;
   m_enabled = e;
   theme().updateEnabledState(*this);
-  setNeedsPaintInvalidation(AllParts);
+
+  ScrollbarPart invalidParts = theme().invalidateOnEnabledChange();
+  setNeedsPaintInvalidation(invalidParts);
 }
 
 int Scrollbar::scrollbarThickness() const {
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h
index a4789a58..6bb469f 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h
@@ -34,14 +34,14 @@
   MOCK_CONST_METHOD1(scrollSize, int(ScrollbarOrientation));
   MOCK_CONST_METHOD0(isScrollCornerVisible, bool());
   MOCK_CONST_METHOD0(scrollCornerRect, IntRect());
-  MOCK_CONST_METHOD0(horizontalScrollbar, Scrollbar*());
-  MOCK_CONST_METHOD0(verticalScrollbar, Scrollbar*());
   MOCK_CONST_METHOD0(enclosingScrollableArea, ScrollableArea*());
   MOCK_CONST_METHOD1(visibleContentRect, IntRect(IncludeScrollbarsInRect));
   MOCK_CONST_METHOD0(contentsSize, IntSize());
   MOCK_CONST_METHOD0(scrollableAreaBoundingBox, IntRect());
   MOCK_CONST_METHOD0(layerForHorizontalScrollbar, GraphicsLayer*());
   MOCK_CONST_METHOD0(layerForVerticalScrollbar, GraphicsLayer*());
+  MOCK_CONST_METHOD0(horizontalScrollbar, Scrollbar*());
+  MOCK_CONST_METHOD0(verticalScrollbar, Scrollbar*());
 
   bool userInputScrollable(ScrollbarOrientation) const override { return true; }
   bool scrollbarsCanBeActive() const override { return true; }
@@ -68,13 +68,16 @@
 
   DEFINE_INLINE_VIRTUAL_TRACE() { ScrollableArea::trace(visitor); }
 
+ protected:
+  explicit MockScrollableArea() : m_maximumScrollOffset(ScrollOffset(0, 100)) {}
+  explicit MockScrollableArea(const ScrollOffset& offset)
+      : m_maximumScrollOffset(offset) {}
+
  private:
   void setMaximumScrollOffset(const ScrollOffset& maximumScrollOffset) {
     m_maximumScrollOffset = maximumScrollOffset;
   }
 
-  explicit MockScrollableArea() : m_maximumScrollOffset(ScrollOffset(0, 100)) {}
-
   ScrollOffset m_scrollOffset;
   ScrollOffset m_maximumScrollOffset;
 };
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
index 9036cd2..948af56 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
@@ -94,6 +94,10 @@
     return AllParts;
   }
 
+  // Returns parts of the scrollbar which must be repainted following a change
+  // in enabled state.
+  virtual ScrollbarPart invalidateOnEnabledChange() const { return AllParts; }
+
   virtual void paintScrollCorner(GraphicsContext&,
                                  const DisplayItemClient&,
                                  const IntRect& cornerRect);
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp
index c2ef26e9..14878b96 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp
@@ -59,6 +59,21 @@
       m_allowHitTest(allowHitTest),
       m_useSolidColor(false) {}
 
+bool ScrollbarThemeOverlay::shouldRepaintAllPartsOnInvalidation() const {
+  return false;
+}
+
+ScrollbarPart ScrollbarThemeOverlay::invalidateOnThumbPositionChange(
+    const ScrollbarThemeClient&,
+    float oldPosition,
+    float newPosition) const {
+  return NoPart;
+}
+
+ScrollbarPart ScrollbarThemeOverlay::invalidateOnEnabledChange() const {
+  return NoPart;
+}
+
 int ScrollbarThemeOverlay::scrollbarThickness(
     ScrollbarControlSize controlSize) {
   return m_thumbThickness + m_scrollbarMargin;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h
index d746291..0772e21 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h
@@ -46,6 +46,15 @@
                         Color);
   ~ScrollbarThemeOverlay() override {}
 
+  bool shouldRepaintAllPartsOnInvalidation() const override;
+
+  ScrollbarPart invalidateOnThumbPositionChange(
+      const ScrollbarThemeClient&,
+      float oldPosition,
+      float newPosition) const override;
+
+  ScrollbarPart invalidateOnEnabledChange() const override;
+
   int scrollbarThickness(ScrollbarControlSize) override;
   int scrollbarMargin() const override;
   bool usesOverlayScrollbars() const override;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
new file mode 100644
index 0000000..028ed37e
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/scroll/ScrollbarThemeOverlay.h"
+
+#include "platform/scroll/ScrollbarTestSuite.h"
+
+namespace blink {
+
+using testing::NiceMock;
+using testing::Return;
+
+class ScrollbarThemeOverlayTest : public ScrollbarTestSuite {};
+
+TEST_F(ScrollbarThemeOverlayTest, PaintInvalidation) {
+  NiceMock<MockScrollableArea>* mockScrollableArea =
+      new NiceMock<MockScrollableArea>(ScrollOffset(100, 100));
+  ScrollbarThemeOverlay theme(14, 0, ScrollbarThemeOverlay::AllowHitTest);
+
+  Scrollbar* verticalScrollbar = Scrollbar::createForTesting(
+      mockScrollableArea, VerticalScrollbar, RegularScrollbar, &theme);
+  Scrollbar* horizontalScrollbar = Scrollbar::createForTesting(
+      mockScrollableArea, HorizontalScrollbar, RegularScrollbar, &theme);
+  ON_CALL(*mockScrollableArea, verticalScrollbar())
+      .WillByDefault(Return(verticalScrollbar));
+  ON_CALL(*mockScrollableArea, horizontalScrollbar())
+      .WillByDefault(Return(horizontalScrollbar));
+
+  IntRect verticalRect(1010, 0, 14, 768);
+  IntRect horizontalRect(0, 754, 1024, 14);
+  verticalScrollbar->setFrameRect(verticalRect);
+  horizontalScrollbar->setFrameRect(horizontalRect);
+
+  ASSERT_EQ(verticalScrollbar, mockScrollableArea->verticalScrollbar());
+  ASSERT_EQ(horizontalScrollbar, mockScrollableArea->horizontalScrollbar());
+
+  verticalScrollbar->clearTrackNeedsRepaint();
+  verticalScrollbar->clearThumbNeedsRepaint();
+  horizontalScrollbar->clearTrackNeedsRepaint();
+  horizontalScrollbar->clearThumbNeedsRepaint();
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  ASSERT_FALSE(verticalScrollbar->thumbNeedsRepaint());
+  ASSERT_FALSE(verticalScrollbar->trackNeedsRepaint());
+  ASSERT_FALSE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+  ASSERT_FALSE(horizontalScrollbar->thumbNeedsRepaint());
+  ASSERT_FALSE(horizontalScrollbar->trackNeedsRepaint());
+  ASSERT_FALSE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation());
+
+  // Changing the scroll offset shouldn't invalide the thumb nor track, but it
+  // should cause a "general" invalidation for non-composited scrollbars.
+  // Ensure the horizontal scrollbar is unaffected.
+  mockScrollableArea->updateScrollOffset(ScrollOffset(0, 5), UserScroll);
+  verticalScrollbar->offsetDidChange();
+  horizontalScrollbar->offsetDidChange();
+  EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+  EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint());
+  EXPECT_FALSE(horizontalScrollbar->trackNeedsRepaint());
+  EXPECT_FALSE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation());
+
+  // Try the horizontal scrollbar.
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+  mockScrollableArea->updateScrollOffset(ScrollOffset(5, 5), UserScroll);
+  horizontalScrollbar->offsetDidChange();
+  verticalScrollbar->offsetDidChange();
+  EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint());
+  EXPECT_FALSE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+  EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint());
+  EXPECT_FALSE(horizontalScrollbar->trackNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation());
+
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  // Move the mouse over the vertical scrollbar's thumb. Ensure the thumb is
+  // invalidated as its state is changed to hover.
+  verticalScrollbar->setHoveredPart(ThumbPart);
+  EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  verticalScrollbar->clearThumbNeedsRepaint();
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  // Pressing down should also cause an invalidation.
+  verticalScrollbar->setPressedPart(ThumbPart);
+  EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  verticalScrollbar->clearThumbNeedsRepaint();
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  // Release should cause invalidation.
+  verticalScrollbar->setPressedPart(NoPart);
+  EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  verticalScrollbar->clearThumbNeedsRepaint();
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  // Move off should cause invalidation
+  verticalScrollbar->setHoveredPart(NoPart);
+  EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  verticalScrollbar->clearThumbNeedsRepaint();
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  // Disabling the scrollbar is used to hide it so it should cause invalidation
+  // but only in the general sense since the compositor will have hidden it
+  // without a repaint.
+  verticalScrollbar->setEnabled(false);
+  EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  mockScrollableArea->clearNeedsPaintInvalidationForScrollControls();
+
+  verticalScrollbar->setEnabled(true);
+  EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint());
+  EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation());
+
+  ThreadState::current()->collectAllGarbage();
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
index c4bd3ec..70d07b7f 100644
--- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
+++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
@@ -27,11 +27,8 @@
 class WebURLLoaderTestDelegate;
 
 // A factory that creates WebURLLoaderMock to simulate resource loading in
-// tests.
-// You register files for specific URLs, the content of the file is then served
-// when these URLs are loaded.
-// In order to serve the asynchronous requests, you need to invoke
-// ServeAsynchronousRequest.
+// tests. Since there are restriction and rules to follow, please read comments
+// in WebURLLoaderMockFactory carefully to use this class correctly.
 class WebURLLoaderMockFactoryImpl : public WebURLLoaderMockFactory {
  public:
   WebURLLoaderMockFactoryImpl();
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index 09ea9e4..75a82ab8 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -477,12 +477,6 @@
 }
 
 void ChromeClientImpl::closeWindowSoon() {
-  // Make sure this Page can no longer be found by JS.
-  m_webView->page()->willBeClosed();
-
-  // Make sure that all loading is stopped.  Ensures that JS stops executing!
-  m_webView->mainFrame()->stopLoading();
-
   if (m_webView->client())
     m_webView->client()->closeWidgetSoon();
 }
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
index 7449b666..00a0c734 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
@@ -75,6 +75,10 @@
 void WebFrameWidgetBase::dragTargetDragLeave() {
   DCHECK(m_currentDragData);
 
+  if (ignoreInputEvents()) {
+    cancelDrag();
+    return;
+  }
   DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(),
                     static_cast<DragOperation>(m_operationsAllowed));
 
@@ -109,12 +113,13 @@
     return;
   }
 
-  m_currentDragData->setModifiers(modifiers);
-  DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
-                    static_cast<DragOperation>(m_operationsAllowed));
+  if (!ignoreInputEvents()) {
+    m_currentDragData->setModifiers(modifiers);
+    DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint,
+                      static_cast<DragOperation>(m_operationsAllowed));
 
-  page()->dragController().performDrag(&dragData, *toCoreFrame(localRoot()));
-
+    page()->dragController().performDrag(&dragData, *toCoreFrame(localRoot()));
+  }
   m_dragOperation = WebDragOperationNone;
   m_currentDragData = nullptr;
 }
@@ -122,6 +127,10 @@
 void WebFrameWidgetBase::dragSourceEndedAt(const WebPoint& pointInViewport,
                                            const WebPoint& screenPoint,
                                            WebDragOperation operation) {
+  if (ignoreInputEvents()) {
+    cancelDrag();
+    return;
+  }
   WebPoint pointInRootFrame(
       page()->frameHost().visualViewport().viewportToRootFrame(
           pointInViewport));
@@ -135,12 +144,16 @@
 }
 
 void WebFrameWidgetBase::dragSourceSystemDragEnded() {
-  // It's possible for us to get this callback while not doing a drag if it's
-  // from a previous page that got unloaded.
-  if (m_doingDragAndDrop) {
-    page()->dragController().dragEnded();
-    m_doingDragAndDrop = false;
-  }
+  cancelDrag();
+}
+
+void WebFrameWidgetBase::cancelDrag() {
+  // It's possible for us this to be callback while we're not doing a drag if
+  // it's from a previous page that got unloaded.
+  if (!m_doingDragAndDrop)
+    return;
+  page()->dragController().dragEnded();
+  m_doingDragAndDrop = false;
 }
 
 void WebFrameWidgetBase::startDragging(WebReferrerPolicy policy,
@@ -158,6 +171,10 @@
     DragAction dragAction,
     int modifiers) {
   DCHECK(m_currentDragData);
+  if (ignoreInputEvents()) {
+    cancelDrag();
+    return WebDragOperationNone;
+  }
 
   WebPoint pointInRootFrame(viewportToRootFrame(pointInViewport));
 
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.h b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
index 67d3dd7a5..f424460 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetBase.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
@@ -108,6 +108,8 @@
   WebDragOperation m_dragOperation = WebDragOperationNone;
 
  private:
+  void cancelDrag();
+
   static bool s_ignoreInputEvents;
 };
 
diff --git a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
index 034bde6..4ec3622 100644
--- a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
@@ -30,6 +30,7 @@
 
 #include "core/loader/FrameLoadRequest.h"
 #include "core/page/Page.h"
+#include "core/page/ScopedPageSuspender.h"
 #include "public/platform/WebInputEvent.h"
 #include "public/web/WebFrameClient.h"
 #include "public/web/WebLocalFrame.h"
@@ -268,14 +269,13 @@
 };
 
 TEST_F(CreateWindowTest, CreateWindowFromSuspendedPage) {
-  m_webView->page()->setSuspended(true);
+  ScopedPageSuspender suspender;
   LocalFrame* frame = toWebLocalFrameImpl(m_mainFrame)->frame();
   FrameLoadRequest request(frame->document());
   WindowFeatures features;
   EXPECT_EQ(nullptr,
             m_chromeClientImpl->createWindow(frame, request, features,
                                              NavigationPolicyNewForegroundTab));
-  m_webView->page()->setSuspended(false);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 257c108f..0840d8c1 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -4119,6 +4119,29 @@
   EXPECT_FALSE(webView->page()->suspended());
 }
 
+TEST_P(WebViewTest, ClosingPageIsSuspended) {
+  WebViewImpl* webView = m_webViewHelper.initialize();
+  Page* page = m_webViewHelper.webView()->page();
+  EXPECT_FALSE(page->suspended());
+
+  webView->setOpenedByDOM();
+
+  LocalFrame* mainFrame = toLocalFrame(page->mainFrame());
+  EXPECT_FALSE(mainFrame->domWindow()->closed());
+
+  mainFrame->domWindow()->close(nullptr);
+  // The window should be marked closed...
+  EXPECT_TRUE(mainFrame->domWindow()->closed());
+  // EXPECT_TRUE(page->isClosing());
+  // ...but not yet detached.
+  EXPECT_TRUE(mainFrame->host());
+
+  {
+    ScopedPageSuspender suspender;
+    EXPECT_TRUE(page->suspended());
+  }
+}
+
 TEST_P(WebViewTest, ForceAndResetViewport) {
   URLTestHelpers::registerMockedURLFromBaseURL(
       WebString::fromUTF8(m_baseURL.c_str()),
diff --git a/third_party/WebKit/public/platform/DEPS b/third_party/WebKit/public/platform/DEPS
index eeefc68..76b11842 100644
--- a/third_party/WebKit/public/platform/DEPS
+++ b/third_party/WebKit/public/platform/DEPS
@@ -19,6 +19,7 @@
     "+platform/weborigin",
     "+public/platform",
     "-public/web",
+    "+services/service_manager/public/interfaces",
     "+third_party/skia",
     "+url",
     "-web",
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index 0aae60e68..fce5664 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -56,6 +56,7 @@
 #include "base/metrics/user_metrics_action.h"
 #include "cc/resources/shared_bitmap.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace gpu {
 class GpuMemoryBufferManager;
@@ -590,6 +591,15 @@
 
   virtual InterfaceProvider* interfaceProvider();
 
+  // Sets up a connection to the ServiceManager by binding |remoteHandle| to a
+  // remote implementation of
+  // //service_manager/public/interfaces/connector.mojom. Using this connection
+  // the caller can then request connections to other services.
+  // NOTE: This handle is not strongly typed because neither the Blink nor
+  // Chromium types generated from connector.mojom should leak across the
+  // Blink-Chromium boundary.
+  virtual void bindServiceConnector(mojo::ScopedMessagePipeHandle remoteHandle);
+
   // Platform events -----------------------------------------------------
   // Device Orientation, Device Motion, Device Light, Battery, Gamepad.
 
diff --git a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
index 1e17c33..b3cc9b16 100644
--- a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
+++ b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
@@ -32,11 +32,15 @@
 
   // Registers a response and the file to be served when the specified URL
   // is loaded. If no file is specified then the response content will be empty.
+  // unregisterURL() should be called for each test entry before registering
+  // another response for the same URL from another test.
   virtual void registerURL(const WebURL&,
                            const WebURLResponse&,
                            const WebString& filePath) = 0;
 
   // Registers an error to be served when the specified URL is requested.
+  // unregisterURL() should be called for each test entry before registering
+  // another response for the same URL from another test.
   virtual void registerErrorURL(const WebURL&,
                                 const WebURLResponse&,
                                 const WebURLError&) = 0;
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index 28d95f64..a34a9e6 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -68,6 +68,7 @@
     <classpathentry kind="src" path="components/sync/android/java/src"/>
     <classpathentry kind="src" path="components/sync/android/javatests/src"/>
     <classpathentry kind="src" path="components/test/android/browsertests_apk/src"/>
+    <classpathentry kind="src" path="components/url_formatter/android/java/src"/>
     <classpathentry kind="src" path="components/variations/android/java/src"/>
     <classpathentry kind="src" path="components/web_contents_delegate_android/android/java/src"/>
     <classpathentry kind="src" path="content/public/android/java/src"/>
@@ -98,6 +99,7 @@
     <classpathentry kind="src" path="remoting/android/apk/src"/>
     <classpathentry kind="src" path="remoting/android/java/src"/>
     <classpathentry kind="src" path="remoting/android/javatests/src"/>
+    <classpathentry kind="src" path="services/service_manager/public/java/src"/>
     <classpathentry kind="src" path="testing/android/appurify_support/java/src"/>
     <classpathentry kind="src" path="testing/android/broker/java/src"/>
     <classpathentry kind="src" path="testing/android/driver/java/src"/>
@@ -254,6 +256,7 @@
     <classpathentry kind="lib" path="out/Debug/lib.java/components/invalidation/impl/proto_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/components/navigation_interception/android/navigation_interception_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/components/policy/android/policy_java.jar"/>
+    <classpathentry kind="lib" path="out/Debug/lib.java/components/payments/payment_request_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/components/precache/android/precache_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/components/safe_json/android/safe_json_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/components/service_tab_launcher/service_tab_launcher_java.jar"/>
@@ -274,6 +277,7 @@
     <classpathentry kind="lib" path="out/Debug/lib.java/mojo/public/java/system.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/net/android/net_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/printing/printing_java.jar"/>
+    <classpathentry kind="lib" path="out/Debug/lib.java/services/service_manager/public/interfaces/interfaces_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/sync/android/sync_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/sync/test_support_proto_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_data_chart/android_data_chart_java.jar"/>
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py
index 381ba75ba..695bb563 100644
--- a/tools/chrome_proxy/webdriver/common.py
+++ b/tools/chrome_proxy/webdriver/common.py
@@ -527,15 +527,22 @@
         http_response.response_headers['via'])
 
   @staticmethod
-  def RunAllTests():
+  def RunAllTests(run_all_tests=False):
     """A simple helper method to run all tests using unittest.main().
+
+    Args:
+      run_all_tests: If True, all tests in the directory will be run, Otherwise
+        only the tests in the file given on the command line will be run.
     """
     flags = ParseFlags()
     logger = GetLogger()
     logger.debug('Command line args: %s', str(sys.argv))
     logger.info('sys.argv parsed to %s', str(flags))
-    # The unittest library uses sys.argv itself and is easily confused by our
-    # command line options. Pass it a simpler argv instead, while working in the
-    # unittest command line args functionality.
-    unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast,
-      catchbreak=flags.catch, buffer=(not flags.disable_buffer))
+    if flags.catch:
+      unittest.installHandler()
+    pattern = '*.py' if run_all_tests else os.path.basename(sys.argv[0])
+    loader = unittest.TestLoader()
+    tests = loader.discover(os.path.dirname(__file__), pattern=pattern)
+    testRunner = unittest.runner.TextTestRunner(verbosity=2,
+      failfast=flags.failfast, buffer=(not flags.disable_buffer))
+    testRunner.run(tests)
diff --git a/services/device/public/cpp/BUILD.gn b/tools/chrome_proxy/webdriver/run_all_tests.py
similarity index 61%
copy from services/device/public/cpp/BUILD.gn
copy to tools/chrome_proxy/webdriver/run_all_tests.py
index f341a18b..604b4764 100644
--- a/services/device/public/cpp/BUILD.gn
+++ b/tools/chrome_proxy/webdriver/run_all_tests.py
@@ -2,9 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("cpp") {
-  sources = [
-    "constants.cc",
-    "constants.h",
-  ]
-}
+import common
+
+if __name__ == "__main__":
+  common.IntegrationTest.RunAllTests(run_all_tests=True)
diff --git a/tools/clang-format-js b/tools/clang-format-js
new file mode 100755
index 0000000..d09925d
--- /dev/null
+++ b/tools/clang-format-js
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if [[ -z "${@}" ]]; then
+  echo >&2 "Usage: `basename $0` <paths_to_clang_format...>";
+  exit 1;
+fi
+
+which clang-format >/dev/null 2>&1;
+if [[ $? -ne 0 ]]; then
+  echo >&2 "Sorry, but you need \`clang-format\` on your \$PATH to run this script";
+  exit 1;
+fi
+
+for arg in "${@}"; do
+  dir=`readlink -f "${arg}"`
+  while dir=`dirname ${dir}`; do
+    if [[ -f "${dir}/.clang-format" ]]; then
+      echo "Using style from: ${dir}/.clang-format";
+      break;
+    elif [[ "${dir}" == "/" ]]; then
+      echo >&2 "No .clang-format file found. Make one at or above ${arg}";
+      exit 1;
+    fi
+  done
+done
+
+find "${@}" -type f -iname '*.js' | xargs clang-format -i -style=file;
diff --git a/tools/clang/rewrite_to_chrome_style/CMakeLists.txt b/tools/clang/rewrite_to_chrome_style/CMakeLists.txt
index 8fa96ef..1935e239 100644
--- a/tools/clang/rewrite_to_chrome_style/CMakeLists.txt
+++ b/tools/clang/rewrite_to_chrome_style/CMakeLists.txt
@@ -7,6 +7,7 @@
   )
 
 add_llvm_executable(rewrite_to_chrome_style
+  EditTracker.cpp
   RewriteToChromeStyle.cpp
   )
 
diff --git a/tools/clang/rewrite_to_chrome_style/EditTracker.cpp b/tools/clang/rewrite_to_chrome_style/EditTracker.cpp
new file mode 100644
index 0000000..cd8228e
--- /dev/null
+++ b/tools/clang/rewrite_to_chrome_style/EditTracker.cpp
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "EditTracker.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+void EditTracker::Add(const clang::SourceManager& source_manager,
+                      clang::SourceLocation location,
+                      llvm::StringRef original_text,
+                      llvm::StringRef new_text) {
+  llvm::StringRef filename;
+  for (int i = 0; i < 10; i++) {
+    filename = source_manager.getFilename(location);
+    if (!filename.empty() || !location.isMacroID())
+      break;
+    // Otherwise, no filename and the SourceLocation is a macro ID. Look one
+    // level up the stack...
+    location = source_manager.getImmediateMacroCallerLoc(location);
+  }
+  assert(!filename.empty() && "Can't track edit with no filename!");
+  auto result = tracked_edits_.try_emplace(original_text);
+  if (result.second) {
+    result.first->getValue().new_text = new_text;
+  }
+  result.first->getValue().filenames.try_emplace(filename);
+}
+
+void EditTracker::SerializeTo(llvm::StringRef tag,
+                              llvm::raw_ostream& output) const {
+  for (const auto& edit : tracked_edits_) {
+    for (const auto& filename : edit.getValue().filenames) {
+      output << filename.getKey() << ":" << tag << ":" << edit.getKey() << ":"
+             << edit.getValue().new_text << "\n";
+    }
+  }
+}
diff --git a/tools/clang/rewrite_to_chrome_style/EditTracker.h b/tools/clang/rewrite_to_chrome_style/EditTracker.h
new file mode 100644
index 0000000..ef5e301
--- /dev/null
+++ b/tools/clang/rewrite_to_chrome_style/EditTracker.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_
+#define TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_
+
+#include <map>
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace llvm {
+class raw_ostream;
+}  // namespace llvm
+
+struct EditInfo {
+  std::string new_text;
+  llvm::StringSet<> filenames;
+};
+
+// Simple class that tracks the edits made by path. Used to dump the databaes
+// used by the Blink rebase helper.
+class EditTracker {
+ public:
+  EditTracker() = default;
+
+  void Add(const clang::SourceManager& source_manager,
+           clang::SourceLocation location,
+           llvm::StringRef original_text,
+           llvm::StringRef new_text);
+
+  // Serializes the tracked edits to |output|. Emits:
+  // <filename>:<tag>:<original text>:<new text>
+  // for each distinct filename for each tracked edit.
+  void SerializeTo(llvm::StringRef tag, llvm::raw_ostream& output) const;
+
+ private:
+  EditTracker(const EditTracker&) = delete;
+  EditTracker& operator=(const EditTracker&) = delete;
+
+  // The string key is the original text.
+  llvm::StringMap<EditInfo> tracked_edits_;
+};
+
+#endif  // #define TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
index 3e18a280..3374ea29 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -14,10 +14,9 @@
 
 #include <assert.h>
 #include <algorithm>
-#include <fstream>
 #include <memory>
+#include <set>
 #include <string>
-#include <unordered_map>
 
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -33,12 +32,7 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/TargetSelect.h"
 
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <sys/file.h>
-#include <unistd.h>
-#endif
+#include "EditTracker.h"
 
 using namespace clang::ast_matchers;
 using clang::tooling::CommonOptionsParser;
@@ -694,19 +688,17 @@
 
     clang::SourceLocation loc =
         TargetNodeTraits<TargetNode>::GetLoc(GetTargetNode(result));
+    edit_tracker_.Add(*result.SourceManager, loc, old_name, new_name);
+
     clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(loc);
     replacements_->emplace(*result.SourceManager, range, new_name);
-    replacement_names_.emplace(old_name.str(), std::move(new_name));
   }
 
-  const std::unordered_map<std::string, std::string>& replacement_names()
-      const {
-    return replacement_names_;
-  }
+  const EditTracker& edit_tracker() const { return edit_tracker_; }
 
  private:
   std::set<Replacement>* const replacements_;
-  std::unordered_map<std::string, std::string> replacement_names_;
+  EditTracker edit_tracker_;
 };
 
 template <typename DeclNode, typename TargetNode>
@@ -1287,38 +1279,15 @@
   if (result != 0)
     return result;
 
-#if defined(_WIN32)
-  HANDLE lockfd = CreateFile("rewrite-sym.lock", GENERIC_READ, FILE_SHARE_READ,
-                             NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-  OVERLAPPED overlapped = {};
-  LockFileEx(lockfd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &overlapped);
-#else
-  int lockfd = open("rewrite-sym.lock", O_RDWR | O_CREAT, 0666);
-  while (flock(lockfd, LOCK_EX)) {  // :D
-  }
-#endif
-
-  std::ofstream replacement_db_file("rewrite-sym.txt",
-                                    std::ios_base::out | std::ios_base::app);
-  for (const auto& p : field_decl_rewriter.replacement_names())
-    replacement_db_file << "var:" << p.first << ":" << p.second << "\n";
-  for (const auto& p : var_decl_rewriter.replacement_names())
-    replacement_db_file << "var:" << p.first << ":" << p.second << "\n";
-  for (const auto& p : enum_member_decl_rewriter.replacement_names())
-    replacement_db_file << "enu:" << p.first << ":" << p.second << "\n";
-  for (const auto& p : function_decl_rewriter.replacement_names())
-    replacement_db_file << "fun:" << p.first << ":" << p.second << "\n";
-  for (const auto& p : method_decl_rewriter.replacement_names())
-    replacement_db_file << "fun:" << p.first << ":" << p.second << "\n";
-  replacement_db_file.close();
-
-#if defined(_WIN32)
-  UnlockFileEx(lockfd, 0, 1, 0, &overlapped);
-  CloseHandle(lockfd);
-#else
-  flock(lockfd, LOCK_UN);
-  close(lockfd);
-#endif
+  // Supplemental data for the Blink rename rebase helper.
+  // TODO(dcheng): There's a lot of match rewriters missing from this list.
+  llvm::outs() << "==== BEGIN TRACKED EDITS ====\n";
+  field_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs());
+  var_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs());
+  enum_member_decl_rewriter.edit_tracker().SerializeTo("enu", llvm::outs());
+  function_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs());
+  method_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs());
+  llvm::outs() << "==== END TRACKED EDITS ====\n";
 
   // Serialization format is documented in tools/clang/scripts/run_tool.py
   llvm::outs() << "==== BEGIN EDITS ====\n";
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
index 327d3b1..ddc092e 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
@@ -50,10 +50,10 @@
 
   // These are special functions that we don't rename so that range-based
   // for loops and STL things work.
-  MyIterator begin() {}
-  my_iterator end() {}
-  my_iterator rbegin() {}
-  MyIterator rend() {}
+  MyIterator begin() { return {}; }
+  my_iterator end() { return {}; }
+  my_iterator rbegin() { return {}; }
+  MyIterator rend() { return {}; }
   // The trace() method is used by Oilpan, but we plan to tweak the Oilpan's
   // clang plugin, so that it recognizes the new method name.
   void Trace() {}
@@ -65,8 +65,8 @@
 
 class Other {
   // Static begin/end/trace don't count, and should be renamed.
-  static MyIterator Begin() {}
-  static my_iterator End() {}
+  static MyIterator Begin() { return {}; }
+  static my_iterator End() { return {}; }
   static void Trace() {}
   static void Lock() {}
 };
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
index afb5162..a5360ac 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
@@ -54,10 +54,10 @@
 
   // These are special functions that we don't rename so that range-based
   // for loops and STL things work.
-  MyIterator begin() {}
-  my_iterator end() {}
-  my_iterator rbegin() {}
-  MyIterator rend() {}
+  MyIterator begin() { return {}; }
+  my_iterator end() { return {}; }
+  my_iterator rbegin() { return {}; }
+  MyIterator rend() { return {}; }
   // The trace() method is used by Oilpan, but we plan to tweak the Oilpan's
   // clang plugin, so that it recognizes the new method name.
   void trace() {}
@@ -69,8 +69,8 @@
 
 class Other {
   // Static begin/end/trace don't count, and should be renamed.
-  static MyIterator begin() {}
-  static my_iterator end() {}
+  static MyIterator begin() { return {}; }
+  static my_iterator end() { return {}; }
   static void trace() {}
   static void lock() {}
 };
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index a8fe279..31c3aef 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -34,7 +34,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=1
+CLANG_SUB_REVISION=2
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 58302431..425567b 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1752,6 +1752,8 @@
     },
 
     'ndk_next': {
+      # GCC is unsupported starting in r13.
+      'mixins': ['clang'],
       'gn_args': 'android_ndk_version="r13b" android_ndk_major_version=13',
     },
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 817f4bc..9ea96f3 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -28685,6 +28685,9 @@
 
 <histogram name="Navigation.Start.RendererBrowserDifference.Negative"
     units="ms">
+  <obsolete>
+    Data collected and posted on issue 675833.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The difference between Now() in the renderer and browser_navigation_start
@@ -28698,6 +28701,9 @@
 
 <histogram name="Navigation.Start.RendererBrowserDifference.Positive"
     units="ms">
+  <obsolete>
+    Data collected and posted on issue 675833.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The difference between Now() in the renderer and browser_navigation_start
@@ -49602,6 +49608,9 @@
 </histogram>
 
 <histogram name="Prerender.PerceivedPLTMatchedComplete" units="ms">
+  <obsolete>
+    Deprecated August 2016
+  </obsolete>
   <owner>pasko@chromium.org</owner>
   <summary>
     Time from when a user navigates to a page to when it loads. Since the pages
@@ -83710,6 +83719,9 @@
       label="Enables users to set weak PINs for the lock screen PIN."/>
   <int value="357"
       label="Enables domain admins to sets a device policy wallpaper."/>
+  <int value="358"
+      label="Enable the creation of roaming copies for Chrome profiles."/>
+  <int value="359" label="Set the roaming profile directory."/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations" type="int">
@@ -88069,6 +88081,7 @@
   <int value="1721" label="V8PeriodicWave_Constructor"/>
   <int value="1722" label="V8StereoPannerNode_Constructor"/>
   <int value="1723" label="V8WaveShaperNode_Constructor"/>
+  <int value="1724" label="V8Headers_GetAll_Method"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 927580b..b842ac5 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -910,16 +910,65 @@
 </rappor-metric>
 
 <rappor-metric
+    name="Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2"
+    type="UMA_RAPPOR_TYPE">
+  <owner>nzolghadr@chromium.org</owner>
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    The eTLD+1 of the website visited, along with the time between initial
+    creation of a touch event and the start of the frame swap on the GPU service
+    caused by the generated ScrollUpdate gesture event if that ScrollUpdate is
+    the first such event in a given scroll gesture event sequence. If no swap
+    was induced by the event, no recording is made.
+  </summary>
+  <string-field name="Domain">
+    <summary>
+      The domain+registry of the URL.
+    </summary>
+  </string-field>
+  <uint64-field name="Latency">
+    <summary>
+      Latency in microseconds representing first
+      Touch.TimeToScrollUpdateSwapBegin.
+    </summary>
+  </uint64-field>
+</rappor-metric>
+
+<rappor-metric
+    name="Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin2"
+    type="UMA_RAPPOR_TYPE">
+  <owner>nzolghadr@chromium.org</owner>
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    The eTLD+1 of the website visited, along with  the time between initial
+    creation of a wheel event and the start of the frame swap on the GPU service
+    caused by the generated ScrollUpdate gesture event if that ScrollUpdate is
+    the first such event in a given scroll gesture event sequence. If no swap
+    was induced by the event, no recording is made.
+  </summary>
+  <string-field name="Domain">
+    <summary>
+      The domain+registry of the URL.
+    </summary>
+  </string-field>
+  <uint64-field name="Latency">
+    <summary>
+      Latency in microseconds representing first
+      Wheel.TimeToScrollUpdateSwapBegin.
+    </summary>
+  </uint64-field>
+</rappor-metric>
+
+<rappor-metric
     name="Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2"
     type="UMA_RAPPOR_TYPE">
   <owner>nzolghadr@chromium.org</owner>
   <owner>tdresser@chromium.org</owner>
   <summary>
-    The eTLD+1 of the website visited, along with a the time between the initial
+    The eTLD+1 of the website visited, along with the time between the initial
     creation of a touch event and the start of the frame swap on the GPU service
-    caused by the generated ScrollUpdate gesture event if that ScrollUpdate is
-    the first such event in a given scroll gesture event sequence. If no swap
-    was induced by the event, no recording is made.
+    caused by the generated ScrollUpdate gesture event. If no swap was induced
+    by the event, no recording is made.
   </summary>
   <string-field name="Domain">
     <summary>
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py
index b6d82cf..282b747 100644
--- a/tools/perf/benchmarks/speedometer.py
+++ b/tools/perf/benchmarks/speedometer.py
@@ -116,3 +116,13 @@
   @classmethod
   def Name(cls):
     return 'speedometer-ignition'
+
+
+class SpeedometerTurbo(Speedometer):
+  def SetExtraBrowserOptions(self, options):
+    super(SpeedometerTurbo, self).SetExtraBrowserOptions(options)
+    v8_helper.EnableTurbo(options)
+
+  @classmethod
+  def Name(cls):
+    return 'speedometer-turbo'
diff --git a/tools/perf/benchmarks/v8_helper.py b/tools/perf/benchmarks/v8_helper.py
index 19ecab3..6eb8d4d 100644
--- a/tools/perf/benchmarks/v8_helper.py
+++ b/tools/perf/benchmarks/v8_helper.py
@@ -9,6 +9,11 @@
   AppendJSFlags(options, '--ignition-staging')
 
 
+def EnableTurbo(options):
+  AppendJSFlags(options, '--ignition-staging')
+  AppendJSFlags(options, '--turbo')
+
+
 def AppendJSFlags(options, js_flags):
   existing_js_flags = ''
   # There should be only one occurence of --js-flags in the browser flags. When
diff --git a/tools/perf/generate_perf_json.py b/tools/perf/generate_perf_json.py
index 29783f0..4ccd4f4 100755
--- a/tools/perf/generate_perf_json.py
+++ b/tools/perf/generate_perf_json.py
@@ -244,7 +244,9 @@
        'device_ids': [
            'build132-m1', 'build133-m1',
            'build134-m1', 'build135-m1', 'build136-m1'
-          ]
+          ],
+       'perf_tests': [
+         ('media_perftests', 2)]
       }
     ])
   waterfall = add_tester(
@@ -259,7 +261,8 @@
           ],
        'perf_tests': [
          ('load_library_perf_tests', 2),
-         ('performance_browser_tests', 2)]
+         ('performance_browser_tests', 2),
+         ('media_perftests', 3)]
       }
     ])
   waterfall = add_tester(
@@ -275,7 +278,8 @@
           ],
        'perf_tests': [
          ('load_library_perf_tests', 2),
-         ('performance_browser_tests', 2)]
+         ('performance_browser_tests', 2),
+         ('media_perftests', 3)]
       }
     ])
   waterfall = add_tester(
@@ -308,7 +312,8 @@
        'perf_tests': [
          ('angle_perftests', 2),
          ('load_library_perf_tests', 2),
-         ('performance_browser_tests', 2)]
+         ('performance_browser_tests', 2),
+         ('media_perftests', 3)]
       }
     ])
   waterfall = add_tester(
@@ -338,7 +343,8 @@
        'perf_tests': [
          ('angle_perftests', 2),
          ('load_library_perf_tests', 2),
-         ('performance_browser_tests', 2)]
+         ('performance_browser_tests', 2),
+         ('media_perftests', 3)]
       }
     ])
 
@@ -352,7 +358,9 @@
        'device_ids': [
            'build102-b1', 'build103-b1',
            'build104-b1', 'build105-b1', 'build106-b1'
-          ]
+          ],
+       'perf_tests': [
+         ('media_perftests', 2)]
       }
     ])
   waterfall = add_tester(
@@ -419,7 +427,8 @@
        'perf_tests': [
          ('cc_perftests', 2),
          ('load_library_perf_tests', 2),
-         ('tracing_perftests', 2)]
+         ('tracing_perftests', 2),
+         ('media_perftests', 3)]
       }
     ])
 
diff --git a/tools/perf/page_sets/system_health/background_stories.py b/tools/perf/page_sets/system_health/background_stories.py
index 31df314..a0eb93f 100644
--- a/tools/perf/page_sets/system_health/background_stories.py
+++ b/tools/perf/page_sets/system_health/background_stories.py
@@ -78,7 +78,7 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
 
 
-@decorators.Disabled('android')  # crbug.com.com/664505
+@decorators.Disabled('android')  # crbug.com/676336
 class BackgroundGmailMobileStory(LoadGmailMobileStory):
   NAME = 'background:tools:gmail'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 04862001..a5c8cca 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -146,6 +146,7 @@
   ITEM_SELECTOR = '.athing .title > a'
 
 
+@decorators.Disabled('android')  # crbug.com/676315
 class NytimesMobileStory(_NewsBrowsingStory):
   """The third top website in http://www.alexa.com/topsites/category/News"""
   NAME = 'browse:news:nytimes'
@@ -198,7 +199,7 @@
 
 
 @decorators.Disabled('win',  # crbug.com/662971
-                     'mac')  # crbug.com/664661, crbug.com/663025
+                     'mac')  # crbug.com/663025
 class TwitterDesktopStory(_NewsBrowsingStory):
   NAME = 'browse:social:twitter'
   URL = 'https://www.twitter.com/nasa'
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index eb0c54e..2b0af06 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -309,7 +309,7 @@
         'document.getElementById("loading").style.display === "none"')
 
 
-@decorators.Disabled('android')  # crbug.com.com/664505
+@decorators.Disabled('android')  # crbug.com/657433
 class LoadGmailMobileStory(_LoadGmailBaseStory):
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
 
diff --git a/tools/perf/page_sets/system_health/long_running_stories.py b/tools/perf/page_sets/system_health/long_running_stories.py
index 2eed0ef..916e75c0 100644
--- a/tools/perf/page_sets/system_health/long_running_stories.py
+++ b/tools/perf/page_sets/system_health/long_running_stories.py
@@ -83,7 +83,7 @@
         'document.getElementById("loading").style.display === "none"')
 
 
-@decorators.Disabled('android')  # crbug.com.com/664505
+@decorators.Disabled('android')  # crbug.com/657433
 class LongRunningGmailMobileForegroundStory(_LongRunningGmailMobileBase):
   NAME = 'long_running:tools:gmail-foreground'
 
@@ -92,8 +92,8 @@
   NAME = 'long_running:tools:gmail-foreground'
 
 
-@decorators.Disabled('android-webview'  # Weview does not have tabs.
-                  , 'android')  # crbug.com/664505
+@decorators.Disabled('android-webview',  # Weview does not have tabs.
+                     'android')  # crbug.com/657433
 class LongRunningGmailMobileBackgroundStory(_LongRunningGmailMobileBase):
   BACKGROUND = True
   NAME = 'long_running:tools:gmail-background'
diff --git a/tools/perf/page_sets/system_health/searching_stories.py b/tools/perf/page_sets/system_health/searching_stories.py
index 898aacf0..329a0cb 100644
--- a/tools/perf/page_sets/system_health/searching_stories.py
+++ b/tools/perf/page_sets/system_health/searching_stories.py
@@ -27,18 +27,7 @@
     # Scroll to the Wikipedia result.
     action_runner.WaitForElement(selector=self._RESULT_SELECTOR)
     action_runner.Wait(1)
-    # TODO(petrcermak): Turn this into a proper Telemetry API (ScrollToElement).
-    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
-    result_visible_expression = ('''
-        (function() {
-          var resultElem = document.querySelector(\'%s\');
-          var boundingRect = resultElem.getBoundingClientRect();
-          return boundingRect.bottom >= window.innerHeight
-        })()''' % self._RESULT_SELECTOR)
-    while action_runner.EvaluateJavaScript(result_visible_expression):
-      action_runner.RepeatableBrowserDrivenScroll(y_scroll_distance_ratio=0.75,
-                                                  prevent_fling=False)
-      action_runner.Wait(0.2)
+    action_runner.ScrollPageToElement(selector=self._RESULT_SELECTOR)
 
     # Click on the Wikipedia result.
     action_runner.Wait(1)
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc
index 79110f33..770fda4 100644
--- a/ui/android/window_android.cc
+++ b/ui/android/window_android.cc
@@ -33,6 +33,7 @@
   explicit WindowBeginFrameSource(WindowAndroid* window)
       : window_(window),
         observer_count_(0),
+        next_sequence_number_(cc::BeginFrameArgs::kStartingFrameNumber),
         in_on_vsync_(false) {}
   ~WindowBeginFrameSource() override {}
 
@@ -43,14 +44,14 @@
                       size_t remaining_frames) override {}
   bool IsThrottled() const override { return true; }
 
-  void OnVSync(base::TimeTicks frame_time,
-               base::TimeDelta vsync_period);
+  void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period);
 
  private:
   WindowAndroid* const window_;
   base::ObserverList<cc::BeginFrameObserver> observers_;
   int observer_count_;
   cc::BeginFrameArgs last_begin_frame_args_;
+  uint64_t next_sequence_number_;
   bool in_on_vsync_;
 };
 
@@ -70,6 +71,9 @@
     cc::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
     if (!last_args.IsValid() ||
         last_args.frame_time < last_begin_frame_args_.frame_time) {
+      DCHECK(last_args.sequence_number <
+                 last_begin_frame_args_.sequence_number ||
+             last_args.source_id != last_begin_frame_args_.source_id);
       last_begin_frame_args_.type = cc::BeginFrameArgs::MISSED;
       // TODO(crbug.com/602485): A deadline doesn't make too much sense
       // for a missed BeginFrame (the intention rather is 'immediately'),
@@ -98,10 +102,11 @@
     base::TimeDelta vsync_period) {
   // frame time is in the past, so give the next vsync period as the deadline.
   base::TimeTicks deadline = frame_time + vsync_period;
-  last_begin_frame_args_ =
-      cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
-                                 vsync_period, cc::BeginFrameArgs::NORMAL);
+  last_begin_frame_args_ = cc::BeginFrameArgs::Create(
+      BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time,
+      deadline, vsync_period, cc::BeginFrameArgs::NORMAL);
   DCHECK(last_begin_frame_args_.IsValid());
+  next_sequence_number_++;
 
   // We support adding/removing observers during observer iteration through
   // base::ObserverList. We also prevent observers that are added during
diff --git a/ui/aura/mus/property_converter.cc b/ui/aura/mus/property_converter.cc
index 875ec3d..b02fca4 100644
--- a/ui/aura/mus/property_converter.cc
+++ b/ui/aura/mus/property_converter.cc
@@ -32,6 +32,8 @@
   // Add known aura properties with associated mus properties.
   RegisterProperty(client::kAlwaysOnTopKey,
                    ui::mojom::WindowManager::kAlwaysOnTop_Property);
+  RegisterProperty(client::kAppIconKey,
+                   ui::mojom::WindowManager::kAppIcon_Property);
   RegisterProperty(client::kAppIdKey,
                    ui::mojom::WindowManager::kAppID_Property);
   RegisterProperty(client::kNameKey, ui::mojom::WindowManager::kName_Property);
@@ -45,6 +47,8 @@
                    ui::mojom::WindowManager::kShowState_Property);
   RegisterProperty(client::kTitleKey,
                    ui::mojom::WindowManager::kWindowTitle_Property);
+  RegisterProperty(client::kWindowIconKey,
+                   ui::mojom::WindowManager::kWindowIcon_Property);
 }
 
 PropertyConverter::~PropertyConverter() {}
diff --git a/ui/base/clipboard/clipboard_mac.h b/ui/base/clipboard/clipboard_mac.h
index 2e264cb..76701ff0 100644
--- a/ui/base/clipboard/clipboard_mac.h
+++ b/ui/base/clipboard/clipboard_mac.h
@@ -21,6 +21,8 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina);
   FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina);
+  FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage);
+  FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage);
   friend class Clipboard;
 
   ClipboardMac();
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index fcc4a3a..ce4bada 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -324,17 +324,27 @@
   } @catch (id exception) {
   }
 
-  if (image.get()) {
-    if ([[image representations] count] == 1u) {
-      NSImageRep* rep = [[image representations] objectAtIndex:0];
+  if (!image)
+    return SkBitmap();
+  if ([[image representations] count] == 0u)
+    return SkBitmap();
+
+  // This logic prevents loss of pixels from retina images, where size != pixel
+  // size. In an ideal world, the concept of "retina-ness" would be plumbed all
+  // the way through to the web, but the clipboard API doesn't support the
+  // additional metainformation.
+  if ([[image representations] count] == 1u) {
+    NSImageRep* rep = [[image representations] objectAtIndex:0];
+    NSInteger width = [rep pixelsWide];
+    NSInteger height = [rep pixelsHigh];
+    if (width != 0 && height != 0) {
       return skia::NSImageRepToSkBitmapWithColorSpace(
-          rep, NSMakeSize([rep pixelsWide], [rep pixelsHigh]),
-          /*is_opaque=*/false, base::mac::GetSystemColorSpace());
+          rep, NSMakeSize(width, height), /*is_opaque=*/false,
+          base::mac::GetSystemColorSpace());
     }
-    return skia::NSImageToSkBitmapWithColorSpace(
-        image.get(), /*is_opaque=*/false, base::mac::GetSystemColorSpace());
   }
-  return SkBitmap();
+  return skia::NSImageToSkBitmapWithColorSpace(
+      image.get(), /*is_opaque=*/false, base::mac::GetSystemColorSpace());
 }
 
 SkBitmap ClipboardMac::ReadImage(ClipboardType type) const {
diff --git a/ui/base/clipboard/clipboard_mac_unittest.mm b/ui/base/clipboard/clipboard_mac_unittest.mm
index 6a5577cf5..475db13 100644
--- a/ui/base/clipboard/clipboard_mac_unittest.mm
+++ b/ui/base/clipboard/clipboard_mac_unittest.mm
@@ -4,6 +4,8 @@
 
 #import "ui/base/clipboard/clipboard_mac.h"
 
+#import <AppKit/AppKit.h>
+
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/free_deleter.h"
@@ -13,6 +15,16 @@
 #include "ui/base/clipboard/clipboard_types.h"
 #include "ui/base/clipboard/clipboard_util_mac.h"
 
+@interface RedView : NSView
+@end
+@implementation RedView
+- (void)drawRect:(NSRect)dirtyRect {
+  [[NSColor redColor] setFill];
+  NSRectFill(dirtyRect);
+  [super drawRect:dirtyRect];
+}
+@end
+
 namespace ui {
 
 class ClipboardMacTest : public PlatformTest {
@@ -78,4 +90,41 @@
   EXPECT_EQ(height, bitmap.height());
 }
 
+TEST_F(ClipboardMacTest, EmptyImage) {
+  base::scoped_nsobject<NSImage> image([[NSImage alloc] init]);
+  scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard;
+  [pasteboard->get() writeObjects:@[ image.get() ]];
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  ui::ClipboardMac* clipboard_mac = static_cast<ui::ClipboardMac*>(clipboard);
+
+  SkBitmap bitmap = clipboard_mac->ReadImage(ui::CLIPBOARD_TYPE_COPY_PASTE,
+                                             pasteboard->get());
+  EXPECT_EQ(0, bitmap.width());
+  EXPECT_EQ(0, bitmap.height());
+}
+
+TEST_F(ClipboardMacTest, PDFImage) {
+  int32_t width = 99;
+  int32_t height = 101;
+  NSRect frame = NSMakeRect(0, 0, width, height);
+
+  // This seems like a round-about way of getting a NSPDFImageRep to shove into
+  // an NSPasteboard. However, I haven't found any other way of generating a
+  // "PDF" image that makes NSPasteboard happy.
+  base::scoped_nsobject<NSView> v([[RedView alloc] initWithFrame:frame]);
+  NSData* data = [v dataWithPDFInsideRect:frame];
+
+  scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard;
+  [pasteboard->get() setData:data forType:NSPasteboardTypePDF];
+
+  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+  ui::ClipboardMac* clipboard_mac = static_cast<ui::ClipboardMac*>(clipboard);
+
+  SkBitmap bitmap = clipboard_mac->ReadImage(ui::CLIPBOARD_TYPE_COPY_PASTE,
+                                             pasteboard->get());
+  EXPECT_EQ(width, bitmap.width());
+  EXPECT_EQ(height, bitmap.height());
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 28e335d..abb3815 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -6,7 +6,7 @@
 import("//ui/ozone/ozone.gni")
 
 declare_args() {
-  use_drm_atomic = false
+  use_drm_atomic = true
 }
 
 visibility = [ "//ui/ozone/*" ]
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
index 0401223..6a3b1b6 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -290,15 +290,10 @@
   overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList());
   ui::OverlayPlaneList plane_list =
       overlay_validator_->PrepareBuffersForPageFlip(plane_list_);
-#if defined(USE_DRM_ATOMIC)
-  EXPECT_EQ(DRM_FORMAT_UYVY,
-            plane_list.back().buffer->GetFramebufferPixelFormat());
-#else
-  // If Atomic support is disabled, ensure we choose DRM_FORMAT_XRGB8888 as the
-  // optimal format even if other packed formats are supported by Primary.
+  // TODO(dcastagna): If Atomic support is enabled, a packed format (UYVY) might
+  // be the optimal one and should be preferred.
   EXPECT_EQ(DRM_FORMAT_XRGB8888,
             plane_list.back().buffer->GetFramebufferPixelFormat());
-#endif
 }
 
 TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat) {
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 1b91c7ec..9b62a695 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -90,7 +90,8 @@
 void DrmThread::Init() {
   bool use_atomic = false;
 #if defined(USE_DRM_ATOMIC)
-  use_atomic = true;
+  use_atomic = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableDrmAtomic);
 #endif
 
   device_manager_.reset(
diff --git a/ui/ozone/public/ozone_switches.cc b/ui/ozone/public/ozone_switches.cc
index 0a9b3ab32..f7a772a5 100644
--- a/ui/ozone/public/ozone_switches.cc
+++ b/ui/ozone/public/ozone_switches.cc
@@ -12,4 +12,7 @@
 // Specify location for image dumps.
 const char kOzoneDumpFile[] = "ozone-dump-file";
 
+// Try to enable drm atomic. This works only with drm platform.
+const char kEnableDrmAtomic[] = "enable-drm-atomic";
+
 }  // namespace switches
diff --git a/ui/ozone/public/ozone_switches.h b/ui/ozone/public/ozone_switches.h
index b062e67..e53912bf 100644
--- a/ui/ozone/public/ozone_switches.h
+++ b/ui/ozone/public/ozone_switches.h
@@ -14,6 +14,8 @@
 
 OZONE_BASE_EXPORT extern const char kOzoneDumpFile[];
 
+OZONE_BASE_EXPORT extern const char kEnableDrmAtomic[];
+
 }  // namespace switches
 
 #endif  // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm
index b5d67a1..15ac350 100644
--- a/ui/views/cocoa/bridged_content_view.mm
+++ b/ui/views/cocoa/bridged_content_view.mm
@@ -1412,11 +1412,15 @@
   composition.underlines.push_back(ui::CompositionUnderline(
       0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT));
   textInputClient_->SetCompositionText(composition);
+  keyDownEvent_ = nil;  // Handled.
 }
 
 - (void)unmarkText {
-  if (textInputClient_)
-    textInputClient_->ConfirmCompositionText();
+  if (!textInputClient_)
+    return;
+
+  textInputClient_->ConfirmCompositionText();
+  keyDownEvent_ = nil;  // Handled.
 }
 
 - (NSArray*)validAttributesForMarkedText {
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index b27b90e4..5a20610c 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -654,31 +654,36 @@
     EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */));
   }
 
-  void PressLeftMouseButton(int extra_flags) {
-    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_,
-                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
-                         ui::EF_LEFT_MOUSE_BUTTON | extra_flags);
-    textfield_->OnMousePressed(click);
+  void PressMouseButton(ui::EventFlags mouse_button_flags, int extra_flags) {
+    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_,
+                         ui::EventTimeForNow(), mouse_button_flags,
+                         mouse_button_flags | extra_flags);
+    textfield_->OnMousePressed(press);
   }
 
-  void PressLeftMouseButton() {
-    PressLeftMouseButton(0);
-  }
-
-  void ReleaseLeftMouseButton() {
+  void ReleaseMouseButton(ui::EventFlags mouse_button_flags) {
     ui::MouseEvent release(ui::ET_MOUSE_RELEASED, mouse_position_,
                            mouse_position_, ui::EventTimeForNow(),
-                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                           mouse_button_flags, mouse_button_flags);
     textfield_->OnMouseReleased(release);
   }
 
-  void ClickLeftMouseButton(int extra_flags) {
+  void PressLeftMouseButton(int extra_flags = 0) {
+    PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON, extra_flags);
+  }
+
+  void ReleaseLeftMouseButton() {
+    ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON);
+  }
+
+  void ClickLeftMouseButton(int extra_flags = 0) {
     PressLeftMouseButton(extra_flags);
     ReleaseLeftMouseButton();
   }
 
-  void ClickLeftMouseButton() {
-    ClickLeftMouseButton(0);
+  void ClickRightMouseButton() {
+    PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON, 0);
+    ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON);
   }
 
   void DragMouseTo(const gfx::Point& where) {
@@ -1453,6 +1458,27 @@
   EXPECT_STR_EQ("hello", textfield_->GetSelectedText());
 }
 
+// Tests text selection behavior on a right click.
+TEST_F(TextfieldTest, SelectWordOnRightClick) {
+  InitTextfield();
+  textfield_->SetText(ASCIIToUTF16("hello world"));
+
+  // Verify right clicking within the selection does not alter the selection.
+  textfield_->SelectRange(gfx::Range(1, 5));
+  EXPECT_STR_EQ("ello", textfield_->GetSelectedText());
+  MoveMouseTo(gfx::Point(GetCursorPositionX(3), 0));
+  ClickRightMouseButton();
+  EXPECT_STR_EQ("ello", textfield_->GetSelectedText());
+
+  // Verify right clicking outside the selection, selects the word under the
+  // cursor on platforms where this is expected.
+  MoveMouseTo(gfx::Point(GetCursorPositionX(8), 0));
+  const char* expected_right_click =
+      PlatformStyle::kSelectWordOnRightClick ? "world" : "ello";
+  ClickRightMouseButton();
+  EXPECT_STR_EQ(expected_right_click, textfield_->GetSelectedText());
+}
+
 TEST_F(TextfieldTest, DragToSelect) {
   InitTextfield();
   textfield_->SetText(ASCIIToUTF16("hello world"));
diff --git a/ui/views/selection_controller.cc b/ui/views/selection_controller.cc
index 38ba6ed..4692c9c 100644
--- a/ui/views/selection_controller.cc
+++ b/ui/views/selection_controller.cc
@@ -56,10 +56,7 @@
         break;
       case 1:
         // Select the word at the click location on a double click.
-        delegate_->OnBeforePointerAction();
-        render_text->MoveCursorTo(event.location(), false);
-        render_text->SelectWord();
-        delegate_->OnAfterPointerAction(false, true);
+        SelectWord(event.location());
         double_click_word_ = render_text->selection();
         break;
       case 2:
@@ -73,6 +70,15 @@
     }
   }
 
+  // TODO(crbug.com/676296): Right clicking an unfocused text view should select
+  // all its text on Mac.
+  const bool select_word_on_right_click =
+      event.IsOnlyRightMouseButton() &&
+      PlatformStyle::kSelectWordOnRightClick &&
+      !render_text->IsPointInSelection(event.location());
+  if (select_word_on_right_click)
+    SelectWord(event.location());
+
   if (handles_selection_clipboard_ && event.IsOnlyMiddleMouseButton()) {
     if (render_text->IsPointInSelection(event.location())) {
       delegate_->OnBeforePointerAction();
@@ -171,6 +177,15 @@
   }
 }
 
+void SelectionController::SelectWord(const gfx::Point& point) {
+  gfx::RenderText* render_text = GetRenderText();
+  DCHECK(render_text);
+  delegate_->OnBeforePointerAction();
+  render_text->MoveCursorTo(point, false);
+  render_text->SelectWord();
+  delegate_->OnAfterPointerAction(false, true);
+}
+
 gfx::RenderText* SelectionController::GetRenderText() {
   return delegate_->GetRenderTextForSelectionController();
 }
diff --git a/ui/views/selection_controller.h b/ui/views/selection_controller.h
index ed9ffab..8a410e9 100644
--- a/ui/views/selection_controller.h
+++ b/ui/views/selection_controller.h
@@ -53,6 +53,9 @@
   // Tracks the mouse clicks for single/double/triple clicks.
   void TrackMouseClicks(const ui::MouseEvent& event);
 
+  // Selects the word at the given |point|.
+  void SelectWord(const gfx::Point& point);
+
   // Returns the associated render text instance via the |delegate_|.
   gfx::RenderText* GetRenderText();
 
diff --git a/ui/views/style/platform_style.cc b/ui/views/style/platform_style.cc
index c7cbab1..dc20e0f8 100644
--- a/ui/views/style/platform_style.cc
+++ b/ui/views/style/platform_style.cc
@@ -42,6 +42,7 @@
 const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = true;
 const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = true;
 const bool PlatformStyle::kTextDragVerticallyDragsToEnd = false;
+const bool PlatformStyle::kSelectWordOnRightClick = false;
 const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction =
     CustomButton::NOTIFY_ON_RELEASE;
 const bool PlatformStyle::kTreeViewHasFocusRing = false;
diff --git a/ui/views/style/platform_style.h b/ui/views/style/platform_style.h
index 181c180..dfa434f13 100644
--- a/ui/views/style/platform_style.h
+++ b/ui/views/style/platform_style.h
@@ -44,6 +44,9 @@
   // the left or right end of the text from the cursor, respectively.
   static const bool kTextDragVerticallyDragsToEnd;
 
+  // Whether right clicking on text, selects the word under cursor.
+  static const bool kSelectWordOnRightClick;
+
   // The menu button's action to show the menu.
   static const CustomButton::NotifyAction kMenuNotifyActivationAction;
 
diff --git a/ui/views/style/platform_style_mac.mm b/ui/views/style/platform_style_mac.mm
index 0b983bb..97caa954 100644
--- a/ui/views/style/platform_style_mac.mm
+++ b/ui/views/style/platform_style_mac.mm
@@ -24,6 +24,7 @@
 const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = false;
 const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = false;
 const bool PlatformStyle::kTextDragVerticallyDragsToEnd = true;
+const bool PlatformStyle::kSelectWordOnRightClick = true;
 const bool PlatformStyle::kTreeViewHasFocusRing = true;
 const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true;
 const bool PlatformStyle::kUseRipples = false;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 132cfa7..cfc4f0d 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1027,12 +1027,15 @@
     // and does not change.
     return;
   }
-
-  SetUseNativeFrame(new_type == Widget::FRAME_TYPE_FORCE_NATIVE);
-  // Replace the frame and layout the contents. Even though we don't have a
-  // swapable glass frame like on Windows, we still replace the frame because
-  // the button assets don't update otherwise.
-  native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame();
+  // Avoid mutating |View::children_| while possibly iterating over them.
+  // See View::PropagateNativeThemeChanged().
+  // TODO(varkha, sadrul): Investigate removing this (and instead expecting the
+  // NonClientView::UpdateFrame() to update the frame-view when theme changes,
+  // like all other views).
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&DesktopWindowTreeHostX11::DelayedChangeFrameType,
+                            weak_factory_.GetWeakPtr(),
+                            new_type));
 }
 
 void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
@@ -2279,6 +2282,14 @@
   delayed_resize_task_.Cancel();
 }
 
+void DesktopWindowTreeHostX11::DelayedChangeFrameType(Widget::FrameType type) {
+  SetUseNativeFrame(type == Widget::FRAME_TYPE_FORCE_NATIVE);
+  // Replace the frame and layout the contents. Even though we don't have a
+  // swappable glass frame like on Windows, we still replace the frame because
+  // the button assets don't update otherwise.
+  native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame();
+}
+
 gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInPixels() const {
   std::vector<int> value;
   if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index 7b78e7e..34a7e75 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -271,6 +271,7 @@
   uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
 
   void DelayedResize(const gfx::Size& size_in_pixels);
+  void DelayedChangeFrameType(Widget::FrameType new_type);
 
   gfx::Rect GetWorkAreaBoundsInPixels() const;
   gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const;