diff --git a/DEPS b/DEPS
index 41b2a7e..e7f9cd5 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': '370c2b304a35297d36fcee91e3b6ac516091434d',
+  'skia_revision': 'e4d45bf5ba413e5a26d4f6c24886bbd104b14885',
   # 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': '0f043477b1c8e56d871237ef0d13fe3296c15b94',
+  'v8_revision': 'c5e8f7690bbb9e99851f08fdf45930da4cd62dab',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '1d95c68f912102dfda5d6e9ad7ca79411cda5900',
+  'pdfium_revision': '4f34c64914be17966f2d91591921dec635582061',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # 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': '4cafad75c8313d6c14b47544574a20ecc49c253f',
+  'catapult_revision': 'b0384fe60f101e921782716d3a7a1881de5b37ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -154,7 +154,7 @@
     Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '42bc671f47b122fad36db5eccbc06868afdf7862',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'ae18d60831126f902d778541495194380ff77fd8',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'dfa798fe694702b43a3debc3290761f22b1acaf8',
 
   'src/third_party/hunspell_dictionaries':
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + 'dc6e7c25bf47cbfb466e0701fd2728b4a12e79d5',
@@ -241,7 +241,7 @@
     Var('chromium_git') + '/external/github.com/open-source-parsers/jsoncpp.git' + '@' + 'f572e8e42e22cfcf5ab0aea26574f408943edfa4', # from svn 248
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '8edd2286fdf9df2b9da806bda7ed262492f95921',  # from r1655
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '7bffe5e1c54bc22daebd57003735e61693719ac6',  # from r1655
 
   'src/third_party/smhasher/src':
     Var('chromium_git') + '/external/smhasher.git' + '@' + 'e87738e57558e0ec472b2fc3a643b838e5b6e88f',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 9944a1eb..46dffa9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -306,9 +306,6 @@
     "shelf/shelf_layout_manager_observer.h",
     "shelf/shelf_locking_manager.cc",
     "shelf/shelf_locking_manager.h",
-    "shelf/shelf_model.cc",
-    "shelf/shelf_model.h",
-    "shelf/shelf_model_observer.h",
     "shelf/shelf_observer.h",
     "shelf/shelf_tooltip_manager.cc",
     "shelf/shelf_tooltip_manager.h",
@@ -1192,7 +1189,6 @@
     "shelf/shelf_controller_unittest.cc",
     "shelf/shelf_layout_manager_unittest.cc",
     "shelf/shelf_locking_manager_unittest.cc",
-    "shelf/shelf_model_unittest.cc",
     "shelf/shelf_tooltip_manager_unittest.cc",
     "shelf/shelf_unittest.cc",
     "shelf/shelf_view_unittest.cc",
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 065ad0f..f89748b 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -7,10 +7,10 @@
 #include "ash/metrics/desktop_task_switch_metric_recorder.h"
 #include "ash/metrics/pointer_metrics_recorder.h"
 #include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
diff --git a/ash/metrics/user_metrics_recorder_unittest.cc b/ash/metrics/user_metrics_recorder_unittest.cc
index d688800..1074339f4 100644
--- a/ash/metrics/user_metrics_recorder_unittest.cc
+++ b/ash/metrics/user_metrics_recorder_unittest.cc
@@ -8,8 +8,8 @@
 
 #include "ash/login_status.h"
 #include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/session/session_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index b8947df..054dd2e 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -20,6 +20,9 @@
     "shelf_item.h",
     "shelf_item_delegate.cc",
     "shelf_item_delegate.h",
+    "shelf_model.cc",
+    "shelf_model.h",
+    "shelf_model_observer.h",
     "shelf_types.cc",
     "shelf_types.h",
     "shell_window_ids.cc",
@@ -55,6 +58,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "shelf_model_unittest.cc",
     "shelf_struct_traits_unittest.cc",
   ]
 
diff --git a/ash/shelf/shelf_model.cc b/ash/public/cpp/shelf_model.cc
similarity index 96%
rename from ash/shelf/shelf_model.cc
rename to ash/public/cpp/shelf_model.cc
index 3aa6ade5..73b8b42 100644
--- a/ash/shelf/shelf_model.cc
+++ b/ash/public/cpp/shelf_model.cc
@@ -2,14 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/shelf/shelf_model.h"
+#include "ash/public/cpp/shelf_model.h"
 
 #include <algorithm>
 
 #include "ash/public/cpp/shelf_item_delegate.h"
-#include "ash/shelf/shelf_model_observer.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ui/base/l10n/l10n_util.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 
 namespace ash {
 
@@ -48,11 +46,11 @@
 const char kAppListId[] = "jlfapfmkapbjlfbpjedlinehodkccjee";
 
 ShelfModel::ShelfModel() {
-  // Add the app list item.
+  // Add the app list item; its title and delegate are set in ShelfController.
+  // This avoids an ash/public dep on ash/strings, and a Chrome-side delegate.
   ShelfItem item;
   item.type = TYPE_APP_LIST;
   item.id = ShelfID(kAppListId);
-  item.title = l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE);
   const int index = Add(item);
   DCHECK_EQ(0, index);
 }
diff --git a/ash/shelf/shelf_model.h b/ash/public/cpp/shelf_model.h
similarity index 92%
rename from ash/shelf/shelf_model.h
rename to ash/public/cpp/shelf_model.h
index fca50d8..530f1d3 100644
--- a/ash/shelf/shelf_model.h
+++ b/ash/public/cpp/shelf_model.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_SHELF_MODEL_H_
-#define ASH_SHELF_SHELF_MODEL_H_
+#ifndef ASH_PUBLIC_CPP_SHELF_MODEL_H_
+#define ASH_PUBLIC_CPP_SHELF_MODEL_H_
 
 #include <map>
 #include <memory>
 
-#include "ash/ash_export.h"
+#include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/interfaces/shelf.mojom.h"
 #include "base/macros.h"
@@ -21,11 +21,10 @@
 
 // An id for the AppList item, which is added in the ShelfModel constructor.
 // Generated as crx_file::id_util::GenerateId("org.chromium.applist")
-ASH_EXPORT extern const char kAppListId[];
+ASH_PUBLIC_EXPORT extern const char kAppListId[];
 
 // Model used for shelf items. Owns ShelfItemDelegates but does not create them.
-// TODO(msw): Move this to ash/public/cpp and use ASH_PUBLIC_EXPORT.
-class ASH_EXPORT ShelfModel {
+class ASH_PUBLIC_EXPORT ShelfModel {
  public:
   ShelfModel();
   ~ShelfModel();
@@ -110,4 +109,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SHELF_SHELF_MODEL_H_
+#endif  // ASH_PUBLIC_CPP_SHELF_MODEL_H_
diff --git a/ash/shelf/shelf_model_observer.h b/ash/public/cpp/shelf_model_observer.h
similarity index 79%
rename from ash/shelf/shelf_model_observer.h
rename to ash/public/cpp/shelf_model_observer.h
index ca8f4054..581d9f1a 100644
--- a/ash/shelf/shelf_model_observer.h
+++ b/ash/public/cpp/shelf_model_observer.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_SHELF_MODEL_OBSERVER_H_
-#define ASH_SHELF_SHELF_MODEL_OBSERVER_H_
+#ifndef ASH_PUBLIC_CPP_SHELF_MODEL_OBSERVER_H_
+#define ASH_PUBLIC_CPP_SHELF_MODEL_OBSERVER_H_
 
-#include "ash/ash_export.h"
+#include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/cpp/shelf_types.h"
 
 namespace ash {
@@ -13,8 +13,8 @@
 struct ShelfItem;
 class ShelfItemDelegate;
 
-// TODO(msw): Move this to ash/public/cpp and use ASH_PUBLIC_EXPORT.
-class ASH_EXPORT ShelfModelObserver {
+// An observer interface for shelf item and delegate changes in ShelfModel.
+class ASH_PUBLIC_EXPORT ShelfModelObserver {
  public:
   // Invoked after an item has been added to the model.
   virtual void ShelfItemAdded(int index) = 0;
@@ -40,4 +40,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SHELF_SHELF_MODEL_OBSERVER_H_
+#endif  // ASH_PUBLIC_CPP_SHELF_MODEL_OBSERVER_H_
diff --git a/ash/shelf/shelf_model_unittest.cc b/ash/public/cpp/shelf_model_unittest.cc
similarity index 98%
rename from ash/shelf/shelf_model_unittest.cc
rename to ash/public/cpp/shelf_model_unittest.cc
index e731c8c..d1fb6ae 100644
--- a/ash/shelf/shelf_model_unittest.cc
+++ b/ash/public/cpp/shelf_model_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/shelf/shelf_model.h"
+#include "ash/public/cpp/shelf_model.h"
 
 #include <set>
 #include <string>
 
-#include "ash/shelf/shelf_model_observer.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc
index 3783580..a8cc9bf 100644
--- a/ash/shelf/app_list_shelf_item_delegate.cc
+++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/shelf/shelf_model.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shell.h"
 
 namespace ash {
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index ec1b0bf..b57c791 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -6,13 +6,13 @@
 
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf_bezel_event_handler.h"
 #include "ash/shelf/shelf_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
diff --git a/ash/shelf/shelf_controller.cc b/ash/shelf/shelf_controller.cc
index 4b51989..16dcbb5 100644
--- a/ash/shelf/shelf_controller.cc
+++ b/ash/shelf/shelf_controller.cc
@@ -11,8 +11,10 @@
 #include "ash/shelf/app_list_shelf_item_delegate.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "base/auto_reset.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -32,9 +34,14 @@
 }  // namespace
 
 ShelfController::ShelfController() {
-  // Create an AppListShelfItemDelegate for the app list item.
+  // Set the delegate and title string for the app list item.
   model_.SetShelfItemDelegate(ShelfID(kAppListId),
                               base::MakeUnique<AppListShelfItemDelegate>());
+  DCHECK_EQ(0, model_.ItemIndexByID(ShelfID(kAppListId)));
+  ShelfItem item = model_.items()[0];
+  item.title = l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE);
+  model_.Set(0, item);
+
   model_.AddObserver(this);
 }
 
diff --git a/ash/shelf/shelf_controller.h b/ash/shelf/shelf_controller.h
index 5b73a388..a55a9f0 100644
--- a/ash/shelf/shelf_controller.h
+++ b/ash/shelf/shelf_controller.h
@@ -6,10 +6,10 @@
 #define ASH_SHELF_SHELF_CONTROLLER_H_
 
 #include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/interfaces/shelf.mojom.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shelf/shelf_model_observer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 
diff --git a/ash/shelf/shelf_controller_unittest.cc b/ash/shelf/shelf_controller_unittest.cc
index 4926ab05..879cf3c 100644
--- a/ash/shelf/shelf_controller_unittest.cc
+++ b/ash/shelf/shelf_controller_unittest.cc
@@ -8,10 +8,10 @@
 #include <string>
 
 #include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/public/interfaces/shelf.mojom.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shelf/shelf_model_observer.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index 67cbe2e3..62501f4 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -5,9 +5,9 @@
 #include "ash/shelf/shelf_tooltip_manager.h"
 
 #include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/shelf/shelf_unittest.cc b/ash/shelf/shelf_unittest.cc
index 233c73b..73c5d8a7 100644
--- a/ash/shelf/shelf_unittest.cc
+++ b/ash/shelf/shelf_unittest.cc
@@ -4,9 +4,9 @@
 
 #include <utility>
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_button.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 75efc9b4..7304bd8 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -11,6 +11,7 @@
 #include "ash/drag_drop/drag_image_view.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/app_list_button.h"
@@ -21,7 +22,6 @@
 #include "ash/shelf/shelf_application_menu_model.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 42e45ca..8c11c9e6 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -10,10 +10,10 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/public/interfaces/shelf.mojom.h"
 #include "ash/shelf/ink_drop_button_listener.h"
 #include "ash/shelf/shelf_button_pressed_metric_tracker.h"
-#include "ash/shelf/shelf_model_observer.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 53668a77..6b40462 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/app_list_button.h"
@@ -20,7 +21,6 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
 #include "ash/shelf/shelf_widget.h"
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
index 88e000a..d52a149 100644
--- a/ash/shelf/shelf_window_watcher.cc
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -7,10 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_window_watcher_item_delegate.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index c10440b..17cac6b 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -6,9 +6,9 @@
 
 #include <utility>
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
index ad97f4e0..b02928f 100644
--- a/ash/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -6,11 +6,11 @@
 
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 1c72b47..b9d4e76 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -51,12 +51,12 @@
 #include "ash/new_window_controller.h"
 #include "ash/palette_delegate.h"
 #include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_window_watcher.h"
 #include "ash/shell_delegate.h"
 #include "ash/shell_init_params.h"
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 5f4f95f0a2..0cc998ba 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -294,8 +294,6 @@
     return std::vector<views::View*>();
   }
 
-  views::View* GetSearchAnswerWebView() override { return nullptr; }
-
   void CustomLauncherPageAnimationChanged(double progress) override {}
 
   void CustomLauncherPagePopSubpage() override {}
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 2fb4b4c..b3b4cda 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -7,9 +7,9 @@
 #include <utility>
 
 #include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell/window_watcher_shelf_item_delegate.h"
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index ea8d244f..ac448de 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -334,12 +334,9 @@
   scroll_content_ = new ScrollContentsView();
   scroller_ = new views::ScrollView;
   scroller_->SetContents(scroll_content_);
-  // Make the |scroller_| have a layer to clip |scroll_content_|'s children.
   // TODO(varkha): Make the sticky rows work with EnableViewPortLayer().
-  scroller_->SetPaintToLayer();
-  scroller_->SetBackground(views::CreateThemedSolidBackground(
-      scroller_, ui::NativeTheme::kColorId_BubbleBackground));
-  scroller_->layer()->SetMasksToBounds(true);
+  scroller_->SetBackgroundThemeColorId(
+      ui::NativeTheme::kColorId_BubbleBackground);
 
   AddChildView(scroller_);
   box_layout_->SetFlexForView(scroller_, 1);
diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc
index 35abf3d7..7601397b 100644
--- a/ash/test/shelf_view_test_api.cc
+++ b/ash/test/shelf_view_test_api.cc
@@ -4,10 +4,10 @@
 
 #include "ash/test/shelf_view_test_api.h"
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 30b2b1b0..6b81b1c 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/panels/panel_layout_manager.h"
 
 #include "ash/public/cpp/config.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
@@ -12,7 +13,6 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index 750f8fb..0478525 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc
index 7a2d59c..422297e 100644
--- a/ash/wm/system_gesture_event_filter_unittest.cc
+++ b/ash/wm/system_gesture_event_filter_unittest.cc
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "ash/accelerators/accelerator_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/test/ash_test_base.h"
diff --git a/base/android/build_info.h b/base/android/build_info.h
index e7dab56..3c4c86c1 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -27,7 +27,8 @@
   SDK_VERSION_LOLLIPOP = 21,
   SDK_VERSION_LOLLIPOP_MR1 = 22,
   SDK_VERSION_MARSHMALLOW = 23,
-  SDK_VERSION_NOUGAT = 24
+  SDK_VERSION_NOUGAT = 24,
+  SDK_VERSION_NOUGAT_MR1 = 25
 };
 
 // BuildInfo is a singleton class that stores android build and device
diff --git a/base/environment_unittest.cc b/base/environment_unittest.cc
index ef264cf..80e3aa6e 100644
--- a/base/environment_unittest.cc
+++ b/base/environment_unittest.cc
@@ -14,11 +14,22 @@
 
 namespace base {
 
+namespace {
+
+// Fuchsia doesn't set PATH, Windows doesn't set PWD. (Fuchsia may eventually
+// set PATH and then this can be removed again.)
+#if defined(OS_FUCHSIA)
+constexpr char kValidEnvironmentVariable[] = "PWD";
+#else
+constexpr char kValidEnvironmentVariable[] = "PATH";
+#endif
+
+}  // namespace
+
 TEST_F(EnvironmentTest, GetVar) {
-  // Every setup should have non-empty PATH...
   std::unique_ptr<Environment> env(Environment::Create());
   std::string env_value;
-  EXPECT_TRUE(env->GetVar("PATH", &env_value));
+  EXPECT_TRUE(env->GetVar(kValidEnvironmentVariable, &env_value));
   EXPECT_NE(env_value, "");
 }
 
@@ -51,9 +62,8 @@
 }
 
 TEST_F(EnvironmentTest, HasVar) {
-  // Every setup should have PATH...
   std::unique_ptr<Environment> env(Environment::Create());
-  EXPECT_TRUE(env->HasVar("PATH"));
+  EXPECT_TRUE(env->HasVar(kValidEnvironmentVariable));
 }
 
 TEST_F(EnvironmentTest, SetVar) {
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index ebf43152..4a7ca94 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -405,12 +405,18 @@
       // ordering bug. This aims to catch those early.
       DCHECK(shutdown_event_);
       if (shutdown_event_->IsSignaled()) {
+#if DCHECK_IS_ON()
+// clang-format off
         // TODO(robliao): http://crbug.com/698140. Since the service thread
         // doesn't stop processing its own tasks at shutdown, we may still
         // attempt to post a BLOCK_SHUTDOWN task in response to a
-        // FileDescriptorWatcher.
-#if DCHECK_IS_ON()
-        DCHECK(IsPostingBlockShutdownTaskAfterShutdownAllowed());
+        // FileDescriptorWatcher. Same is true for FilePathWatcher
+        // (http://crbug.com/728235). Until it's possible for such services to
+        // post to non-BLOCK_SHUTDOWN sequences which are themselves funneled to
+        // the main execution sequence (a future plan for the post_task.h API),
+        // this DCHECK will be flaky and must be disabled.
+        // DCHECK(IsPostingBlockShutdownTaskAfterShutdownAllowed());
+// clang-format on
 #endif
         state_->DecrementNumTasksBlockingShutdown();
         return false;
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
index 2fbfff27..d7c7b60a 100644
--- a/base/task_scheduler/task_tracker_unittest.cc
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -412,11 +412,7 @@
   std::unique_ptr<Task> task(CreateTask(GetParam()));
 
   // |task_tracker_| shouldn't allow a task to be posted after shutdown.
-  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
-    EXPECT_DCHECK_DEATH({ tracker_.WillPostTask(task.get()); });
-  } else {
-    EXPECT_FALSE(tracker_.WillPostTask(task.get()));
-  }
+  EXPECT_FALSE(tracker_.WillPostTask(task.get()));
 }
 
 // Verify that BLOCK_SHUTDOWN and SKIP_ON_SHUTDOWN tasks can
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 17f4d963..829bc43 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -139,7 +139,7 @@
   # This is intended to be used for instrumented builds.
   use_custom_libcxx =
       (is_asan && is_linux && !is_chromeos) || is_tsan || is_msan || is_ubsan ||
-      is_ubsan_security || use_libfuzzer || use_afl
+      is_ubsan_security || (use_libfuzzer && !is_mac) || use_afl
 
   # Enable -fsanitize-coverage.
   use_sanitizer_coverage =
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc
index 4632648002..b9ddce2 100644
--- a/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -121,6 +121,7 @@
     clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
+    host_impl_.active_tree()->UpdateScrollbarGeometries();
 
     scrollbar_controller_ = ScrollbarAnimationController::
         CreateScrollbarAnimationControllerAuraOverlay(
@@ -1261,6 +1262,8 @@
     clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
+    DCHECK(host_impl_.active_tree()->ScrollbarGeometriesNeedUpdate());
+    host_impl_.active_tree()->UpdateScrollbarGeometries();
 
     scrollbar_controller_ =
         ScrollbarAnimationController::CreateScrollbarAnimationControllerAndroid(
diff --git a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
index 0faa8d3..8df33897 100644
--- a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
@@ -96,6 +96,7 @@
     scrollbar_layer_->test_properties()->opacity_can_animate = true;
     clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
+    host_impl_.active_tree()->UpdateScrollbarGeometries();
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
     scrollbar_controller_ = SingleScrollbarAnimationControllerThinning::Create(
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 57cb1b4..be6cf82 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -94,7 +94,6 @@
 LayerImpl::~LayerImpl() {
   DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_);
 
-  layer_tree_impl_->UnregisterScrollLayer(this);
   layer_tree_impl_->UnregisterLayer(this);
 
   layer_tree_impl_->RemoveFromElementMap(this);
@@ -275,10 +274,12 @@
 void LayerImpl::SetScrollClipLayer(int scroll_clip_layer_id) {
   if (scroll_clip_layer_id_ == scroll_clip_layer_id)
     return;
-
-  layer_tree_impl()->UnregisterScrollLayer(this);
   scroll_clip_layer_id_ = scroll_clip_layer_id;
+
   layer_tree_impl()->RegisterScrollLayer(this);
+
+  // The scrolling bounds are determined from the scroll clip layer's bounds.
+  layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
 }
 
 LayerImpl* LayerImpl::scroll_clip_layer() const {
@@ -516,7 +517,8 @@
 
   bounds_ = bounds;
 
-  layer_tree_impl()->DidUpdateScrollState(id());
+  // Scrollbar positions depend on scrolling bounds and scroll clip bounds.
+  layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
 
   NoteLayerPropertyChanged();
 }
@@ -543,7 +545,9 @@
       NOTREACHED();
   }
 
-  layer_tree_impl()->DidUpdateScrollState(id());
+  // Viewport scrollbar positions are determined using the viewport bounds
+  // delta.
+  layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
 
   if (masks_to_bounds()) {
     // If layer is clipping, then update the clip node using the new bounds.
@@ -986,4 +990,26 @@
   return GetPropertyTrees()->transform_tree;
 }
 
+bool LayerImpl::HasValidPropertyTreeIndices() const {
+  // TODO(crbug.com/726423): LayerImpls should never have invalid PropertyTree
+  // indices.
+  const bool has_valid_transform_node =
+      !!GetTransformTree().Node(transform_tree_index());
+  DCHECK(has_valid_transform_node);
+
+  const bool has_valid_effect_node =
+      !!GetEffectTree().Node(effect_tree_index());
+  DCHECK(has_valid_effect_node);
+
+  const bool has_valid_clip_node = !!GetClipTree().Node(clip_tree_index());
+  DCHECK(has_valid_clip_node);
+
+  const bool has_valid_scroll_node =
+      !!GetScrollTree().Node(scroll_tree_index());
+  DCHECK(has_valid_scroll_node);
+
+  return has_valid_transform_node && has_valid_effect_node &&
+         has_valid_clip_node && has_valid_scroll_node;
+}
+
 }  // namespace cc
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 35a155de..cd791ca 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -448,6 +448,8 @@
   }
   bool raster_even_if_not_drawn() const { return raster_even_if_not_drawn_; }
 
+  bool HasValidPropertyTreeIndices() const;
+
  protected:
   LayerImpl(LayerTreeImpl* layer_impl,
             int id,
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index d2744ee..b12d3a3 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -141,6 +141,7 @@
 
   root->test_properties()->force_render_surface = true;
   root->SetMasksToBounds(true);
+  root->SetScrollClipLayer(root_clip->id());
   root->layer_tree_impl()->ResetAllChangeTracking();
 
   root->test_properties()->AddChild(
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 2dd6d0b7..3932b0e 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -57,6 +57,9 @@
   scrollbar_layer_impl->set_thumb_ui_resource_id(thumb_uid);
   scrollbar_layer_impl->set_thumb_opacity(thumb_opacity);
 
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
diff --git a/cc/layers/scrollbar_layer_impl_base.cc b/cc/layers/scrollbar_layer_impl_base.cc
index 5070c84..7c51ea9e 100644
--- a/cc/layers/scrollbar_layer_impl_base.cc
+++ b/cc/layers/scrollbar_layer_impl_base.cc
@@ -59,6 +59,26 @@
   return true;
 }
 
+float ScrollbarLayerImplBase::current_pos() const {
+  DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
+  return current_pos_;
+}
+
+float ScrollbarLayerImplBase::clip_layer_length() const {
+  DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
+  return clip_layer_length_;
+}
+
+float ScrollbarLayerImplBase::scroll_layer_length() const {
+  DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
+  return scroll_layer_length_;
+}
+
+float ScrollbarLayerImplBase::vertical_adjust() const {
+  DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
+  return vertical_adjust_;
+}
+
 bool ScrollbarLayerImplBase::CanScrollOrientation() const {
   // TODO(pdr): Refactor this to not depend on layers by using the associated
   // scroll node's user_scrollable values.
@@ -67,44 +87,47 @@
   if (!scroll_layer)
     return false;
 
+  // Ensure the clip_layer_length and scroll_layer_length values are up-to-date.
+  // TODO(pdr): Instead of using the clip and scroll layer lengths which require
+  // an update, refactor to use the scroll tree (ScrollTree::MaxScrollOffset
+  // as in LayerTreeHostImpl::TryScroll).
+  layer_tree_impl()->UpdateScrollbarGeometries();
+
   return scroll_layer->user_scrollable(orientation()) &&
-         // Ensure clip_layer_length_ smaller than scroll_layer_length_ not
-         // caused by floating error.
-         !MathUtil::IsFloatNearlyTheSame(clip_layer_length_,
-                                         scroll_layer_length_) &&
-         clip_layer_length_ < scroll_layer_length_;
+         // Ensure clip_layer_length is smaller than scroll_layer_length, not
+         // including small deltas due to floating point error.
+         !MathUtil::IsFloatNearlyTheSame(clip_layer_length(),
+                                         scroll_layer_length()) &&
+         clip_layer_length() < scroll_layer_length();
 }
 
-bool ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
+void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
   if (vertical_adjust_ == vertical_adjust)
-    return false;
+    return;
   vertical_adjust_ = vertical_adjust;
   NoteLayerPropertyChanged();
-  return true;
 }
 
-bool ScrollbarLayerImplBase::SetClipLayerLength(float clip_layer_length) {
+void ScrollbarLayerImplBase::SetClipLayerLength(float clip_layer_length) {
   if (clip_layer_length_ == clip_layer_length)
-    return false;
+    return;
   clip_layer_length_ = clip_layer_length;
   NoteLayerPropertyChanged();
-  return true;
 }
 
-bool ScrollbarLayerImplBase::SetScrollLayerLength(float scroll_layer_length) {
+void ScrollbarLayerImplBase::SetScrollLayerLength(float scroll_layer_length) {
   if (scroll_layer_length_ == scroll_layer_length)
-    return false;
+    return;
   scroll_layer_length_ = scroll_layer_length;
   NoteLayerPropertyChanged();
-  return true;
+  return;
 }
 
-bool ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
+void ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
   if (thumb_thickness_scale_factor_ == factor)
-    return false;
+    return;
   thumb_thickness_scale_factor_ = factor;
   NoteLayerPropertyChanged();
-  return true;
 }
 
 gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale(
@@ -173,10 +196,10 @@
   float track_length = TrackLength();
   int thumb_length = ThumbLength();
   int thumb_thickness = ThumbThickness();
-  float maximum = scroll_layer_length_ - clip_layer_length_;
+  float maximum = scroll_layer_length() - clip_layer_length();
 
   // With the length known, we can compute the thumb's position.
-  float clamped_current_pos = std::min(std::max(current_pos_, 0.f), maximum);
+  float clamped_current_pos = std::min(std::max(current_pos(), 0.f), maximum);
 
   int thumb_offset = TrackStart();
   if (maximum > 0) {
diff --git a/cc/layers/scrollbar_layer_impl_base.h b/cc/layers/scrollbar_layer_impl_base.h
index 7becd72..644557a 100644
--- a/cc/layers/scrollbar_layer_impl_base.h
+++ b/cc/layers/scrollbar_layer_impl_base.h
@@ -21,15 +21,17 @@
   ElementId scroll_element_id() const { return scroll_element_id_; }
   void SetScrollElementId(ElementId scroll_element_id);
 
-  float current_pos() const { return current_pos_; }
+  // The following setters should be called when updating scrollbar geometries
+  // (see: LayerTreeImpl::UpdateScrollbarGeometries).
   bool SetCurrentPos(float current_pos);
-  bool SetClipLayerLength(float clip_layer_length);
-  bool SetScrollLayerLength(float scroll_layer_length);
-  bool SetVerticalAdjust(float vertical_adjust);
+  void SetClipLayerLength(float clip_layer_length);
+  void SetScrollLayerLength(float scroll_layer_length);
+  void SetVerticalAdjust(float vertical_adjust);
 
-  float clip_layer_length() const { return clip_layer_length_; }
-  float scroll_layer_length() const { return scroll_layer_length_; }
-  float vertical_adjust() const { return vertical_adjust_; }
+  float current_pos() const;
+  float clip_layer_length() const;
+  float scroll_layer_length() const;
+  float vertical_adjust() const;
 
   bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; }
   void set_is_overlay_scrollbar(bool is_overlay) {
@@ -53,7 +55,7 @@
   float thumb_thickness_scale_factor() {
     return thumb_thickness_scale_factor_;
   }
-  bool SetThumbThicknessScaleFactor(float thumb_thickness_scale_factor);
+  void SetThumbThicknessScaleFactor(float thumb_thickness_scale_factor);
 
   virtual int ThumbThickness() const = 0;
 
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index ff5d38a5..f33fab5 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -315,6 +315,7 @@
       static_cast<PaintedScrollbarLayerImpl*>(
           layer_impl_tree_root->layer_tree_impl()->LayerById(
               scrollbar_layer->id()));
+  layer_impl_tree_root->layer_tree_impl()->UpdateScrollbarGeometries();
 
   EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos());
   EXPECT_EQ(30, cc_scrollbar_layer->scroll_layer_length() -
@@ -327,6 +328,7 @@
 
   layer_tree_host_->UpdateLayers();
   layer_impl_tree_root = layer_tree_host_->CommitAndCreateLayerImplTree();
+  layer_impl_tree_root->layer_tree_impl()->UpdateScrollbarGeometries();
 
   EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos());
   EXPECT_EQ(300, cc_scrollbar_layer->scroll_layer_length() -
@@ -335,6 +337,7 @@
   LayerImpl* scroll_layer_impl =
       layer_impl_tree_root->layer_tree_impl()->LayerById(scroll_layer->id());
   scroll_layer_impl->ScrollBy(gfx::Vector2d(12, 34));
+  layer_impl_tree_root->layer_tree_impl()->UpdateScrollbarGeometries();
 
   EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos());
   EXPECT_EQ(300, cc_scrollbar_layer->scroll_layer_length() -
@@ -346,6 +349,7 @@
     scrollbar_layer->UpdateInternalContentScale();                           \
     scrollbar_layer->UpdateThumbAndTrackGeometry();                          \
     root_clip_layer_impl = layer_tree_host_->CommitAndCreateLayerImplTree(); \
+    root_clip_layer_impl->layer_tree_impl()->UpdateScrollbarGeometries();    \
     scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(          \
         root_clip_layer_impl->layer_tree_impl()->LayerById(                  \
             scrollbar_layer->id()));                                         \
@@ -538,6 +542,9 @@
   scrollbar_layer_impl->SetClipLayerLength(200 / 3.f);
   scrollbar_layer_impl->SetScrollLayerLength(100 + 200 / 3.f);
 
+  DCHECK(layer_tree_host_->active_tree()->ScrollbarGeometriesNeedUpdate());
+  layer_tree_host_->active_tree()->UpdateScrollbarGeometries();
+
   // Thickness should be overridden to 3.
   {
     std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
@@ -622,6 +629,9 @@
   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
   scrollbar_layer_impl->SetCurrentPos(4.f);
 
+  DCHECK(layer_tree_host_->active_tree()->ScrollbarGeometriesNeedUpdate());
+  layer_tree_host_->active_tree()->UpdateScrollbarGeometries();
+
   {
     std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
 
@@ -782,6 +792,9 @@
   clip_layer->SetBounds(gfx::Size(980, 980));
   scroll_layer->SetBounds(gfx::Size(980, 980));
 
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
   impl.CalcDrawProps(viewport_size);
 
   // Fake clip layer length to scrollbar to mock rounding error.
@@ -795,6 +808,57 @@
   EXPECT_TRUE(scrollbar_layer->CanScrollOrientation());
 }
 
+TEST_F(ScrollbarLayerTest, LayerChangesAffectingScrollbarGeometries) {
+  gfx::Size viewport_size(980, 980);
+
+  LayerTestCommon::LayerImplTest impl;
+
+  LayerImpl* clip_layer = impl.AddChildToRoot<LayerImpl>();
+  LayerImpl* scroll_layer = impl.AddChild<LayerImpl>(clip_layer);
+  scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
+
+  // Make clip_layer the inner viewport container layer. This ensures the later
+  // call to |SetViewportBoundsDelta| will be on a viewport layer.
+  LayerTreeImpl::ViewportLayerIds viewport_ids;
+  viewport_ids.inner_viewport_container = clip_layer->id();
+  impl.host_impl()->active_tree()->SetViewportLayersFromIds(viewport_ids);
+
+  const int kTrackStart = 0;
+  const int kThumbThickness = 10;
+  const bool kIsLeftSideVerticalScrollbar = false;
+  const bool kIsOverlayScrollbar = false;
+  SolidColorScrollbarLayerImpl* scrollbar_layer =
+      impl.AddChild<SolidColorScrollbarLayerImpl>(
+          scroll_layer, HORIZONTAL, kThumbThickness, kTrackStart,
+          kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
+  scrollbar_layer->SetScrollElementId(scroll_layer->element_id());
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
+  scroll_layer->SetScrollClipLayer(clip_layer->id());
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
+  clip_layer->SetBounds(gfx::Size(900, 900));
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
+  scroll_layer->SetBounds(gfx::Size(980, 980));
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
+  clip_layer->SetViewportBoundsDelta(gfx::Vector2dF(1, 2));
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
+  // Not changing the current value should not require an update.
+  scroll_layer->SetScrollClipLayer(clip_layer->id());
+  clip_layer->SetBounds(gfx::Size(900, 900));
+  scroll_layer->SetBounds(gfx::Size(980, 980));
+  clip_layer->SetViewportBoundsDelta(gfx::Vector2dF(1, 2));
+  DCHECK(!impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+}
+
 class ScrollbarLayerSolidColorThumbTest : public testing::Test {
  public:
   ScrollbarLayerSolidColorThumbTest() {
@@ -841,6 +905,8 @@
   horizontal_scrollbar_layer_->SetClipLayerLength(5.f);
   horizontal_scrollbar_layer_->SetScrollLayerLength(15.f);
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
+  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
   EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
 
   // The thumb's length should never be less than its thickness.
@@ -852,6 +918,8 @@
 
 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
+  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
 
   horizontal_scrollbar_layer_->SetCurrentPos(0.f);
   horizontal_scrollbar_layer_->SetClipLayerLength(12.f);
@@ -881,6 +949,9 @@
   layers[0]->SetBounds(gfx::Size(100, 3));
   layers[1]->SetBounds(gfx::Size(3, 100));
 
+  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
+
   EXPECT_EQ(gfx::Rect(20, 0, 20, 3),
             horizontal_scrollbar_layer_->ComputeThumbQuadRect());
   EXPECT_EQ(gfx::Rect(0, 20, 3, 20),
diff --git a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
index a2b24303..4cff576 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
@@ -39,6 +39,9 @@
   // SolidColorScrollbarLayers construct with opacity = 0.f, so override.
   scrollbar_layer_impl->test_properties()->opacity = 1.f;
 
+  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
diff --git a/cc/test/fake_external_begin_frame_source.cc b/cc/test/fake_external_begin_frame_source.cc
index 8398dcfc..a2a0b3d 100644
--- a/cc/test/fake_external_begin_frame_source.cc
+++ b/cc/test/fake_external_begin_frame_source.cc
@@ -18,11 +18,11 @@
     : tick_automatically_(tick_automatically),
       milliseconds_per_frame_(1000.0 / refresh_rate),
       weak_ptr_factory_(this) {
-  DetachFromThread();
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 FakeExternalBeginFrameSource::~FakeExternalBeginFrameSource() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void FakeExternalBeginFrameSource::SetPaused(bool paused) {
@@ -87,7 +87,7 @@
 
 void FakeExternalBeginFrameSource::TestOnBeginFrame(
     const BeginFrameArgs& args) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   current_args_ = args;
   std::set<BeginFrameObserver*> observers(observers_);
   for (auto* obs : observers)
diff --git a/cc/test/fake_external_begin_frame_source.h b/cc/test/fake_external_begin_frame_source.h
index 0a03149c..9ab8f6e 100644
--- a/cc/test/fake_external_begin_frame_source.h
+++ b/cc/test/fake_external_begin_frame_source.h
@@ -9,7 +9,7 @@
 
 #include "base/cancelable_callback.h"
 #include "base/memory/weak_ptr.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
 #include "cc/output/begin_frame_args.h"
 #include "cc/scheduler/begin_frame_source.h"
 
@@ -19,9 +19,7 @@
 
 namespace cc {
 
-class FakeExternalBeginFrameSource
-    : public BeginFrameSource,
-      public NON_EXPORTED_BASE(base::NonThreadSafe) {
+class FakeExternalBeginFrameSource : public BeginFrameSource {
  public:
   class Client {
    public:
@@ -63,6 +61,9 @@
   uint64_t next_begin_frame_number_ = BeginFrameArgs::kStartingFrameNumber;
   std::set<BeginFrameObserver*> observers_;
   base::CancelableCallback<void(const BeginFrameArgs&)> begin_frame_task_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<FakeExternalBeginFrameSource> weak_ptr_factory_;
 };
 
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc
index 96ce656..2a81e0e0 100644
--- a/cc/test/fake_layer_tree_host.cc
+++ b/cc/test/fake_layer_tree_host.cc
@@ -69,6 +69,8 @@
 void FakeLayerTreeHost::SetNeedsCommit() { needs_commit_ = true; }
 
 LayerImpl* FakeLayerTreeHost::CommitAndCreateLayerImplTree() {
+  // TODO(pdr): Update the LayerTreeImpl lifecycle states here so lifecycle
+  // violations can be caught.
   TreeSynchronizer::SynchronizeTrees(root_layer(), active_tree());
   active_tree()->SetPropertyTrees(property_trees());
   TreeSynchronizer::PushLayerProperties(root_layer()->layer_tree_host(),
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index bd0375f..db42684 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -458,15 +458,6 @@
       transform_tree.Node(layer->transform_tree_index());
   const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
 
-  DCHECK(effect_node);
-  DCHECK(transform_node);
-  // TODO(crbug.com/726423) : This is a workaround for crbug.com/726225 to
-  // avoid crashing when there is no effect or transform node. Effect node and
-  // transform node should always exist here and this workaround should be
-  // removed.
-  if (!transform_node || !effect_node)
-    return true;
-
   if (effect_node->has_render_surface && effect_node->subtree_has_copy_request)
     return false;
 
@@ -857,7 +848,7 @@
     // TODO(crbug.com/726423) : This is a workaround for crbug.com/725851 to
     // avoid crashing when layer_impl is nullptr. This workaround should be
     // removed as layer_impl should not be nullptr here.
-    if (!layer_impl)
+    if (!layer_impl || !layer_impl->HasValidPropertyTreeIndices())
       continue;
     if (!IsRootLayer(layer_impl) &&
         LayerShouldBeSkippedForDrawPropertiesComputation(
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 679fb2ae..3dfcae27 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1090,6 +1090,8 @@
   if (input_handler_client_)
     input_handler_client_->ReconcileElasticOverscrollAndRootScroll();
 
+  active_tree_->UpdateScrollbarGeometries();
+
   if (const char* client_name = GetClientNameForMetrics()) {
     size_t total_memory = 0;
     for (const PictureLayerImpl* layer : active_tree()->picture_layers())
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 4d12ca12..1d526b4 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -3226,6 +3226,8 @@
 
   horiz_scrollbar->SetScrollElementId(root_scroll->element_id());
 
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
+
   EXPECT_EQ(300, horiz_scrollbar->clip_layer_length());
 }
 
@@ -4344,6 +4346,99 @@
   host_impl_->ScrollEnd(EndState().get());
 }
 
+// Tests that browser controls affect the position of horizontal scrollbars.
+TEST_F(LayerTreeHostImplBrowserControlsTest,
+       HidingBrowserControlsAdjustsScrollbarPosition) {
+  SetupBrowserControlsAndScrollLayerWithVirtualViewport(
+      gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
+
+  LayerTreeImpl* active_tree = host_impl_->active_tree();
+
+  // Create a horizontal scrollbar.
+  const int scrollbar_id = 23;
+  gfx::Size scrollbar_size(gfx::Size(50, 15));
+  std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar =
+      SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
+                                           scrollbar_id, HORIZONTAL, 3, 20,
+                                           false, true);
+  scrollbar->SetScrollElementId(
+      host_impl_->OuterViewportScrollLayer()->element_id());
+  scrollbar->SetDrawsContent(true);
+  scrollbar->SetBounds(scrollbar_size);
+  scrollbar->SetTouchEventHandlerRegion(gfx::Rect(scrollbar_size));
+  scrollbar->SetCurrentPos(0);
+  scrollbar->SetPosition(gfx::PointF(0, 35));
+  host_impl_->active_tree()
+      ->InnerViewportContainerLayer()
+      ->test_properties()
+      ->AddChild(std::move(scrollbar));
+  host_impl_->active_tree()->BuildPropertyTreesForTesting();
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
+
+  DrawFrame();
+
+  LayerImpl* inner_container = active_tree->InnerViewportContainerLayer();
+  LayerImpl* outer_container = active_tree->OuterViewportContainerLayer();
+  SolidColorScrollbarLayerImpl* scrollbar_layer =
+      static_cast<SolidColorScrollbarLayerImpl*>(
+          active_tree->LayerById(scrollbar_id));
+
+  // The browser controls should start off showing so the viewport should be
+  // shrunk.
+  EXPECT_EQ(gfx::Size(50, 50), inner_container->bounds());
+  EXPECT_EQ(gfx::Size(50, 50), outer_container->bounds());
+  EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize());
+  EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
+  EXPECT_EQ(gfx::Rect(20, 0, 10, 3), scrollbar_layer->ComputeThumbQuadRect());
+
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point()).get(),
+                              InputHandler::TOUCHSCREEN)
+                .thread);
+
+  host_impl_->browser_controls_manager()->ScrollBegin();
+
+  // Hide the browser controls by a bit, the scrollable size should increase but
+  // the actual content bounds shouldn't.
+  {
+    host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 25.f));
+    host_impl_->active_tree()->UpdateScrollbarGeometries();
+    ASSERT_EQ(gfx::Size(50, 75), inner_container->bounds());
+    ASSERT_EQ(gfx::Size(50, 75), outer_container->bounds());
+    EXPECT_EQ(gfx::SizeF(50, 75), active_tree->ScrollableSize());
+    EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
+    EXPECT_EQ(gfx::Rect(20, 25, 10, 3),
+              scrollbar_layer->ComputeThumbQuadRect());
+  }
+
+  // Fully hide the browser controls.
+  {
+    host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 25.f));
+    host_impl_->active_tree()->UpdateScrollbarGeometries();
+    ASSERT_EQ(gfx::Size(50, 100), inner_container->bounds());
+    ASSERT_EQ(gfx::Size(50, 100), outer_container->bounds());
+    EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
+    EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
+    EXPECT_EQ(gfx::Rect(20, 50, 10, 3),
+              scrollbar_layer->ComputeThumbQuadRect());
+  }
+
+  // Additional scrolling shouldn't have any effect.
+  {
+    host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 25.f));
+    ASSERT_EQ(gfx::Size(50, 100), inner_container->bounds());
+    ASSERT_EQ(gfx::Size(50, 100), outer_container->bounds());
+    EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize());
+    EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
+    EXPECT_EQ(gfx::Rect(20, 50, 10, 3),
+              scrollbar_layer->ComputeThumbQuadRect());
+  }
+
+  host_impl_->browser_controls_manager()->ScrollEnd();
+  host_impl_->ScrollEnd(EndState().get());
+}
+
 TEST_F(LayerTreeHostImplBrowserControlsTest,
        ScrollBrowserControlsByFractionalAmount) {
   SetupBrowserControlsAndScrollLayerWithVirtualViewport(
@@ -12121,6 +12216,7 @@
       ->AddChild(std::move(scrollbar_1));
 
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
   host_impl_->active_tree()->DidBecomeActive();
 
   DrawFrame();
@@ -12229,6 +12325,7 @@
   root_scroll->test_properties()->AddChild(std::move(child_clip));
 
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
+  host_impl_->active_tree()->UpdateScrollbarGeometries();
   host_impl_->active_tree()->DidBecomeActive();
 
   ScrollbarAnimationController* scrollbar_2_animation_controller =
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 999efbf..8f1158a4 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -89,6 +89,7 @@
       layers_(new OwnedLayerImplList),
       viewport_size_invalid_(false),
       needs_update_draw_properties_(true),
+      scrollbar_geometries_need_update_(false),
       needs_full_tree_sync_(true),
       needs_surface_ids_sync_(false),
       next_activation_forces_redraw_(false),
@@ -169,7 +170,9 @@
 }
 
 void LayerTreeImpl::DidUpdateScrollOffset(int layer_id) {
-  DidUpdateScrollState(layer_id);
+  // Scrollbar positions depend on the current scroll offset.
+  SetScrollbarGeometriesNeedUpdate();
+
   DCHECK(lifecycle().AllowsPropertyTreeAccess());
   TransformTree& transform_tree = property_trees()->transform_tree;
   ScrollTree& scroll_tree = property_trees()->scroll_tree;
@@ -204,85 +207,82 @@
     layer_tree_host_impl_->pending_tree()->DidUpdateScrollOffset(layer_id);
 }
 
-void LayerTreeImpl::DidUpdateScrollState(int layer_id) {
+void LayerTreeImpl::UpdateScrollbarGeometries() {
   if (!IsActiveTree())
     return;
 
   DCHECK(lifecycle().AllowsPropertyTreeAccess());
 
-  // The scroll_clip_layer Layer properties should be up-to-date.
-  // TODO(pdr): This DCHECK fails on existing tests but should be enabled.
-  // DCHECK(lifecycle().AllowsLayerPropertyAccess());
+  // Layer properties such as bounds should be up-to-date.
+  DCHECK(lifecycle().AllowsLayerPropertyAccess());
 
-  if (layer_id == Layer::INVALID_ID)
+  if (!scrollbar_geometries_need_update_)
     return;
 
-  int scroll_layer_id, clip_layer_id;
-  if (IsViewportLayerId(layer_id)) {
-    // For scrollbar purposes, a change to any of the four viewport layers
-    // should affect the scrollbars tied to the outermost layers, which express
-    // the sum of the entire viewport.
-    scroll_layer_id = viewport_layer_ids_.outer_viewport_scroll;
-    clip_layer_id = viewport_layer_ids_.inner_viewport_container;
-  } else {
-    // If the clip layer id was passed in, then look up the scroll layer, or
-    // vice versa.
-    auto i = clip_scroll_map_.find(layer_id);
-    if (i != clip_scroll_map_.end()) {
-      scroll_layer_id = i->second;
-      clip_layer_id = layer_id;
-    } else {
-      scroll_layer_id = layer_id;
-      clip_layer_id = LayerById(scroll_layer_id)->scroll_clip_layer_id();
+  for (auto& pair : element_id_to_scrollbar_layer_ids_) {
+    ElementId scrolling_element_id = pair.first;
+
+    LayerImpl* scrolling_layer = LayerByElementId(scrolling_element_id);
+    if (!scrolling_layer)
+      continue;
+
+    LayerImpl* bounds_layer = scrolling_layer->scroll_clip_layer();
+    if (!bounds_layer)
+      continue;
+
+    // The viewport scrollbars are special because all viewport layers can
+    // affect the scrollbars. Begin with the inner container and outer scroll.
+    bool is_viewport_scrollbar = scrolling_layer->is_viewport_layer_type();
+    if (is_viewport_scrollbar) {
+      bounds_layer = InnerViewportContainerLayer();
+      scrolling_layer = OuterViewportScrollLayer();
+      if (!bounds_layer || !scrolling_layer)
+        continue;
     }
-  }
-  UpdateScrollbars(scroll_layer_id, clip_layer_id);
-}
 
-void LayerTreeImpl::UpdateScrollbars(int scroll_layer_id, int clip_layer_id) {
-  DCHECK(IsActiveTree());
+    gfx::SizeF scrolling_size(scrolling_layer->BoundsForScrolling());
+    if (scrolling_size.IsEmpty())
+      continue;
 
-  LayerImpl* clip_layer = LayerById(clip_layer_id);
-  LayerImpl* scroll_layer = LayerById(scroll_layer_id);
+    gfx::ScrollOffset current_offset = scrolling_layer->CurrentScrollOffset();
+    gfx::SizeF bounds_size(bounds_layer->BoundsForScrolling());
+    float viewport_vertical_adjust = 0;
 
-  if (!clip_layer || !scroll_layer)
-    return;
+    // Viewport adjustments to account for the inner scroll layer and outer
+    // container layer.
+    if (is_viewport_scrollbar) {
+      // The offset is the combination of the outer and inner scroll offsets.
+      current_offset += InnerViewportScrollLayer()->CurrentScrollOffset();
 
-  gfx::SizeF clip_size(clip_layer->BoundsForScrolling());
-  gfx::SizeF scroll_size(scroll_layer->BoundsForScrolling());
+      // The bounds are set using the intersection of the two viewport clip
+      // layers, adjusted for the page scale factor.
+      if (auto* outer_viewport_container = OuterViewportContainerLayer())
+        bounds_size.SetToMin(outer_viewport_container->BoundsForScrolling());
+      bounds_size.Scale(1 / current_page_scale_factor());
 
-  if (scroll_size.IsEmpty())
-    return;
-
-  gfx::ScrollOffset current_offset = scroll_layer->CurrentScrollOffset();
-  float viewport_vertical_adjust = 0;
-
-  bool is_viewport_scrollbar = scroll_layer->is_viewport_layer_type();
-  if (is_viewport_scrollbar) {
-    current_offset += InnerViewportScrollLayer()->CurrentScrollOffset();
-    if (OuterViewportContainerLayer())
-      clip_size.SetToMin(OuterViewportContainerLayer()->BoundsForScrolling());
-    clip_size.Scale(1 / current_page_scale_factor());
-    viewport_vertical_adjust = clip_layer->ViewportBoundsDelta().y();
-  }
-
-  bool y_offset_did_change = false;
-  for (auto* scrollbar : ScrollbarsFor(scroll_layer->element_id())) {
-    if (scrollbar->orientation() == HORIZONTAL) {
-      scrollbar->SetCurrentPos(current_offset.x());
-      scrollbar->SetClipLayerLength(clip_size.width());
-      scrollbar->SetScrollLayerLength(scroll_size.width());
-    } else {
-      y_offset_did_change = scrollbar->SetCurrentPos(current_offset.y());
-      scrollbar->SetClipLayerLength(clip_size.height());
-      scrollbar->SetScrollLayerLength(scroll_size.height());
+      viewport_vertical_adjust = bounds_layer->ViewportBoundsDelta().y();
     }
-    scrollbar->SetVerticalAdjust(viewport_vertical_adjust);
+
+    bool y_offset_did_change = false;
+    for (auto* scrollbar : ScrollbarsFor(scrolling_element_id)) {
+      if (scrollbar->orientation() == HORIZONTAL) {
+        scrollbar->SetCurrentPos(current_offset.x());
+        scrollbar->SetClipLayerLength(bounds_size.width());
+        scrollbar->SetScrollLayerLength(scrolling_size.width());
+      } else {
+        y_offset_did_change = scrollbar->SetCurrentPos(current_offset.y());
+        scrollbar->SetClipLayerLength(bounds_size.height());
+        scrollbar->SetScrollLayerLength(scrolling_size.height());
+      }
+      scrollbar->SetVerticalAdjust(viewport_vertical_adjust);
+    }
+
+    if (y_offset_did_change && is_viewport_scrollbar)
+      TRACE_COUNTER_ID1("cc", "scroll_offset_y", scrolling_layer->id(),
+                        current_offset.y());
   }
 
-  if (y_offset_did_change && is_viewport_scrollbar)
-    TRACE_COUNTER_ID1("cc", "scroll_offset_y", scroll_layer->id(),
-                      current_offset.y());
+  scrollbar_geometries_need_update_ = false;
 }
 
 const RenderSurfaceImpl* LayerTreeImpl::RootRenderSurface() const {
@@ -925,7 +925,9 @@
         ClampPageScaleFactorToLimits(current_page_scale_factor()));
 
   set_needs_update_draw_properties();
-  DidUpdateScrollState(viewport_layer_ids_.inner_viewport_scroll);
+
+  // Viewport scrollbar sizes depend on the page scale factor.
+  SetScrollbarGeometriesNeedUpdate();
 
   if (IsActiveTree() && layer_tree_host_impl_->ViewportMainScrollLayer()) {
     if (ScrollbarAnimationController* controller =
@@ -1710,9 +1712,8 @@
         scroll_element_id, scrollbar_layer->Opacity());
   }
 
-  // TODO(pdr): Refactor DidUpdateScrollState to use ElementIds instead of
-  // layer ids and remove this use of LayerIdByElementId.
-  DidUpdateScrollState(LayerIdByElementId(scroll_element_id));
+  // The new scrollbar's geometries need to be initialized.
+  SetScrollbarGeometriesNeedUpdate();
 }
 
 void LayerTreeImpl::UnregisterScrollbar(
@@ -1754,22 +1755,10 @@
   if (layer->scroll_clip_layer_id() == Layer::INVALID_ID)
     return;
 
-  clip_scroll_map_.insert(
-      std::pair<int, int>(layer->scroll_clip_layer_id(), layer->id()));
-
-  DidUpdateScrollState(layer->id());
-
   if (settings().scrollbar_animator == LayerTreeSettings::AURA_OVERLAY)
     layer->set_needs_show_scrollbars(true);
 }
 
-void LayerTreeImpl::UnregisterScrollLayer(LayerImpl* layer) {
-  if (layer->scroll_clip_layer_id() == Layer::INVALID_ID)
-    return;
-
-  clip_scroll_map_.erase(layer->scroll_clip_layer_id());
-}
-
 static bool PointHitsRect(
     const gfx::PointF& screen_space_point,
     const gfx::Transform& local_space_to_screen_space_transform,
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 3bf4e0ad..7f05e43 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -453,7 +453,6 @@
   ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const;
 
   void RegisterScrollLayer(LayerImpl* layer);
-  void UnregisterScrollLayer(LayerImpl* layer);
 
   LayerImpl* FindFirstScrollingLayerOrDrawnScrollbarThatIsHitByPoint(
       const gfx::PointF& screen_space_point);
@@ -490,7 +489,20 @@
   std::unique_ptr<PendingPageScaleAnimation> TakePendingPageScaleAnimation();
 
   void DidUpdateScrollOffset(int layer_id);
-  void DidUpdateScrollState(int layer_id);
+
+  // Mark the scrollbar geometries (e.g., thumb size and position) as needing an
+  // update.
+  void SetScrollbarGeometriesNeedUpdate() {
+    if (IsActiveTree())
+      scrollbar_geometries_need_update_ = true;
+  }
+  bool ScrollbarGeometriesNeedUpdate() const {
+    return scrollbar_geometries_need_update_;
+  }
+  // Update the geometries of all scrollbars (e.g., thumb size and position). An
+  // update only occurs if a scroll-related layer has changed (see:
+  // SetScrollbarGeometriesNeedUpdate).
+  void UpdateScrollbarGeometries();
 
   bool have_scroll_event_handlers() const {
     return have_scroll_event_handlers_;
@@ -531,7 +543,6 @@
   bool SetPageScaleFactorLimits(float min_page_scale_factor,
                                 float max_page_scale_factor);
   bool IsViewportLayerId(int id) const;
-  void UpdateScrollbars(int scroll_layer_id, int clip_layer_id);
   void DidUpdatePageScale();
   void PushBrowserControls(const float* top_controls_shown_ratio);
   bool ClampBrowserControlsShownRatio();
@@ -579,11 +590,6 @@
   std::unordered_map<ElementId, FilterOperations, ElementIdHash>
       element_id_to_filter_animations_;
 
-  // Maps from clip layer ids to scroll layer ids.  Note that this only includes
-  // the subset of clip layers that act as scrolling containers.  (This is
-  // derived from LayerImpl::scroll_clip_layer_ and exists to avoid O(n) walks.)
-  std::unordered_map<int, int> clip_scroll_map_;
-
   struct ScrollbarLayerIds {
     int horizontal = Layer::INVALID_ID;
     int vertical = Layer::INVALID_ID;
@@ -606,6 +612,10 @@
   bool viewport_size_invalid_;
   bool needs_update_draw_properties_;
 
+  // True if a scrollbar geometry value has changed. For example, if the scroll
+  // offset changes, scrollbar thumb positions need to be updated.
+  bool scrollbar_geometries_need_update_;
+
   // In impl-side painting mode, this is true when the tree may contain
   // structural differences relative to the active tree.
   bool needs_full_tree_sync_;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index faea5d9..384e420 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1742,6 +1742,12 @@
         //   exit Chrome on top of closing the tab
         final boolean minimizeApp = !shouldCloseTab || currentTab.isCreatedForExternalApp();
         if (minimizeApp) {
+            // TODO(mthiesse): We never want to close Chrome through the in-vr back button (but we
+            // want to reuse CTA logic for how the back button should otherwise behave). We should
+            // refactor the behaviour in this function so that we can either re-use the parts of
+            // this behaviour we want, or be able to know in advance whether or not clicking the
+            // back button would close Chrome so that we can disable it.
+            if (VrShellDelegate.isInVr()) return true;
             if (shouldCloseTab) {
                 recordBackPressedUma("Minimized and closed tab", BACK_PRESSED_MINIMIZED_TAB_CLOSED);
                 mActivityStopMetrics.setStopReason(ActivityStopMetrics.STOP_REASON_BACK_BUTTON);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index d5aa628..44556c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -58,6 +58,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.UrlUtilities;
+import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.net.NetworkChangeNotifier;
@@ -228,6 +229,7 @@
         @Override
         public void focusSearchBox(boolean beginVoiceSearch, String pastedText) {
             if (mIsDestroyed) return;
+            if (VrShellDelegate.isInVr()) return;
             if (mFakeboxDelegate != null) {
                 if (beginVoiceSearch) {
                     mFakeboxDelegate.startVoiceRecognition();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index a6f0500..8bc205a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -25,8 +25,10 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
@@ -61,15 +63,15 @@
     // a way to compute good values for any screen size/scaling ratio.
 
     // Increasing DPR any more than this doesn't appear to increase text quality.
-    private static final float DEFAULT_DPR = 1.2f;
+    private static final float DEFAULT_DPR = 1.4f;
     // For WebVR we just create a DPR 1.0 display that matches the physical display size.
     private static final float WEBVR_DPR = 1.0f;
     // Fairly arbitrary values that put a good amount of content on the screen without making the
     // text too small to read.
     @VisibleForTesting
-    public static final float DEFAULT_CONTENT_WIDTH = 960f;
+    public static final float DEFAULT_CONTENT_WIDTH = 800f;
     @VisibleForTesting
-    public static final float DEFAULT_CONTENT_HEIGHT = 640f;
+    public static final float DEFAULT_CONTENT_HEIGHT = 533f;
 
     // Make full screen 16:9 until we get exact dimensions from playing video.
     private static final float FULLSCREEN_CONTENT_WIDTH = 1024f;
@@ -585,14 +587,27 @@
 
     @CalledByNative
     public void navigateBack() {
-        mActivity.getToolbarManager().back();
+        if (mActivity instanceof ChromeTabbedActivity) {
+            // TODO(mthiesse): We should do this for custom tabs as well, as back for custom tabs
+            // is also expected to close tabs.
+            ((ChromeTabbedActivity) mActivity).handleBackPressed();
+        } else {
+            mActivity.getToolbarManager().back();
+        }
         updateHistoryButtonsVisibility();
     }
 
     private void updateHistoryButtonsVisibility() {
-        boolean canGoBack = mTab != null && mTab.canGoBack();
-        boolean canGoForward = mTab != null && mTab.canGoForward();
-        nativeSetHistoryButtonsEnabled(mNativeVrShell, canGoBack, canGoForward);
+        if (mTab == null) {
+            nativeSetHistoryButtonsEnabled(mNativeVrShell, false, false);
+            return;
+        }
+        // Hitting back when on the NTP usually closes Chrome, which we don't allow in VR, so we
+        // just disable the back button.
+        boolean shouldAlwaysGoBack = mActivity instanceof ChromeTabbedActivity
+                && (mNativePage == null || !(mNativePage instanceof NewTabPage));
+        boolean canGoBack = mTab.canGoBack() || shouldAlwaysGoBack;
+        nativeSetHistoryButtonsEnabled(mNativeVrShell, canGoBack, mTab.canGoForward());
     }
 
     @CalledByNative
diff --git a/chrome/browser/accessibility/accessibility_extension_api.cc b/chrome/browser/accessibility/accessibility_extension_api.cc
index 62abc25..4e975f3 100644
--- a/chrome/browser/accessibility/accessibility_extension_api.cc
+++ b/chrome/browser/accessibility/accessibility_extension_api.cc
@@ -27,6 +27,7 @@
 #include "extensions/common/error_utils.h"
 #include "extensions/common/image_util.h"
 #include "extensions/common/manifest_handlers/background_info.h"
+#include "ui/events/keycodes/keyboard_codes.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
@@ -164,3 +165,29 @@
 
   return RespondNow(Error(kErrorNotSupported));
 }
+
+#if defined(OS_CHROMEOS)
+ExtensionFunction::ResponseAction
+AccessibilityPrivateSetSwitchAccessKeysFunction::Run() {
+  std::unique_ptr<accessibility_private::SetSwitchAccessKeys::Params> params =
+      accessibility_private::SetSwitchAccessKeys::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  // For now, only accept key code if it represents an alphanumeric character.
+  std::set<int> key_codes;
+  for (auto key_code : params->key_codes) {
+    EXTENSION_FUNCTION_VALIDATE(key_code >= ui::VKEY_0 &&
+                                key_code <= ui::VKEY_Z);
+    key_codes.insert(key_code);
+  }
+
+  chromeos::AccessibilityManager* manager =
+      chromeos::AccessibilityManager::Get();
+
+  // AccessibilityManager can be null during system shut down, but no need to
+  // return error in this case, so just check that manager is not null.
+  if (manager)
+    manager->SetSwitchAccessKeys(key_codes);
+  return RespondNow(NoArguments());
+}
+#endif  // defined (OS_CHROMEOS)
diff --git a/chrome/browser/accessibility/accessibility_extension_api.h b/chrome/browser/accessibility/accessibility_extension_api.h
index 3784ec4e..c12e868 100644
--- a/chrome/browser/accessibility/accessibility_extension_api.h
+++ b/chrome/browser/accessibility/accessibility_extension_api.h
@@ -50,4 +50,15 @@
                              ACCESSIBILITY_PRIVATE_DARKENSCREEN)
 };
 
+// API function that sets the keys to be captured by Switch Access.
+#if defined(OS_CHROMEOS)
+class AccessibilityPrivateSetSwitchAccessKeysFunction
+    : public UIThreadExtensionFunction {
+  ~AccessibilityPrivateSetSwitchAccessKeysFunction() override {}
+  ResponseAction Run() override;
+  DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.setSwitchAccessKeys",
+                             ACCESSIBILITY_PRIVATE_SETSWITCHACCESSKEYS)
+};
+#endif  // defined (OS_CHROMEOS)
+
 #endif  // CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EXTENSION_API_H_
diff --git a/chrome/browser/android/vr_shell/color_scheme.cc b/chrome/browser/android/vr_shell/color_scheme.cc
index 49432770d..c15cc11a 100644
--- a/chrome/browser/android/vr_shell/color_scheme.cc
+++ b/chrome/browser/android/vr_shell/color_scheme.cc
@@ -18,47 +18,50 @@
     return;
 
   ColorScheme& normal_scheme = kColorSchemes[ColorScheme::kModeNormal];
-  normal_scheme.horizon = 0xFFE3E3E3;
-  normal_scheme.floor = 0xFFCFCFCF;
-  normal_scheme.ceiling = 0xFFDBDBDB;
-  normal_scheme.floor_grid = SK_ColorWHITE;
-  normal_scheme.background = 0x66EBEBEB;
-  normal_scheme.background_hover = 0xFFEAEAEA;
-  normal_scheme.background_down = 0xFFFAFAFA;
+  normal_scheme.horizon = 0xFF999999;
+  normal_scheme.floor = 0xFF8C8C8C;
+  normal_scheme.ceiling = normal_scheme.floor;
+  normal_scheme.floor_grid = 0x26FFFFFF;
+  normal_scheme.background = 0xCCB3B3B3;
+  normal_scheme.background_hover = 0xFFCCCCCC;
+  normal_scheme.background_down = 0xFFF3F3F3;
   normal_scheme.foreground = 0xFF333333;
   normal_scheme.emphasized = 0xFF000000;
   normal_scheme.deemphasized = 0xFF5A5A5A;
-  normal_scheme.separator = 0x33000000;
+  normal_scheme.separator = 0xFF9E9E9E;
   normal_scheme.secure = gfx::kGoogleGreen700;
   normal_scheme.insecure = gfx::kGoogleRed700;
   normal_scheme.warning = normal_scheme.deemphasized;
   normal_scheme.disabled = 0x33333333;
+  normal_scheme.loading_foreground = normal_scheme.foreground;
+  normal_scheme.loading_background = normal_scheme.floor;
 
   kColorSchemes[ColorScheme::kModeFullscreen] =
       kColorSchemes[ColorScheme::kModeNormal];
   ColorScheme& fullscreen_scheme = kColorSchemes[ColorScheme::kModeFullscreen];
-  fullscreen_scheme.horizon = 0xFF0A0015;
+  fullscreen_scheme.horizon = 0xFF000714;
   fullscreen_scheme.floor = 0xFF070F1C;
   fullscreen_scheme.ceiling = 0xFF04080F;
-  fullscreen_scheme.floor_grid = 0x80A3E0FF;
+  fullscreen_scheme.floor_grid = 0x40A3E0FF;
 
-  // TODO(joshcarpenter): Update these per spec.
   ColorScheme& incognito_scheme = kColorSchemes[ColorScheme::kModeIncognito];
   incognito_scheme.horizon = 0xFF2E2E2E;
   incognito_scheme.floor = 0xFF282828;
   incognito_scheme.ceiling = 0xFF2F2F2F;
-  incognito_scheme.floor_grid = 0xFF595959;
-  incognito_scheme.foreground = 0xFFE6E6E6;
-  incognito_scheme.emphasized = 0xFFFFFFFF;
-  incognito_scheme.deemphasized = incognito_scheme.foreground;
-  incognito_scheme.background = 0xB9454545;
-  incognito_scheme.background_hover = 0xD9454545;
-  incognito_scheme.background_down = 0xFF454545;
-  incognito_scheme.separator = 0xDD595959;
-  incognito_scheme.secure = incognito_scheme.foreground;
-  incognito_scheme.insecure = incognito_scheme.foreground;
-  incognito_scheme.warning = incognito_scheme.foreground;
+  incognito_scheme.floor_grid = 0xCC595959;
+  incognito_scheme.foreground = 0xFFBCBCBC;
+  incognito_scheme.emphasized = 0xFFEDEDED;
+  incognito_scheme.deemphasized = 0xFF878787;
+  incognito_scheme.background = 0xCC454545;
+  incognito_scheme.background_hover = 0xCC505050;
+  incognito_scheme.background_down = 0xCC888888;
+  incognito_scheme.separator = 0xFF474747;
+  incognito_scheme.secure = incognito_scheme.emphasized;
+  incognito_scheme.insecure = incognito_scheme.emphasized;
+  incognito_scheme.warning = incognito_scheme.emphasized;
   incognito_scheme.disabled = 0x33E6E6E6;
+  normal_scheme.loading_foreground = 0xFF454545;
+  normal_scheme.loading_background = 0xFF8A8A8A;
 
   initialized = true;
 }
diff --git a/chrome/browser/android/vr_shell/color_scheme.h b/chrome/browser/android/vr_shell/color_scheme.h
index 9f14c5ac..ebbed05 100644
--- a/chrome/browser/android/vr_shell/color_scheme.h
+++ b/chrome/browser/android/vr_shell/color_scheme.h
@@ -48,6 +48,10 @@
 
   // The color used for disabled icons.
   SkColor disabled;
+
+  // Colors used for the loading progress bar indicator.
+  SkColor loading_background;
+  SkColor loading_foreground;
 };
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/textures/loading_indicator_texture.cc b/chrome/browser/android/vr_shell/textures/loading_indicator_texture.cc
index 2fcf2f2..d330387 100644
--- a/chrome/browser/android/vr_shell/textures/loading_indicator_texture.cc
+++ b/chrome/browser/android/vr_shell/textures/loading_indicator_texture.cc
@@ -13,8 +13,8 @@
 
 namespace {
 
-static constexpr SkColor kBackground = 0xCCAAAAAA;
-static constexpr SkColor kForeground = 0xCC444444;
+static constexpr SkColor kBackground = 0xFF8C8C8C;
+static constexpr SkColor kForeground = 0xFF333333;
 static constexpr float kWidth = 0.24;
 static constexpr float kHeight = 0.008;
 
diff --git a/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc b/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
index c89c3102..80d73a23f 100644
--- a/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
+++ b/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
@@ -22,15 +22,17 @@
 
 namespace vr_shell {
 
+// TODO(cjgrant): Use ColorScheme instead of hardcoded values
+// where it makes sense.
 static const SkColor kEmphasizedColor = 0xFF000000;
 static const SkColor kDeemphasizedColor = 0xFF5A5A5A;
 static const SkColor kSecureColor = gfx::kGoogleGreen700;
 static const SkColor kWarningColor = gfx::kGoogleRed700;
 
-static const SkColor kIncognitoDeemphasizedColor = 0xFFE6E6E6;
-static const SkColor kIncognitoEmphasizedColor = 0xFFFFFFFF;
-static const SkColor kIncognitoSecureColor = 0xFFE6E6E6;
-static const SkColor kIncognitoWarningColor = 0xFFE6E6E6;
+static const SkColor kIncognitoDeemphasizedColor = 0xFF878787;
+static const SkColor kIncognitoEmphasizedColor = 0xFFEDEDED;
+static const SkColor kIncognitoSecureColor = 0xFFEDEDED;
+static const SkColor kIncognitoWarningColor = 0xFFEDEDED;
 
 static constexpr int kUrlWidth = 400;
 static constexpr int kUrlHeight = 30;
diff --git a/chrome/browser/android/vr_shell/ui_elements/url_bar.cc b/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
index 72dcf1c..34ab3538 100644
--- a/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
@@ -57,7 +57,7 @@
 void UrlBar::OnButtonUp(const gfx::PointF& position) {
   down_ = false;
   OnStateUpdated(position);
-  if (texture_->HitsBackButton(position))
+  if (can_go_back_ && texture_->HitsBackButton(position))
     back_button_callback_.Run();
 }
 
@@ -84,7 +84,8 @@
 }
 
 void UrlBar::SetHistoryButtonsEnabled(bool can_go_back) {
-  texture_->SetHistoryButtonsEnabled(can_go_back);
+  can_go_back_ = can_go_back;
+  texture_->SetHistoryButtonsEnabled(can_go_back_);
 }
 
 void UrlBar::SetSecurityLevel(security_state::SecurityLevel level) {
diff --git a/chrome/browser/android/vr_shell/ui_elements/url_bar.h b/chrome/browser/android/vr_shell/ui_elements/url_bar.h
index feeaa4c..5c28acb8 100644
--- a/chrome/browser/android/vr_shell/ui_elements/url_bar.h
+++ b/chrome/browser/android/vr_shell/ui_elements/url_bar.h
@@ -48,6 +48,7 @@
   std::unique_ptr<UrlBarTexture> texture_;
   base::Callback<void()> back_button_callback_;
   bool enabled_ = false;
+  bool can_go_back_ = false;
   bool down_ = false;
   base::TimeTicks last_begin_frame_time_;
   base::TimeTicks last_update_time_;
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index 4a6f4511..2ef910a 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -50,11 +50,16 @@
 static constexpr float kUrlBarWidth = 0.672 * kUrlBarDistance;
 static constexpr float kUrlBarHeight = 0.088 * kUrlBarDistance;
 static constexpr float kUrlBarVerticalOffset = -0.516 * kUrlBarDistance;
+static constexpr float kUrlBarRotationRad = -0.175;
 
 static constexpr float kLoadingIndicatorWidth = 0.24 * kUrlBarDistance;
 static constexpr float kLoadingIndicatorHeight = 0.008 * kUrlBarDistance;
-static constexpr float kLoadingIndicatorOffset =
-    -0.016 * kUrlBarDistance - kLoadingIndicatorHeight / 2;
+static constexpr float kLoadingIndicatorVerticalOffset =
+    (-kUrlBarVerticalOffset + kContentVerticalOffset - kContentHeight / 2 -
+     kUrlBarHeight / 2) /
+    2;
+static constexpr float kLoadingIndicatorDepthOffset =
+    (kUrlBarDistance - kContentDistance) / 2;
 
 static constexpr float kSceneSize = 25.0;
 static constexpr float kSceneHeight = 4.0;
@@ -260,6 +265,7 @@
   url_bar->set_debug_id(kUrlBar);
   url_bar->set_id(AllocateId());
   url_bar->set_translation({0, kUrlBarVerticalOffset, -kUrlBarDistance});
+  url_bar->set_rotation({1.0, 0.0, 0.0, kUrlBarRotationRad});
   url_bar->set_size({kUrlBarWidth, kUrlBarHeight, 1});
   url_bar->SetBackButtonCallback(
       base::Bind(&UiSceneManager::OnBackButtonClicked, base::Unretained(this)));
@@ -270,7 +276,8 @@
   auto indicator = base::MakeUnique<LoadingIndicator>(256);
   indicator->set_debug_id(kLoadingIndicator);
   indicator->set_id(AllocateId());
-  indicator->set_translation({0, 0, kLoadingIndicatorOffset});
+  indicator->set_translation(
+      {0, kLoadingIndicatorVerticalOffset, kLoadingIndicatorDepthOffset});
   indicator->set_size({kLoadingIndicatorWidth, kLoadingIndicatorHeight, 1});
   indicator->set_parent_id(url_bar_->id());
   indicator->set_y_anchoring(YAnchoring::YTOP);
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 127397c..e097b551 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -78,8 +78,6 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/mock_cryptohome_client.h"
 #include "components/signin/core/account_id/account_id.h"
@@ -1367,8 +1365,6 @@
 #if defined(OS_CHROMEOS)
 TEST_F(ChromeBrowsingDataRemoverDelegateTest,
        ContentProtectionPlatformKeysRemoval) {
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service;
-  chromeos::ScopedTestCrosSettings test_cros_settings;
   chromeos::MockUserManager* mock_user_manager =
       new testing::NiceMock<chromeos::MockUserManager>();
   mock_user_manager->SetActiveUser(
diff --git a/chrome/browser/budget_service/budget_manager_browsertest.cc b/chrome/browser/budget_service/budget_manager_browsertest.cc
index 65e56f76..954e4d59 100644
--- a/chrome/browser/budget_service/budget_manager_browsertest.cc
+++ b/chrome/browser/budget_service/budget_manager_browsertest.cc
@@ -8,12 +8,16 @@
 #include "build/build_config.h"
 #include "chrome/browser/budget_service/budget_manager.h"
 #include "chrome/browser/budget_service/budget_manager_factory.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/engagement/site_engagement_score.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
@@ -44,6 +48,15 @@
 
   // InProcessBrowserTest:
   void SetUpOnMainThread() override {
+    SiteEngagementScore::SetParamValuesForTesting();
+
+    // Grant Notification permission for these tests. See the privacy
+    // requirement for this outlined in https://crbug.com/710809.
+    HostContentSettingsMapFactory::GetForProfile(browser()->profile())
+        ->SetContentSettingDefaultScope(https_server_->base_url(), GURL(),
+                                        CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+                                        std::string(), CONTENT_SETTING_ALLOW);
+
     LoadTestPage();
     InProcessBrowserTest::SetUpOnMainThread();
     budget_manager_ = BudgetManagerFactory::GetForProfile(browser()->profile());
@@ -57,10 +70,21 @@
     InProcessBrowserTest::SetUpCommandLine(command_line);
   }
 
+  // Sets the absolute Site Engagement |score| for the testing origin, assuming
+  // that notification permission has been granted.
+  // The |score| must be higher than the bonus points awarded to an origin for
+  // having the Notification permission granted. Should be wrapped in the
+  // ASSERT_NO_FATAL_FAILURE macro because it contains an ASSERT_GE.
   void SetSiteEngagementScore(double score) {
     SiteEngagementService* service =
         SiteEngagementService::Get(browser()->profile());
-    service->ResetBaseScoreForURL(https_server_->GetURL(kTestURL), score);
+
+    double notification_permission_bonus =
+        SiteEngagementScore::GetNotificationPermissionPoints();
+    ASSERT_GE(score, notification_permission_bonus);
+
+    service->ResetBaseScoreForURL(https_server_->GetURL(kTestURL),
+                                  score - notification_permission_bonus);
   }
 
   bool RunScript(const std::string& script, std::string* result) {
@@ -102,7 +126,7 @@
 IN_PROC_BROWSER_TEST_F(BudgetManagerBrowserTest, BudgetInDocument) {
   std::string script_result;
 
-  SetSiteEngagementScore(5);
+  ASSERT_NO_FATAL_FAILURE(SetSiteEngagementScore(5));
 
   // Site Engagement score of 5 gives a budget of 2.
   ASSERT_TRUE(RunScript("documentGetBudget()", &script_result));
@@ -135,7 +159,7 @@
   ASSERT_EQ("ok - service worker registered", script_result);
 
   LoadTestPage();  // Reload to become controlled.
-  SetSiteEngagementScore(12);
+  ASSERT_NO_FATAL_FAILURE(SetSiteEngagementScore(12));
 
   ASSERT_TRUE(RunScript("isControlled()", &script_result));
   ASSERT_EQ("true - is controlled", script_result);
diff --git a/chrome/browser/budget_service/budget_service_impl.cc b/chrome/browser/budget_service/budget_service_impl.cc
index 5bbb9b4..2db176cb 100644
--- a/chrome/browser/budget_service/budget_service_impl.cc
+++ b/chrome/browser/budget_service/budget_service_impl.cc
@@ -7,7 +7,10 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/budget_service/budget_manager.h"
 #include "chrome/browser/budget_service/budget_manager_factory.h"
+#include "chrome/browser/permissions/permission_manager.h"
+#include "chrome/browser/permissions/permission_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
@@ -50,9 +53,33 @@
       content::RenderProcessHost::FromID(render_process_id_);
   DCHECK(host);
 
+  Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
+
+  PermissionManager* permission_manager =
+      PermissionManagerFactory::GetForProfile(profile);
+  DCHECK(permission_manager);
+
+  // By request of the Privacy Team, we only communicate the budget buckets with
+  // the developer when the notification permission has been granted. This is
+  // something the impact of which has to be reconsidered when the feature is
+  // ready to ship for real. See https://crbug.com/710809 for context.
+  if (permission_manager->GetPermissionStatus(
+          content::PermissionType::NOTIFICATIONS, origin.GetURL(), GURL()) !=
+      blink::mojom::PermissionStatus::GRANTED) {
+    blink::mojom::BudgetStatePtr empty_state(blink::mojom::BudgetState::New());
+    empty_state->budget_at = 0;
+    empty_state->time = base::Time::Now().ToDoubleT();
+
+    std::vector<blink::mojom::BudgetStatePtr> predictions;
+    predictions.push_back(std::move(empty_state));
+
+    callback.Run(blink::mojom::BudgetServiceErrorType::NONE,
+                 std::move(predictions));
+    return;
+  }
+
   // Query the BudgetManager for the budget.
-  content::BrowserContext* context = host->GetBrowserContext();
-  BudgetManagerFactory::GetForProfile(context)->GetBudget(origin, callback);
+  BudgetManagerFactory::GetForProfile(profile)->GetBudget(origin, callback);
 }
 
 void BudgetServiceImpl::Reserve(const url::Origin& origin,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 0074d39..0e1a8ff4 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1541,6 +1541,8 @@
     "login/screens/mock_network_screen.h",
     "login/screens/mock_update_screen.cc",
     "login/screens/mock_update_screen.h",
+    "scoped_set_running_on_chromeos_for_testing.cc",
+    "scoped_set_running_on_chromeos_for_testing.h",
   ]
 
   deps = [
@@ -1841,6 +1843,7 @@
     ":device_policy_proto",
     ":test_support",
     "//ash/resources",
+    "//base",
     "//components/cryptauth:test_support",
     "//components/drive",
     "//components/drive:test_support_chromeos",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index dcad19e4..d6d1cc3 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -1531,4 +1531,9 @@
     extension_registry_observer_.Add(registry);
 }
 
+void AccessibilityManager::SetSwitchAccessKeys(const std::set<int>& key_codes) {
+  if (switch_access_enabled_)
+    switch_access_event_handler_->SetKeysToCapture(key_codes);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h
index 38f6bca..1553049 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.h
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -311,6 +311,9 @@
   }
   bool keyboard_listener_capture() { return keyboard_listener_capture_; }
 
+  // Set the keys to be captured by Switch Access.
+  void SetSwitchAccessKeys(const std::set<int>& key_codes);
+
  protected:
   AccessibilityManager();
   ~AccessibilityManager() override;
diff --git a/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc b/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc
index 3f01698..c9b9cde 100644
--- a/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc
+++ b/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc
@@ -24,15 +24,16 @@
     ash::Shell::Get()->RemovePreTargetHandler(this);
 }
 
+void SwitchAccessEventHandler::SetKeysToCapture(
+    const std::set<int>& key_codes) {
+  captured_keys_ = key_codes;
+}
+
 void SwitchAccessEventHandler::OnKeyEvent(ui::KeyEvent* event) {
   DCHECK(event);
 
   ui::KeyboardCode key_code = event->key_code();
-  if (key_code == ui::VKEY_1 || key_code == ui::VKEY_2 ||
-      key_code == ui::VKEY_3 || key_code == ui::VKEY_4 ||
-      key_code == ui::VKEY_5 || key_code == ui::VKEY_6 ||
-      key_code == ui::VKEY_7 || key_code == ui::VKEY_8 ||
-      key_code == ui::VKEY_9) {
+  if (captured_keys_.find(key_code) != captured_keys_.end()) {
     CancelEvent(event);
     DispatchKeyEventToSwitchAccess(*event);
   }
diff --git a/chrome/browser/chromeos/accessibility/switch_access_event_handler.h b/chrome/browser/chromeos/accessibility/switch_access_event_handler.h
index 331f836..e52af0cc 100644
--- a/chrome/browser/chromeos/accessibility/switch_access_event_handler.h
+++ b/chrome/browser/chromeos/accessibility/switch_access_event_handler.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SWITCH_ACCESS_EVENT_HANDLER_H_
 #define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SWITCH_ACCESS_EVENT_HANDLER_H_
 
+#include <set>
+
 #include "base/macros.h"
 #include "ui/events/event_handler.h"
 
@@ -22,6 +24,8 @@
   SwitchAccessEventHandler();
   ~SwitchAccessEventHandler() override;
 
+  void SetKeysToCapture(const std::set<int>& key_codes);
+
  private:
   // EventHandler:
   void OnKeyEvent(ui::KeyEvent* event) override;
@@ -29,6 +33,8 @@
   void CancelEvent(ui::Event* event);
   void DispatchKeyEventToSwitchAccess(const ui::KeyEvent& event);
 
+  std::set<int> captured_keys_;
+
   DISALLOW_COPY_AND_ASSIGN(SwitchAccessEventHandler);
 };
 
diff --git a/chrome/browser/chromeos/customization/customization_document_unittest.cc b/chrome/browser/chromeos/customization/customization_document_unittest.cc
index 6922a29..1a94c747 100644
--- a/chrome/browser/chromeos/customization/customization_document_unittest.cc
+++ b/chrome/browser/chromeos/customization/customization_document_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h"
+#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/extensions/external_provider_impl.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
@@ -293,6 +294,7 @@
 
  private:
   system::ScopedFakeStatisticsProvider fake_statistics_provider_;
+  ScopedCrosSettingsTestHelper scoped_cros_settings_test_helper_;
   content::TestBrowserThreadBundle thread_bundle_;
   TestingPrefServiceSimple local_state_;
   TestURLFetcherCallback url_callback_;
diff --git a/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc
index 9cca475..869863d4 100644
--- a/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h"
 
+#include <memory>
 #include <string>
 
 #include "base/run_loop.h"
@@ -41,14 +42,16 @@
   void TearDown() override;
 
   ActiveTabPermissionGranterDelegateChromeOS delegate_;
-  chromeos::ScopedTestPublicSessionLoginState login_state_;
+  std::unique_ptr<chromeos::ScopedTestPublicSessionLoginState> login_state_;
 };
 
 void ActiveTabPermissionGranterDelegateChromeOSTest::SetUp() {
   ChromeRenderViewHostTestHarness::SetUp();
+  login_state_.reset(new chromeos::ScopedTestPublicSessionLoginState());
 }
 
 void ActiveTabPermissionGranterDelegateChromeOSTest::TearDown() {
+  login_state_.reset();
   permission_helper::ResetPermissionsForTesting();
   ChromeRenderViewHostTestHarness::TearDown();
 }
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
index 56b71b1..4539fa2 100644
--- a/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
+++ b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
@@ -119,7 +119,7 @@
 
   std::vector<PermissionIDSet> allowed_permissions_;
 
-  chromeos::ScopedTestPublicSessionLoginState login_state_;
+  std::unique_ptr<chromeos::ScopedTestPublicSessionLoginState> login_state_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PublicSessionPermissionHelperTest);
@@ -127,11 +127,13 @@
 
 void PublicSessionPermissionHelperTest::SetUp() {
   ChromeRenderViewHostTestHarness::SetUp();
+  login_state_.reset(new chromeos::ScopedTestPublicSessionLoginState());
   extension_a_ = LoadManifestHelper("extension_a");
   extension_b_ = LoadManifestHelper("extension_b");
 }
 
 void PublicSessionPermissionHelperTest::TearDown() {
+  login_state_.reset();
   ResetPermissionsForTesting();
   ChromeRenderViewHostTestHarness::TearDown();
 }
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index f7ad121..351f450 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -5,9 +5,11 @@
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 
 #include "base/files/file_path.h"
+#include "base/sys_info.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -20,9 +22,17 @@
 namespace util {
 namespace {
 
+const char kLsbRelease[] =
+    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+
 TEST(FileManagerPathUtilTest, MultiProfileDownloadsFolderMigration) {
   content::TestBrowserThreadBundle thread_bundle;
   TestingProfile profile;
+  // MigratePathFromOldFormat is explicitly disabled on Linux build.
+  // So we need to fake that this is real ChromeOS system.
+  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
+                                                              base::Time());
 
   // This looks like "/home/chronos/u-hash/Downloads" in the production
   // environment.
diff --git a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
index 81b85d51..e33eb0e 100644
--- a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
+++ b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
@@ -140,15 +140,20 @@
         mock_caller_(NULL),
         mock_homedir_methods_(NULL),
         owner_key_util_(new ownership::MockOwnerKeyUtil()) {
+    // Testing profile must be initialized after user_manager_ +
+    // user_manager_enabler_, because it will create another UserManager
+    // instance if UserManager instance has not been registed before.
+    profile_.reset(new TestingProfile);
     OwnerSettingsServiceChromeOSFactory::GetInstance()
         ->SetOwnerKeyUtilForTesting(owner_key_util_);
     user_context_.SetKey(Key("fakepass"));
     user_context_.SetUserIDHash("me_nowhere_com_hash");
     const user_manager::User* user =
         user_manager_->AddUser(user_context_.GetAccountId());
-    profile_.set_profile_name(user_context_.GetAccountId().GetUserEmail());
+    profile_->set_profile_name(user_context_.GetAccountId().GetUserEmail());
 
-    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, &profile_);
+    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
+                                                            profile_.get());
 
     CreateTransformedKey(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
                          SystemSaltGetter::ConvertRawSaltToHexString(
@@ -325,9 +330,9 @@
   ScopedDeviceSettingsTestHelper device_settings_test_helper_;
   ScopedTestCrosSettings test_cros_settings_;
 
-  TestingProfile profile_;
-  std::unique_ptr<TestingProfileManager> profile_manager_;
   chromeos::FakeChromeUserManager* user_manager_;
+  std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
   ScopedUserManagerEnabler user_manager_enabler_;
 
   cryptohome::MockAsyncMethodCaller* mock_caller_;
diff --git a/chrome/browser/chromeos/login/hwid_checker_unittest.cc b/chrome/browser/chromeos/login/hwid_checker_unittest.cc
index 6badad8..e7d8e13 100644
--- a/chrome/browser/chromeos/login/hwid_checker_unittest.cc
+++ b/chrome/browser/chromeos/login/hwid_checker_unittest.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/chromeos/login/hwid_checker.h"
 #include "base/sys_info.h"
 #include "base/test/scoped_command_line.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/login/hwid_checker.h"
+#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chromeos/system/fake_statistics_provider.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-
 namespace chromeos {
 
 TEST(HWIDCheckerTest, EmptyHWID) {
@@ -128,14 +128,9 @@
 }
 
 #if defined(GOOGLE_CHROME_BUILD)
-
-// Sets a valid Chrome  OS version info so IsRunningOnChromeOS() returns true.
-void SetRunningOnChromeOS() {
-  const char kLsbRelease[] =
-      "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-      "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-  base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
-}
+const char kLsbRelease[] =
+    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
 
 // Test logic for command line "test-type" switch.
 TEST(MachineHWIDCheckerTest, TestSwitch) {
@@ -146,7 +141,7 @@
 
   // THEN IsMachineHWIDCorrect() is always true.
   EXPECT_TRUE(IsMachineHWIDCorrect());
-  SetRunningOnChromeOS();
+  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
   EXPECT_TRUE(IsMachineHWIDCorrect());
 
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
@@ -160,7 +155,7 @@
 // Test logic when not running on Chrome OS.
 TEST(MachineHWIDCheckerTest, NotOnChromeOS) {
   // GIVEN the OS is not Chrome OS.
-  base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
+  ScopedSetRunningOnChromeOSForTesting fake_release("", base::Time());
 
   // THEN IsMachineHWIDCorrect() is always true.
   EXPECT_TRUE(IsMachineHWIDCorrect());
@@ -176,7 +171,7 @@
 // Test logic when running on Chrome OS but the HWID is not present.
 TEST(MachineHWIDCheckerTest, OnCrosNoHWID) {
   // GIVEN the OS is Chrome OS.
-  SetRunningOnChromeOS();
+  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
 
   // GIVEN the HWID is not present.
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
@@ -204,7 +199,7 @@
                                                "DELL HORIZON MAGENTA DVT 4770");
 
   // THEN IsMachineHWIDCorrect() is always true.
-  SetRunningOnChromeOS();
+  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
   EXPECT_TRUE(IsMachineHWIDCorrect());
   fake_statistics_provider.SetMachineStatistic(system::kIsVmKey,
                                                system::kIsVmValueFalse);
@@ -222,7 +217,7 @@
                                                system::kIsVmValueTrue);
 
   // GIVEN the OS is Chrome OS.
-  SetRunningOnChromeOS();
+  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
   // THEN IsMachineHWIDCorrect() is always true.
   fake_statistics_provider.SetMachineStatistic(system::kHardwareClassKey,
                                                "INVALID_HWID");
@@ -239,7 +234,7 @@
 // Test logic when HWID is invalid and we're not in a VM.
 TEST(MachineHWIDCheckerTest, InvalidHWIDInVMNotTrue) {
   // GIVEN the OS is Chrome OS.
-  SetRunningOnChromeOS();
+  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
 
   // GIVEN the HWID is invalid.
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
diff --git a/chrome/browser/chromeos/login/signin/merge_session_load_page_unittest.cc b/chrome/browser/chromeos/login/signin/merge_session_load_page_unittest.cc
index 4de94d8..08c13c5 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_load_page_unittest.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_load_page_unittest.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/chromeos/login/signin/merge_session_load_page.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -52,19 +51,7 @@
 
 class MergeSessionLoadPageTest : public ChromeRenderViewHostTestHarness {
  protected:
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-#if defined OS_CHROMEOS
-  test_user_manager_.reset(new chromeos::ScopedTestUserManager());
-#endif
-  }
-
   void TearDown() override {
-#if defined OS_CHROMEOS
-    // Clean up pending tasks that might depend on the user manager.
-    base::RunLoop().RunUntilIdle();
-    test_user_manager_.reset();
-#endif
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
@@ -118,7 +105,6 @@
  private:
   ScopedTestDeviceSettingsService test_device_settings_service_;
   ScopedTestCrosSettings test_cros_settings_;
-  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
 };
 
 TEST_F(MergeSessionLoadPageTest, MergeSessionPageNotShown) {
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 aca917c..b3fdb4a 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -192,7 +192,9 @@
 }
 
 ChromeUserManagerImpl::ChromeUserManagerImpl()
-    : ChromeUserManager(base::ThreadTaskRunnerHandle::Get()),
+    : ChromeUserManager(base::ThreadTaskRunnerHandle::IsSet()
+                            ? base::ThreadTaskRunnerHandle::Get()
+                            : scoped_refptr<base::TaskRunner>()),
       cros_settings_(CrosSettings::Get()),
       device_local_account_policy_service_(NULL),
       supervised_user_manager_(new SupervisedUserManagerImpl(this)),
@@ -201,7 +203,10 @@
   UpdateNumberOfUsers();
 
   // UserManager instance should be used only on UI thread.
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // (or in unit tests)
+  if (base::ThreadTaskRunnerHandle::IsSet())
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   registrar_.Add(this,
                  chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
                  content::NotificationService::AllSources());
diff --git a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
index 16a8f5b6..e745b54 100644
--- a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
@@ -134,7 +134,9 @@
     ChromeUserManagerImpl* owner)
     : owner_(owner), cros_settings_(CrosSettings::Get()) {
   // SupervisedUserManager instance should be used only on UI thread.
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // (or in unit_tests)
+  if (base::ThreadTaskRunnerHandle::IsSet())
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
   authentication_.reset(new SupervisedUserAuthentication(this));
 }
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
index 91c3678..c744172 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
@@ -40,7 +40,8 @@
 }
 
 DeviceCloudPolicyStoreChromeOS::~DeviceCloudPolicyStoreChromeOS() {
-  device_settings_service_->RemoveObserver(this);
+  if (device_settings_service_)
+    device_settings_service_->RemoveObserver(this);
 }
 
 void DeviceCloudPolicyStoreChromeOS::Store(
@@ -107,6 +108,7 @@
 }
 
 void DeviceCloudPolicyStoreChromeOS::OnDeviceSettingsServiceShutdown() {
+  device_settings_service_->RemoveObserver(this);
   device_settings_service_ = nullptr;
 }
 
diff --git a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc
new file mode 100644
index 0000000..55121cc
--- /dev/null
+++ b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
+
+#include "base/sys_info.h"
+
+namespace chromeos {
+
+ScopedSetRunningOnChromeOSForTesting::ScopedSetRunningOnChromeOSForTesting(
+    const std::string& lsb_release,
+    const base::Time& lsb_release_time) {
+  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, lsb_release_time);
+}
+
+ScopedSetRunningOnChromeOSForTesting::~ScopedSetRunningOnChromeOSForTesting() {
+  base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h
new file mode 100644
index 0000000..d58b6b67
--- /dev/null
+++ b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
+#define CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
+
+#include <string>
+
+#include "base/time/time.h"
+
+namespace chromeos {
+
+// unit_tests must always reset status of modified global singletons,
+// otherwise later tests will fail, because they are run in the same process.
+//
+// This object resets LSB-Release to empty string on destruction, forcing
+// is_running_on_chromeos() to return false.
+class ScopedSetRunningOnChromeOSForTesting {
+ public:
+  ScopedSetRunningOnChromeOSForTesting(const std::string& lsb_release,
+                                       const base::Time& lsb_release_time);
+  ~ScopedSetRunningOnChromeOSForTesting();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedSetRunningOnChromeOSForTesting);
+};
+
+}  // namespace chromeos
+
+#endif  //  CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index 5b732a5..46f4d91 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -37,10 +37,8 @@
 #if defined(OS_CHROMEOS)
 #include "base/run_loop.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/login/scoped_test_public_session_login_state.h"
@@ -451,10 +449,7 @@
   AccountId account_id = AccountId::FromUserEmailGaiaId(user_email, user_id);
   std::string user_id_hash = chromeos::ProfileHelper::Get()->
       GetUserIdHashByUserIdForTesting(user_id);
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
   ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
-  chromeos::ScopedTestUserManager test_user_manager_;
   chromeos::WallpaperManager::Initialize();
   g_browser_process->local_state()->SetString(
       "PublicAccountPendingDataRemoval", user_email);
@@ -491,6 +486,7 @@
   // Cleanup.
   chromeos::WallpaperManager::Shutdown();
   delete ActiveTabPermissionGranter::SetPlatformDelegate(nullptr);
+  chromeos::ChromeUserManager::Get()->Shutdown();
 }
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/extensions/activity_log/activity_database_unittest.cc b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
index 71cb5954..cdd65787 100644
--- a/chrome/browser/extensions/activity_log/activity_database_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
@@ -30,14 +30,6 @@
 #include "sql/statement.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chromeos/chromeos_switches.h"
-#endif
-
 using content::BrowserThread;
 
 namespace constants = activity_log_constants;
@@ -113,18 +105,12 @@
  protected:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-#if defined OS_CHROMEOS
-    test_user_manager_.reset(new chromeos::ScopedTestUserManager());
-#endif
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableExtensionActivityLogTesting);
   }
 
   void TearDown() override {
-#if defined OS_CHROMEOS
-    test_user_manager_.reset();
-#endif
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
@@ -163,12 +149,6 @@
   }
 
  private:
-#if defined OS_CHROMEOS
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
-#endif
-
   ActivityDatabaseTestPolicy* db_delegate_;
 };
 
diff --git a/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
index 87356ec4..4432214 100644
--- a/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_enabled_unittest.cc
@@ -17,12 +17,6 @@
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension_builder.h"
 
-#if defined OS_CHROMEOS
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 namespace extensions {
 
 const char kExtensionID[] = "eplckmlabaanikjjcgnigddmagoglhmp";
@@ -31,23 +25,11 @@
  protected:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-#if defined OS_CHROMEOS
-    test_user_manager_.reset(new chromeos::ScopedTestUserManager());
-#endif
   }
 
   void TearDown() override {
-#if defined OS_CHROMEOS
-    test_user_manager_.reset();
-#endif
     ChromeRenderViewHostTestHarness::TearDown();
   }
-
-#if defined OS_CHROMEOS
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
-#endif
 };
 
 TEST_F(ActivityLogEnabledTest, NoSwitch) {
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index d0aa3a4..cf22b31 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -33,12 +33,6 @@
 #include "extensions/common/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 namespace {
 
 const char kExtensionId[] = "abc";
@@ -65,9 +59,6 @@
   virtual bool enable_activity_logging_switch() const { return true; }
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-#if defined OS_CHROMEOS
-    test_user_manager_.reset(new chromeos::ScopedTestUserManager());
-#endif
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     if (enable_activity_logging_switch()) {
       base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -82,9 +73,6 @@
   }
 
   void TearDown() override {
-#if defined OS_CHROMEOS
-    test_user_manager_.reset();
-#endif
     base::RunLoop().RunUntilIdle();
     ChromeRenderViewHostTestHarness::TearDown();
   }
@@ -198,12 +186,6 @@
   }
 
   ExtensionService* extension_service_;
-
-#if defined OS_CHROMEOS
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
-#endif
 };
 
 TEST_F(ActivityLogTest, Construct) {
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
index 22297d9..5580e87 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
@@ -18,12 +18,6 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/event_router_factory.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 namespace extensions {
 namespace image_writer {
 
@@ -85,12 +79,6 @@
 
   TestingProfile test_profile_;
   FakeEventRouter* event_router_;
-
-#if defined(OS_CHROMEOS)
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  chromeos::ScopedTestUserManager test_user_manager_;
-#endif
 };
 
 TEST_F(ImageWriterOperationManagerTest, WriteFromFile) {
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 671c980..1119372 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -76,8 +76,8 @@
 #endif  // defined(OS_WIN)
 
 #if defined(USE_ASH)
-#include "ash/shelf/shelf_model.h"  // nogncheck
-#include "ash/shell.h"              // nogncheck
+#include "ash/public/cpp/shelf_model.h"  // nogncheck
+#include "ash/shell.h"                   // nogncheck
 #endif
 
 namespace {
diff --git a/chrome/browser/extensions/chrome_app_icon.cc b/chrome/browser/extensions/chrome_app_icon.cc
index 1b526d2..6c012f73 100644
--- a/chrome/browser/extensions/chrome_app_icon.cc
+++ b/chrome/browser/extensions/chrome_app_icon.cc
@@ -90,7 +90,8 @@
 void ChromeAppIcon::Reload() {
   const Extension* extension = GetExtension();
   icon_ = base::MakeUnique<IconImage>(
-      browser_context_, extension, IconsInfo::GetIcons(extension),
+      browser_context_, extension,
+      extension ? IconsInfo::GetIcons(extension) : ExtensionIconSet(),
       resource_size_in_dip_, util::GetDefaultAppIcon(), this);
   UpdateIcon();
 }
diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc
index 3941ca78..8902bd59 100644
--- a/chrome/browser/extensions/extension_install_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/app_sorting.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 
 using content::WebContents;
@@ -34,6 +35,9 @@
 
 class ExtensionInstallUIBrowserTest : public ExtensionBrowserTest {
  public:
+  ExtensionInstallUIBrowserTest() {}
+  ~ExtensionInstallUIBrowserTest() override {}
+
   // Checks that a theme info bar is currently visible and issues an undo to
   // revert to the previous theme.
   void VerifyThemeInfoBarAndUndoInstall() {
@@ -47,18 +51,29 @@
         infobar_service->infobar_at(0)->delegate()->AsConfirmInfoBarDelegate();
     ASSERT_TRUE(delegate);
     delegate->Cancel();
+    WaitForThemeChange();
     ASSERT_EQ(0U, infobar_service->infobar_count());
   }
 
   // Install the given theme from the data dir and verify expected name.
   void InstallThemeAndVerify(const char* theme_name,
                              const std::string& expected_name) {
-    // If there is already a theme installed, the current theme should be
-    // disabled and the new one installed + enabled.
-    int expected_change = GetTheme() ? 0 : 1;
     const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_name);
-    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, expected_change,
-        browser()));
+    const bool theme_exists = GetTheme();
+    // Themes install asynchronously so we must check the number of enabled
+    // extensions after theme install completes.
+    size_t num_before = extensions::ExtensionRegistry::Get(profile())
+                            ->enabled_extensions()
+                            .size();
+    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, 1, browser()));
+    WaitForThemeChange();
+    size_t num_after = extensions::ExtensionRegistry::Get(profile())
+                           ->enabled_extensions()
+                           .size();
+    // If a theme was already installed, we're just swapping one for another, so
+    // no change in extension count.
+    EXPECT_EQ(num_before + (theme_exists ? 0 : 1), num_after);
+
     const Extension* theme = GetTheme();
     ASSERT_TRUE(theme);
     ASSERT_EQ(theme->name(), expected_name);
@@ -67,6 +82,17 @@
   const Extension* GetTheme() const {
     return ThemeServiceFactory::GetThemeForProfile(browser()->profile());
   }
+
+  void WaitForThemeChange() {
+    content::WindowedNotificationObserver theme_change_observer(
+        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+        content::Source<ThemeService>(
+            ThemeServiceFactory::GetForProfile(browser()->profile())));
+    theme_change_observer.Wait();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExtensionInstallUIBrowserTest);
 };
 
 // Fails on Linux and Windows (http://crbug.com/580907).
@@ -75,6 +101,7 @@
   // Install theme once and undo to verify we go back to default theme.
   base::FilePath theme_crx = PackExtension(test_data_dir_.AppendASCII("theme"));
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 1, browser()));
+  WaitForThemeChange();
   const Extension* theme = GetTheme();
   ASSERT_TRUE(theme);
   std::string theme_id = theme->id();
@@ -83,10 +110,12 @@
 
   // Set the same theme twice and undo to verify we go back to default theme.
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 0, browser()));
+  WaitForThemeChange();
   theme = GetTheme();
   ASSERT_TRUE(theme);
   ASSERT_EQ(theme_id, theme->id());
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 0, browser()));
+  WaitForThemeChange();
   theme = GetTheme();
   ASSERT_TRUE(theme);
   ASSERT_EQ(theme_id, theme->id());
@@ -104,7 +133,7 @@
   // Then install second theme.
   InstallThemeAndVerify("theme2", "snowflake theme");
   const Extension* theme2 = GetTheme();
-  EXPECT_FALSE(theme_id == theme2->id());
+  EXPECT_NE(theme_id, theme2->id());
 
   // Undo second theme will revert to first theme.
   VerifyThemeInfoBarAndUndoInstall();
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc
index 1b810fa..8d91faa8 100644
--- a/chrome/browser/extensions/extension_service_sync_unittest.cc
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/test/mock_entropy_provider.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -28,6 +29,8 @@
 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/sync_helper.h"
@@ -1563,6 +1566,11 @@
   // Installing a theme should not result in a sync change (themes are handled
   // separately by ThemeSyncableService).
   InstallCRX(data_dir().AppendASCII("theme.crx"), INSTALL_NEW);
+  content::WindowedNotificationObserver theme_change_observer(
+      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+      content::Source<ThemeService>(
+          ThemeServiceFactory::GetForProfile(profile())));
+  theme_change_observer.Wait();
   EXPECT_TRUE(processor->changes().empty());
 }
 
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 6e3e7e3..04fcdd5a 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -146,12 +146,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 // The blacklist tests rely on the safe-browsing database.
 #if defined(SAFE_BROWSING_DB_LOCAL)
 #define ENABLE_BLACKLIST_TESTS
@@ -5564,12 +5558,6 @@
   ExtensionErrorReporter::Init(false);  // no noisy errors
   ExtensionsReadyRecorder recorder;
   std::unique_ptr<TestingProfile> profile(new TestingProfile());
-#if defined OS_CHROMEOS
-  chromeos::ScopedTestDeviceSettingsService device_settings_service;
-  chromeos::ScopedTestCrosSettings cros_settings;
-  std::unique_ptr<chromeos::ScopedTestUserManager> user_manager(
-      new chromeos::ScopedTestUserManager);
-#endif
   std::unique_ptr<base::CommandLine> command_line;
   base::FilePath install_dir = profile->GetPath()
       .AppendASCII(extensions::kInstallDirectoryName);
@@ -5586,9 +5574,6 @@
   service->Init();
   content::RunAllBlockingPoolTasksUntilIdle();
   EXPECT_TRUE(recorder.ready());
-#if defined OS_CHROMEOS
-  user_manager.reset();
-#endif
 
   // If either the command line or pref is set, we are disabled.
   recorder.set_ready(false);
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index f775635..e0453fec 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -28,6 +28,9 @@
 #include "extensions/browser/state_store.h"
 #include "extensions/browser/value_store/test_value_store_factory.h"
 #include "extensions/browser/value_store/testing_value_store.h"
+#if defined(OS_CHROMEOS)
+#include "components/user_manager/user_manager.h"
+#endif
 
 using content::BrowserThread;
 
@@ -38,7 +41,12 @@
       store_factory_(new TestValueStoreFactory()),
       info_map_(new InfoMap()),
       quota_service_(new QuotaService()),
-      app_sorting_(new ChromeAppSorting(profile_)) {}
+      app_sorting_(new ChromeAppSorting(profile_)) {
+#if defined(OS_CHROMEOS)
+  if (!user_manager::UserManager::IsInitialized())
+    test_user_manager_.reset(new chromeos::ScopedTestUserManager);
+#endif
+}
 
 TestExtensionSystem::~TestExtensionSystem() {
 }
diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h
index 0a54e6c7..6b1c8008 100644
--- a/chrome/browser/extensions/test_extension_system.h
+++ b/chrome/browser/extensions/test_extension_system.h
@@ -10,6 +10,10 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/one_shot_event.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
+#endif
+
 class Profile;
 class TestingValueStore;
 
@@ -91,6 +95,10 @@
   std::unique_ptr<QuotaService> quota_service_;
   std::unique_ptr<AppSorting> app_sorting_;
   OneShotEvent ready_;
+
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
+#endif
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc
index 16d349d..e432bbdc 100644
--- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc
+++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc
@@ -144,7 +144,9 @@
     const extensions::Extension* previous_theme =
         extension_service_->GetExtensionById(previous_theme_id_, true);
     if (previous_theme) {
-      theme_service_->SetTheme(previous_theme);
+      theme_service_->RevertToTheme(previous_theme);
+      // TODO(estade): while we're waiting to close, it would be nice to
+      // indicate that the theme is busy reverting.
       return false;  // The theme change will close us.
     }
   }
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 090f91b..a887674 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -76,8 +76,8 @@
       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
         content::NotificationService::AllSources());
   InstallExtension("theme2.crx");
-  infobar_added_2.Wait();
   infobar_removed_1.Wait();
+  infobar_added_2.Wait();
   EXPECT_EQ(
       0u,
       InfoBarService::FromWebContents(
diff --git a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
index a12384f..a7723003 100644
--- a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
+++ b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
@@ -52,7 +52,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #endif
@@ -379,7 +378,6 @@
 #if defined(OS_CHROMEOS)
   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
   chromeos::ScopedTestCrosSettings test_cros_settings_;
-  std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
 #endif
 
   MockProfileSharedRenderProcessHostFactory rph_factory_;
@@ -777,10 +775,6 @@
   test_file_system_context_ = new TestMediaFileSystemContext(
       g_browser_process->media_file_system_registry());
 
-#if defined(OS_CHROMEOS)
-  test_user_manager_.reset(new chromeos::ScopedTestUserManager());
-#endif
-
   ASSERT_TRUE(galleries_dir_.CreateUniqueTempDir());
   empty_dir_ = galleries_dir_.GetPath().AppendASCII("empty");
   ASSERT_TRUE(base::CreateDirectory(empty_dir_));
@@ -796,10 +790,6 @@
       g_browser_process->media_file_system_registry();
   EXPECT_EQ(0U, GetExtensionGalleriesHostCount(registry));
 
-#if defined(OS_CHROMEOS)
-  test_user_manager_.reset();
-#endif
-
   // The TestingProfile must be destroyed before the TestingBrowserProcess
   // because it uses it in its destructor.
   ChromeRenderViewHostTestHarness::TearDown();
diff --git a/chrome/browser/notifications/extension_welcome_notification_unittest.cc b/chrome/browser/notifications/extension_welcome_notification_unittest.cc
index a46d881..acaf8e8 100644
--- a/chrome/browser/notifications/extension_welcome_notification_unittest.cc
+++ b/chrome/browser/notifications/extension_welcome_notification_unittest.cc
@@ -139,6 +139,7 @@
     task_runner_ = new base::TestSimpleTaskRunner();
     base::MessageLoop::current()->SetTaskRunner(task_runner_);
     profile_.reset(new TestingProfile());
+    task_runner()->RunUntilIdle();
     delegate_ = new WelcomeNotificationDelegate();
     welcome_notification_.reset(
         ExtensionWelcomeNotification::Create(profile_.get(), delegate_));
diff --git a/chrome/browser/notifications/platform_notification_service_unittest.cc b/chrome/browser/notifications/platform_notification_service_unittest.cc
index d0a79e4..7e34a6b0 100644
--- a/chrome/browser/notifications/platform_notification_service_unittest.cc
+++ b/chrome/browser/notifications/platform_notification_service_unittest.cc
@@ -48,12 +48,6 @@
 #include "extensions/common/value_builder.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 using content::NotificationResources;
 using content::PlatformNotificationData;
 
@@ -344,13 +338,6 @@
 }
 
 TEST_F(PlatformNotificationServiceTest, ExtensionPermissionChecks) {
-#if defined(OS_CHROMEOS)
-  // The ExtensionService on Chrome OS requires these objects to be initialized.
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service;
-  chromeos::ScopedTestCrosSettings test_cros_settings;
-  chromeos::ScopedTestUserManager test_user_manager;
-#endif
-
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   extensions::TestExtensionSystem* test_extension_system =
       static_cast<extensions::TestExtensionSystem*>(
diff --git a/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp b/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp
index 1846c41..1b127ee 100644
--- a/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp
+++ b/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp
@@ -40,6 +40,7 @@
     {
       'target_name': 'keyboard_handler',
       'dependencies': [
+        '<(EXTERNS_GYP):accessibility_private',
         'switch_access_interface',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js b/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
index 901690f..7231a6b 100644
--- a/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/switch_access/keyboard_handler.js
@@ -28,6 +28,10 @@
    * @private
    */
   init_: function() {
+    // Capture keycodes for keys 1 through 4, and 6 through 9.
+    let keyCodes = ['1', '2', '3', '4', '6', '7', '8', '9'].map(
+        key => key.charCodeAt(0));
+    chrome.accessibilityPrivate.setSwitchAccessKeys(keyCodes);
     document.addEventListener('keyup', this.handleKeyEvent_.bind(this));
   },
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index 1baa562b..4c9f799 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -2095,6 +2095,9 @@
 // START: These tests use SafeBrowsingService::Client to directly interact with
 // SafeBrowsingService.
 ///////////////////////////////////////////////////////////////////////////////
+#if defined(GOOGLE_CHROME_BUILD)
+// This test is only enabled when GOOGLE_CHROME_BUILD is true because the store
+// that this test uses is only populated on GOOGLE_CHROME_BUILD builds.
 IN_PROC_BROWSER_TEST_F(V4SafeBrowsingServiceTest, CheckDownloadUrl) {
   GURL badbin_url = embedded_test_server()->GetURL(kMalwareFile);
   std::vector<GURL> badbin_urls(1, badbin_url);
@@ -2112,6 +2115,7 @@
   // Now, the badbin_url is not safe since it is added to download database.
   EXPECT_EQ(SB_THREAT_TYPE_BINARY_MALWARE_URL, client->GetThreatType());
 }
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
 IN_PROC_BROWSER_TEST_F(V4SafeBrowsingServiceTest, CheckUnwantedSoftwareUrl) {
   const GURL bad_url = embedded_test_server()->GetURL(kMalwareFile);
@@ -2186,6 +2190,9 @@
   }
 }
 
+#if defined(GOOGLE_CHROME_BUILD)
+// This test is only enabled when GOOGLE_CHROME_BUILD is true because the store
+// that this test uses is only populated on GOOGLE_CHROME_BUILD builds.
 IN_PROC_BROWSER_TEST_F(V4SafeBrowsingServiceTest, CheckDownloadUrlRedirects) {
   GURL original_url = embedded_test_server()->GetURL(kEmptyPage);
   GURL badbin_url = embedded_test_server()->GetURL(kMalwareFile);
@@ -2208,6 +2215,7 @@
   // Now, the badbin_url is not safe since it is added to download database.
   EXPECT_EQ(SB_THREAT_TYPE_BINARY_MALWARE_URL, client->GetThreatType());
 }
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
 #if defined(GOOGLE_CHROME_BUILD)
 // This test is only enabled when GOOGLE_CHROME_BUILD is true because the store
@@ -2242,7 +2250,7 @@
   client->CheckResourceUrl(embedded_test_server()->GetURL(kEmptyPage));
   EXPECT_EQ(SB_THREAT_TYPE_SAFE, client->GetThreatType());
 }
-#endif
+#endif  // defined(GOOGLE_CHROME_BUILD)
 ///////////////////////////////////////////////////////////////////////////////
 // END: These tests use SafeBrowsingService::Client to directly interact with
 // SafeBrowsingService.
diff --git a/chrome/browser/signin/easy_unlock_service_regular.cc b/chrome/browser/signin/easy_unlock_service_regular.cc
index 83b58ffe..0c3dabce 100644
--- a/chrome/browser/signin/easy_unlock_service_regular.cc
+++ b/chrome/browser/signin/easy_unlock_service_regular.cc
@@ -586,6 +586,9 @@
     const cryptauth::ToggleEasyUnlockResponse& response) {
   cryptauth_client_.reset();
 
+  GetCryptAuthDeviceManager()->ForceSyncNow(
+      cryptauth::InvocationReason::INVOCATION_REASON_FEATURE_TOGGLED);
+  EasyUnlockService::ResetLocalStateForUser(GetAccountId());
   SetRemoteDevices(base::ListValue());
   SetTurnOffFlowStatus(IDLE);
   ReloadAppAndLockScreen();
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
index ed77bc7..4f2c183 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
@@ -6,8 +6,10 @@
 
 #include <stddef.h>
 
+#include <algorithm>
 #include <functional>
 #include <utility>
+#include <vector>
 
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
@@ -15,6 +17,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/spellcheck/browser/spellcheck_host_metrics.h"
 #include "components/spellcheck/common/spellcheck_common.h"
@@ -59,7 +63,6 @@
 // invalid checksum, then returns ChecksumStatus::INVALID and clears |words|.
 ChecksumStatus LoadFile(const base::FilePath& file_path,
                         std::set<std::string>* words) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(words);
   words->clear();
   std::string contents;
@@ -118,7 +121,6 @@
 // called on the file thread.
 std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult>
 LoadDictionaryFileReliably(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   // Load the contents and verify the checksum.
   std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult> result(
       new SpellcheckCustomDictionary::LoadFileResult);
@@ -140,7 +142,6 @@
 // the custom spellcheck dictionary at |path|.
 void SaveDictionaryFileReliably(const base::FilePath& path,
                                 const std::set<std::string>& custom_words) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   std::stringstream content;
   for (const std::string& word : custom_words)
     content << word << '\n';
@@ -155,7 +156,6 @@
     const base::FilePath& path,
     std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult>
         load_file_result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(load_file_result);
   SaveDictionaryFileReliably(path, load_file_result->words);
 }
@@ -212,19 +212,21 @@
     : custom_dictionary_path_(
           dictionary_directory_name.Append(chrome::kCustomDictionaryFileName)),
       is_loaded_(false),
-      weak_ptr_factory_(this) {
-}
+      task_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      weak_ptr_factory_(this) {}
 
 SpellcheckCustomDictionary::~SpellcheckCustomDictionary() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 const std::set<std::string>& SpellcheckCustomDictionary::GetWords() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return words_;
 }
 
 bool SpellcheckCustomDictionary::AddWord(const std::string& word) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<Change> dictionary_change(new Change);
   dictionary_change->AddWord(word);
   int result = dictionary_change->Sanitize(GetWords());
@@ -236,7 +238,7 @@
 }
 
 bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<Change> dictionary_change(new Change);
   dictionary_change->RemoveWord(word);
   int result = dictionary_change->Sanitize(GetWords());
@@ -252,36 +254,36 @@
 }
 
 void SpellcheckCustomDictionary::AddObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(observer);
   observers_.AddObserver(observer);
 }
 
 void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(observer);
   observers_.RemoveObserver(observer);
 }
 
 bool SpellcheckCustomDictionary::IsLoaded() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return is_loaded_;
 }
 
 bool SpellcheckCustomDictionary::IsSyncing() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return !!sync_processor_.get();
 }
 
 void SpellcheckCustomDictionary::Load() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&SpellcheckCustomDictionary::LoadDictionaryFile,
-                 custom_dictionary_path_),
-      base::Bind(&SpellcheckCustomDictionary::OnLoaded,
-                 weak_ptr_factory_.GetWeakPtr()));
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::PostTaskAndReplyWithResult(
+      task_runner_.get(), FROM_HERE,
+      base::BindOnce(&SpellcheckCustomDictionary::LoadDictionaryFile,
+                     custom_dictionary_path_),
+      base::BindOnce(&SpellcheckCustomDictionary::OnLoaded,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 syncer::SyncMergeResult SpellcheckCustomDictionary::MergeDataAndStartSyncing(
@@ -289,7 +291,7 @@
     const syncer::SyncDataList& initial_sync_data,
     std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
     std::unique_ptr<syncer::SyncErrorFactory> sync_error_handler) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!sync_processor_.get());
   DCHECK(!sync_error_handler_.get());
   DCHECK(sync_processor.get());
@@ -323,7 +325,7 @@
 }
 
 void SpellcheckCustomDictionary::StopSyncing(syncer::ModelType type) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(syncer::DICTIONARY, type);
   sync_processor_.reset();
   sync_error_handler_.reset();
@@ -331,7 +333,7 @@
 
 syncer::SyncDataList SpellcheckCustomDictionary::GetAllSyncData(
     syncer::ModelType type) const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(syncer::DICTIONARY, type);
   syncer::SyncDataList data;
   size_t i = 0;
@@ -348,7 +350,7 @@
 syncer::SyncError SpellcheckCustomDictionary::ProcessSyncChanges(
     const tracked_objects::Location& from_here,
     const syncer::SyncChangeList& change_list) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<Change> dictionary_change(new Change);
   for (const syncer::SyncChange& change : change_list) {
     DCHECK(change.IsValid());
@@ -387,7 +389,6 @@
 // static
 std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult>
 SpellcheckCustomDictionary::LoadDictionaryFile(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   std::unique_ptr<LoadFileResult> result = LoadDictionaryFileReliably(path);
   SpellCheckHostMetrics::RecordCustomWordCountStats(result->words.size());
   return result;
@@ -397,7 +398,6 @@
 void SpellcheckCustomDictionary::UpdateDictionaryFile(
     std::unique_ptr<Change> dictionary_change,
     const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(dictionary_change);
 
   if (dictionary_change->empty())
@@ -417,7 +417,7 @@
 
 void SpellcheckCustomDictionary::OnLoaded(
     std::unique_ptr<LoadFileResult> result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(result);
   Change dictionary_change;
   dictionary_change.AddWords(result->words);
@@ -439,7 +439,7 @@
 }
 
 void SpellcheckCustomDictionary::Apply(const Change& dictionary_change) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!dictionary_change.to_add().empty()) {
     words_.insert(dictionary_change.to_add().begin(),
                   dictionary_change.to_add().end());
@@ -450,26 +450,28 @@
 
 void SpellcheckCustomDictionary::FixInvalidFile(
     std::unique_ptr<LoadFileResult> load_file_result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserThread::PostTask(
-      BrowserThread::FILE, FROM_HERE,
-      base::Bind(&SavePassedWordsToDictionaryFileReliably,
-                 custom_dictionary_path_, base::Passed(&load_file_result)));
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SavePassedWordsToDictionaryFileReliably,
+                     custom_dictionary_path_, std::move(load_file_result)));
 }
 
 void SpellcheckCustomDictionary::Save(
     std::unique_ptr<Change> dictionary_change) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   fix_invalid_file_.Cancel();
-  BrowserThread::PostTask(
-      BrowserThread::FILE, FROM_HERE,
-      base::Bind(&SpellcheckCustomDictionary::UpdateDictionaryFile,
-                 base::Passed(&dictionary_change), custom_dictionary_path_));
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SpellcheckCustomDictionary::UpdateDictionaryFile,
+                     std::move(dictionary_change), custom_dictionary_path_));
 }
 
 syncer::SyncError SpellcheckCustomDictionary::Sync(
     const Change& dictionary_change) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   syncer::SyncError error;
   if (!IsSyncing() || dictionary_change.empty())
     return error;
@@ -520,7 +522,7 @@
 }
 
 void SpellcheckCustomDictionary::Notify(const Change& dictionary_change) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!IsLoaded() || dictionary_change.empty())
     return;
   for (Observer& observer : observers_)
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
index d1da70ae..43a8f84 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
@@ -12,8 +12,11 @@
 #include "base/cancelable_callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
 #include "components/spellcheck/browser/spellcheck_dictionary.h"
 #include "components/sync/model/sync_data.h"
 #include "components/sync/model/sync_error.h"
@@ -217,6 +220,10 @@
   // A post-startup task to fix the invalid custom dictionary file.
   base::CancelableClosure fix_invalid_file_;
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
   // Used to create weak pointers for an instance of this class.
   base::WeakPtrFactory<SpellcheckCustomDictionary> weak_ptr_factory_;
 
diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
index 32e04b8..9eff2b3 100644
--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
@@ -13,6 +13,8 @@
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
@@ -21,7 +23,6 @@
 #include "components/spellcheck/browser/spellcheck_platform.h"
 #include "components/spellcheck/common/spellcheck_common.h"
 #include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -34,13 +35,10 @@
 #include "third_party/hunspell/google/bdict.h"
 #endif
 
-using content::BrowserThread;
-
 namespace {
 
 // Close the file.
 void CloseDictionary(base::File file) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   file.Close();
 }
 
@@ -48,8 +46,6 @@
 // returns false.
 bool SaveDictionaryData(std::unique_ptr<std::string> data,
                         const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
   size_t bytes_written =
       base::WriteFile(path, data->data(), data->length());
   if (bytes_written != data->length()) {
@@ -76,15 +72,12 @@
 
 }  // namespace
 
-SpellcheckHunspellDictionary::DictionaryFile::DictionaryFile() {
-}
+SpellcheckHunspellDictionary::DictionaryFile::DictionaryFile() = default;
 
 SpellcheckHunspellDictionary::DictionaryFile::~DictionaryFile() {
   if (file.IsValid()) {
-    BrowserThread::PostTask(
-        BrowserThread::FILE,
-        FROM_HERE,
-        base::Bind(&CloseDictionary, Passed(&file)));
+    base::PostTaskWithTraits(FROM_HERE, {base::MayBlock()},
+                             base::BindOnce(&CloseDictionary, std::move(file)));
   }
 }
 
@@ -109,14 +102,16 @@
       request_context_getter_(request_context_getter),
       spellcheck_service_(spellcheck_service),
       download_status_(DOWNLOAD_NONE),
-      weak_ptr_factory_(this) {
-}
+      task_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      weak_ptr_factory_(this) {}
 
 SpellcheckHunspellDictionary::~SpellcheckHunspellDictionary() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void SpellcheckHunspellDictionary::Load() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   if (spellcheck_platform::SpellCheckerAvailable() &&
@@ -135,11 +130,10 @@
 // Mac falls back on hunspell if its platform spellchecker isn't available.
 // However, Android does not support hunspell.
 #if !defined(OS_ANDROID)
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&InitializeDictionaryLocation, language_),
-      base::Bind(
+  base::PostTaskAndReplyWithResult(
+      task_runner_.get(), FROM_HERE,
+      base::BindOnce(&InitializeDictionaryLocation, language_),
+      base::BindOnce(
           &SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete,
           weak_ptr_factory_.GetWeakPtr()));
 #endif  // !OS_ANDROID
@@ -147,7 +141,7 @@
 
 void SpellcheckHunspellDictionary::RetryDownloadDictionary(
       net::URLRequestContextGetter* request_context_getter) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (dictionary_file_.file.IsValid()) {
     NOTREACHED();
     return;
@@ -173,12 +167,12 @@
 }
 
 void SpellcheckHunspellDictionary::AddObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   observers_.AddObserver(observer);
 }
 
 void SpellcheckHunspellDictionary::RemoveObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   observers_.RemoveObserver(observer);
 }
 
@@ -193,7 +187,7 @@
 void SpellcheckHunspellDictionary::OnURLFetchComplete(
     const net::URLFetcher* source) {
   DCHECK(source);
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<net::URLFetcher> fetcher_destructor(fetcher_.release());
 
   if ((source->GetResponseCode() / 100) != 2) {
@@ -223,14 +217,12 @@
   }
 #endif
 
-  BrowserThread::PostTaskAndReplyWithResult<bool>(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&SaveDictionaryData,
-                 base::Passed(&data),
-                 dictionary_file_.path),
-      base::Bind(&SpellcheckHunspellDictionary::SaveDictionaryDataComplete,
-                 weak_ptr_factory_.GetWeakPtr()));
+  base::PostTaskAndReplyWithResult(
+      task_runner_.get(), FROM_HERE,
+      base::BindOnce(&SaveDictionaryData, std::move(data),
+                     dictionary_file_.path),
+      base::BindOnce(&SpellcheckHunspellDictionary::SaveDictionaryDataComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 GURL SpellcheckHunspellDictionary::GetDictionaryURL() {
@@ -245,7 +237,7 @@
 }
 
 void SpellcheckHunspellDictionary::DownloadDictionary(GURL url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(request_context_getter_);
 
   download_status_ = DOWNLOAD_IN_PROGRESS;
@@ -294,9 +286,10 @@
 // For systemwide installations on Windows, the default directory may not
 // have permissions for download. In that case, the alternate directory for
 // download is chrome::DIR_USER_DATA.
+//
+// static
 SpellcheckHunspellDictionary::DictionaryFile
 SpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DictionaryFile dictionary;
 
 #if defined(OS_WIN)
@@ -340,11 +333,11 @@
 
 // The default place where the spellcheck dictionary resides is
 // chrome::DIR_APP_DICTIONARIES.
+//
+// static
 SpellcheckHunspellDictionary::DictionaryFile
 SpellcheckHunspellDictionary::InitializeDictionaryLocation(
     const std::string& language) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
   // Initialize the BDICT path. Initialization should be in the FILE thread
   // because it checks if there is a "Dictionaries" directory and create it.
   base::FilePath dict_dir;
@@ -357,7 +350,6 @@
 
 void SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete(
     DictionaryFile file) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   dictionary_file_ = std::move(file);
 
   if (!dictionary_file_.file.IsValid()) {
@@ -382,7 +374,7 @@
 
 void SpellcheckHunspellDictionary::SaveDictionaryDataComplete(
     bool dictionary_saved) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (dictionary_saved) {
     download_status_ = DOWNLOAD_NONE;
diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
index 77b22c9..8b0de815 100644
--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
+++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
@@ -11,8 +11,11 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
 #include "components/spellcheck/browser/spellcheck_dictionary.h"
 #include "net/url_request/url_fetcher_delegate.h"
 
@@ -165,6 +168,10 @@
   // Dictionary file path and descriptor.
   DictionaryFile dictionary_file_;
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<SpellcheckHunspellDictionary> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SpellcheckHunspellDictionary);
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index 2afbf90..bbd0b4bf 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -463,8 +463,7 @@
   // Check the received event. Also we check if Chrome has successfully deleted
   // the corrupted dictionary. We delete the corrupted dictionary to avoid
   // leaking it when this test fails.
-  content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
-  content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(SpellcheckService::BDICT_CORRUPTED,
             SpellcheckService::GetStatusEvent());
   base::ThreadRestrictions::ScopedAllowIO allow_io;
diff --git a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
index ce5099f..fa5b140d 100644
--- a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
@@ -4,20 +4,25 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/themes_helper.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
+#include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "content/public/test/test_utils.h"
 
 using themes_helper::GetCustomTheme;
 using themes_helper::GetThemeID;
-using themes_helper::UseCustomTheme;
 using themes_helper::UseDefaultTheme;
 using themes_helper::UseSystemTheme;
 using themes_helper::UsingCustomTheme;
 using themes_helper::UsingDefaultTheme;
 using themes_helper::UsingSystemTheme;
 
+namespace {
+
 class SingleClientThemesSyncTest : public SyncTest {
  public:
   SingleClientThemesSyncTest() : SyncTest(SINGLE_CLIENT) {}
@@ -34,18 +39,18 @@
 IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, CustomTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
-  ASSERT_FALSE(UsingCustomTheme(verifier()));
+  EXPECT_FALSE(UsingCustomTheme(GetProfile(0)));
+  EXPECT_FALSE(UsingCustomTheme(verifier()));
 
-  UseCustomTheme(GetProfile(0), 0);
-  UseCustomTheme(verifier(), 0);
-  ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
-  ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+  SetCustomTheme(GetProfile(0));
+  SetCustomTheme(verifier());
+  EXPECT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+  EXPECT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
-  ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+  EXPECT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+  EXPECT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
 }
 
 // TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
@@ -56,41 +61,43 @@
 #endif  // OS_CHROMEOS
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  UseCustomTheme(GetProfile(0), 0);
-  UseCustomTheme(verifier(), 0);
-  ASSERT_FALSE(UsingSystemTheme(GetProfile(0)));
-  ASSERT_FALSE(UsingSystemTheme(verifier()));
+  SetCustomTheme(GetProfile(0));
+  SetCustomTheme(verifier());
+  EXPECT_FALSE(UsingSystemTheme(GetProfile(0)));
+  EXPECT_FALSE(UsingSystemTheme(verifier()));
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
   UseSystemTheme(GetProfile(0));
   UseSystemTheme(verifier());
-  ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
-  ASSERT_TRUE(UsingSystemTheme(verifier()));
+  EXPECT_TRUE(UsingSystemTheme(GetProfile(0)));
+  EXPECT_TRUE(UsingSystemTheme(verifier()));
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
-  ASSERT_TRUE(UsingSystemTheme(verifier()));
+  EXPECT_TRUE(UsingSystemTheme(GetProfile(0)));
+  EXPECT_TRUE(UsingSystemTheme(verifier()));
 }
 
 IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DefaultTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  UseCustomTheme(GetProfile(0), 0);
-  UseCustomTheme(verifier(), 0);
-  ASSERT_FALSE(UsingDefaultTheme(GetProfile(0)));
-  ASSERT_FALSE(UsingDefaultTheme(verifier()));
+  SetCustomTheme(GetProfile(0));
+  EXPECT_FALSE(UsingDefaultTheme(GetProfile(0)));
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  SetCustomTheme(verifier());
+  EXPECT_FALSE(UsingDefaultTheme(verifier()));
 
+  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
   UseDefaultTheme(GetProfile(0));
+  EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
   UseDefaultTheme(verifier());
-  ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
-  ASSERT_TRUE(UsingDefaultTheme(verifier()));
+  EXPECT_TRUE(UsingDefaultTheme(verifier()));
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
-  ASSERT_TRUE(UsingDefaultTheme(verifier()));
+  EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
+  EXPECT_TRUE(UsingDefaultTheme(verifier()));
 }
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_integration_test_util.cc b/chrome/browser/sync/test/integration/sync_integration_test_util.cc
index 42ef3e24..d9afa063 100644
--- a/chrome/browser/sync/test/integration/sync_integration_test_util.cc
+++ b/chrome/browser/sync/test/integration/sync_integration_test_util.cc
@@ -5,7 +5,20 @@
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/sync/test/integration/themes_helper.h"
+#include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "content/public/test/test_utils.h"
+
+void SetCustomTheme(Profile* profile, int theme_index) {
+  themes_helper::UseCustomTheme(profile, theme_index);
+  content::WindowedNotificationObserver theme_change_observer(
+      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+      content::Source<ThemeService>(
+          ThemeServiceFactory::GetForProfile(profile)));
+  theme_change_observer.Wait();
+}
 
 ServerCountMatchStatusChecker::ServerCountMatchStatusChecker(
     syncer::ModelType type,
diff --git a/chrome/browser/sync/test/integration/sync_integration_test_util.h b/chrome/browser/sync/test/integration/sync_integration_test_util.h
index c3af2d7..63df4c9 100644
--- a/chrome/browser/sync/test/integration/sync_integration_test_util.h
+++ b/chrome/browser/sync/test/integration/sync_integration_test_util.h
@@ -11,10 +11,15 @@
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "components/sync/base/model_type.h"
 
+class Profile;
+
 namespace browser_sync {
 class ProfileSyncService;
 }  // namespace browser_sync
 
+// Sets a custom theme and wait until the asynchronous process is done.
+void SetCustomTheme(Profile* profile, int theme_index = 0);
+
 // Checker to block until the server has a given number of entities.
 class ServerCountMatchStatusChecker
     : public fake_server::FakeServerMatchStatusChecker {
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index 530f209..68beb142 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -38,7 +38,7 @@
   ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
   ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
 
-  UseCustomTheme(GetProfile(0), 0);
+  SetCustomTheme(GetProfile(0));
   ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
 
   // TODO(sync): Add functions to simulate when a pending extension
@@ -57,8 +57,8 @@
                        E2E_ENABLED(CustomThenSyncNative)) {
   ASSERT_TRUE(SetupClients());
 
-  UseCustomTheme(GetProfile(0), 0);
-  UseCustomTheme(GetProfile(1), 0);
+  SetCustomTheme(GetProfile(0));
+  SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
 
@@ -77,8 +77,8 @@
                        E2E_ENABLED(CustomThenSyncDefault)) {
   ASSERT_TRUE(SetupClients());
 
-  UseCustomTheme(GetProfile(0), 0);
-  UseCustomTheme(GetProfile(1), 0);
+  SetCustomTheme(GetProfile(0));
+  SetCustomTheme(GetProfile(1));
 
   ASSERT_TRUE(SetupSync());
 
@@ -97,7 +97,7 @@
 IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  UseCustomTheme(GetProfile(0), 0);
+  SetCustomTheme(GetProfile(0));
 
   ASSERT_TRUE(
       ThemePendingInstallChecker(GetProfile(1), GetCustomTheme(0)).Wait());
@@ -115,7 +115,7 @@
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(1)));
 
-  UseCustomTheme(GetProfile(0), 1);
+  SetCustomTheme(GetProfile(0), 1);
   ASSERT_TRUE(
       ThemePendingInstallChecker(GetProfile(1), GetCustomTheme(1)).Wait());
   EXPECT_EQ(GetCustomTheme(1), GetThemeID(GetProfile(0)));
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index a352c67..4f3da008 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -546,13 +546,13 @@
 }
 
 // static
-scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromExtension(
-    const Extension* extension) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+void BrowserThemePack::BuildFromExtension(
+    const extensions::Extension* extension,
+    scoped_refptr<BrowserThemePack> pack) {
   DCHECK(extension);
   DCHECK(extension->is_theme());
+  DCHECK(!pack->is_valid());
 
-  scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
   pack->BuildHeader(extension);
   pack->BuildTintsFromJSON(extensions::ThemeInfo::GetTints(extension));
   pack->BuildColorsFromJSON(extensions::ThemeInfo::GetColors(extension));
@@ -567,14 +567,14 @@
       &file_paths);
   pack->BuildSourceImagesArray(file_paths);
 
-  if (!pack->LoadRawBitmapsTo(file_paths, &pack->images_on_ui_thread_))
-    return NULL;
+  if (!pack->LoadRawBitmapsTo(file_paths, &pack->images_))
+    return;
 
-  pack->CreateImages(&pack->images_on_ui_thread_);
+  pack->CreateImages(&pack->images_);
 
   // Make sure the |images_on_file_thread_| has bitmaps for supported
   // scale factors before passing to FILE thread.
-  pack->images_on_file_thread_ = pack->images_on_ui_thread_;
+  pack->images_on_file_thread_ = pack->images_;
   for (ImageCache::iterator it = pack->images_on_file_thread_.begin();
        it != pack->images_on_file_thread_.end(); ++it) {
     gfx::ImageSkia* image_skia =
@@ -582,13 +582,13 @@
     image_skia->MakeThreadSafe();
   }
 
-  // Set ThemeImageSource on |images_on_ui_thread_| to resample the source
+  // Set ThemeImageSource on |images_| to resample the source
   // image if a caller of BrowserThemePack::GetImageNamed() requests an
   // ImageSkiaRep for a scale factor not specified by the theme author.
   // Callers of BrowserThemePack::GetImageNamed() to be able to retrieve
   // ImageSkiaReps for all supported scale factors.
-  for (ImageCache::iterator it = pack->images_on_ui_thread_.begin();
-       it != pack->images_on_ui_thread_.end(); ++it) {
+  for (ImageCache::iterator it = pack->images_.begin();
+       it != pack->images_.end(); ++it) {
     const gfx::ImageSkia source_image_skia = it->second.AsImageSkia();
     ThemeImageSource* source = new ThemeImageSource(source_image_skia);
     // image_skia takes ownership of source.
@@ -603,7 +603,7 @@
   }
 
   // The BrowserThemePack is now in a consistent state.
-  return pack;
+  pack->is_valid_ = true;
 }
 
 // static
@@ -671,6 +671,7 @@
                 << "from those supported by platform.";
     return NULL;
   }
+  pack->is_valid_ = true;
   return pack;
 }
 
@@ -683,6 +684,13 @@
   return false;
 }
 
+BrowserThemePack::BrowserThemePack() : CustomThemeSupplier(EXTENSION) {
+  scale_factors_ = ui::GetSupportedScaleFactors();
+  // On Windows HiDPI SCALE_FACTOR_100P may not be supported by default.
+  if (!base::ContainsValue(scale_factors_, ui::SCALE_FACTOR_100P))
+    scale_factors_.push_back(ui::SCALE_FACTOR_100P);
+}
+
 bool BrowserThemePack::WriteToDisk(const base::FilePath& path) const {
   // Add resources for each of the property arrays.
   RawDataForWriting resources;
@@ -771,8 +779,8 @@
     return gfx::Image();
 
   // Check if the image is cached.
-  ImageCache::const_iterator image_iter = images_on_ui_thread_.find(prs_id);
-  if (image_iter != images_on_ui_thread_.end())
+  ImageCache::const_iterator image_iter = images_.find(prs_id);
+  if (image_iter != images_.end())
     return image_iter->second;
 
   ThemeImagePngSource::PngMap png_map;
@@ -786,7 +794,7 @@
     gfx::ImageSkia image_skia(new ThemeImagePngSource(png_map), 1.0f);
     // |image_skia| takes ownership of ThemeImagePngSource.
     gfx::Image ret = gfx::Image(image_skia);
-    images_on_ui_thread_[prs_id] = ret;
+    images_[prs_id] = ret;
     return ret;
   }
 
@@ -830,19 +838,6 @@
 
 // private:
 
-BrowserThemePack::BrowserThemePack()
-    : CustomThemeSupplier(EXTENSION),
-      header_(NULL),
-      tints_(NULL),
-      colors_(NULL),
-      display_properties_(NULL),
-      source_images_(NULL) {
-  scale_factors_ = ui::GetSupportedScaleFactors();
-  // On Windows HiDPI SCALE_FACTOR_100P may not be supported by default.
-  if (!base::ContainsValue(scale_factors_, ui::SCALE_FACTOR_100P))
-    scale_factors_.push_back(ui::SCALE_FACTOR_100P);
-}
-
 void BrowserThemePack::BuildHeader(const Extension* extension) {
   header_ = new BrowserThemePackHeader;
   header_->version = kThemePackVersion;
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h
index cc975cc..d527e973 100644
--- a/chrome/browser/themes/browser_theme_pack.h
+++ b/chrome/browser/themes/browser_theme_pack.h
@@ -53,11 +53,11 @@
 // will trip our IO on the UI thread detector.
 class BrowserThemePack : public CustomThemeSupplier {
  public:
-  // Builds the theme pack from all data from |extension|. This is often done
-  // on a separate thread as it takes so long. This can fail and return NULL in
-  // the case where the theme has invalid data.
-  static scoped_refptr<BrowserThemePack> BuildFromExtension(
-      const extensions::Extension* extension);
+  // Builds the theme from |extension| into |pack|. This may be done on a
+  // separate thread as it takes so long. This can fail in the case where the
+  // theme has invalid data, in which case |pack->is_valid()| will be false.
+  static void BuildFromExtension(const extensions::Extension* extension,
+                                 scoped_refptr<BrowserThemePack> pack);
 
   // Builds the theme pack from a previously performed WriteToDisk(). This
   // operation should be relatively fast, as it should be an mmap() and some
@@ -69,6 +69,11 @@
   // in the data pack.
   static bool IsPersistentImageID(int id);
 
+  // Default. Everything is empty.
+  BrowserThemePack();
+
+  bool is_valid() const { return is_valid_; }
+
   // Builds a data pack on disk at |path| for future quick loading by
   // BuildFromDataPack(). Often (but not always) called from the file thread;
   // implementation should be threadsafe because neither thread will write to
@@ -103,9 +108,6 @@
   // Maps image ids to maps of scale factors to file paths.
   typedef std::map<int, ScaleFactorToFileMap> FilePathMap;
 
-  // Default. Everything is empty.
-  BrowserThemePack();
-
   ~BrowserThemePack() override;
 
   // Builds a header ready to write to disk.
@@ -217,7 +219,7 @@
 
     // theme_id without NULL terminator.
     uint8_t theme_id[16];
-  } *header_;
+  }* header_ = nullptr;
 
   // The remaining structs represent individual entries in an array. For the
   // following three structs, BrowserThemePack will either allocate an array or
@@ -227,21 +229,21 @@
     double h;
     double s;
     double l;
-  } *tints_;
+  }* tints_ = nullptr;
 
   struct ColorPair {
     int32_t id;
     SkColor color;
-  } *colors_;
+  }* colors_ = nullptr;
 
   struct DisplayPropertyPair {
     int32_t id;
     int32_t property;
-  } *display_properties_;
+  }* display_properties_ = nullptr;
 
   // A list of included source images. A pointer to a -1 terminated array of
   // our persistent IDs.
-  int* source_images_;
+  int* source_images_ = nullptr;
 #pragma pack(pop)
 
   // The scale factors represented by the images in the theme pack.
@@ -254,9 +256,8 @@
   RawImages image_memory_;
 
   // Loaded images. These are loaded from |image_memory_|, from |data_pack_|,
-  // and by BuildFromExtension(). These images should only be accessed on the UI
-  // thread.
-  ImageCache images_on_ui_thread_;
+  // and by BuildFromExtension().
+  ImageCache images_;
 
   // Cache of images created in BuildFromExtension(). Once the theme pack is
   // created, this cache should only be accessed on the file thread. There
@@ -264,6 +265,10 @@
   // or vice versa.
   ImageCache images_on_file_thread_;
 
+  // Whether the theme pack has been succesfully initialized and is ready to
+  // use.
+  bool is_valid_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserThemePack);
 };
 
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index 1180081..22e9d2f 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_reader.h"
 #include "base/path_service.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/themes/theme_properties.h"
@@ -34,14 +35,16 @@
 
 class BrowserThemePackTest : public ::testing::Test {
  public:
-  BrowserThemePackTest() {
+  BrowserThemePackTest()
+      : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
+        theme_pack_(new BrowserThemePack()) {
     std::vector<ui::ScaleFactor> scale_factors;
     scale_factors.push_back(ui::SCALE_FACTOR_100P);
     scale_factors.push_back(ui::SCALE_FACTOR_200P);
     scoped_set_supported_scale_factors_.reset(
-      new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
-    theme_pack_ = new BrowserThemePack();
+        new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
   }
+  ~BrowserThemePackTest() override {}
 
   // Transformation for link underline colors.
   SkColor BuildThirdOpacity(SkColor color_link) {
@@ -142,33 +145,22 @@
   }
 
   bool LoadRawBitmapsTo(const TestFilePathMap& out_file_paths) {
-    return theme_pack_->LoadRawBitmapsTo(out_file_paths,
-                                         &theme_pack_->images_on_ui_thread_);
+    return theme_pack_->LoadRawBitmapsTo(out_file_paths, &theme_pack_->images_);
   }
 
   // This function returns void in order to be able use ASSERT_...
   // The BrowserThemePack is returned in |pack|.
   void BuildFromUnpackedExtension(const base::FilePath& extension_path,
-                                  scoped_refptr<BrowserThemePack>& pack) {
-    base::FilePath manifest_path =
-        extension_path.AppendASCII("manifest.json");
-    std::string error;
-    JSONFileValueDeserializer deserializer(manifest_path);
-    std::unique_ptr<base::DictionaryValue> valid_value =
-        base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
-    EXPECT_EQ("", error);
-    ASSERT_TRUE(valid_value.get());
-    scoped_refptr<Extension> extension(
-        Extension::Create(
-            extension_path,
-            extensions::Manifest::INVALID_LOCATION,
-            *valid_value,
-            Extension::REQUIRE_KEY,
-            &error));
-    ASSERT_TRUE(extension.get());
-    ASSERT_EQ("", error);
-    pack = BrowserThemePack::BuildFromExtension(extension.get());
-    ASSERT_TRUE(pack.get());
+                                  scoped_refptr<BrowserThemePack>* pack) {
+    io_waiter_.reset(new base::WaitableEvent(
+        base::WaitableEvent::ResetPolicy::AUTOMATIC,
+        base::WaitableEvent::InitialState::NOT_SIGNALED));
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO, FROM_HERE,
+        base::Bind(&BrowserThemePackTest::DoBuildFromUnpackedExtension,
+                   base::Unretained(this), extension_path, pack));
+    io_waiter_->Wait();
+    ASSERT_TRUE((*pack)->is_valid());
   }
 
   base::FilePath GetStarGazingPath() {
@@ -347,12 +339,33 @@
     }
   }
 
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
-
+ protected:
   typedef std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors>
       ScopedSetSupportedScaleFactors;
   ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
+
+  void DoBuildFromUnpackedExtension(const base::FilePath& extension_path,
+                                    scoped_refptr<BrowserThemePack>* pack) {
+    base::FilePath manifest_path = extension_path.AppendASCII("manifest.json");
+    std::string error;
+    JSONFileValueDeserializer deserializer(manifest_path);
+    std::unique_ptr<base::DictionaryValue> valid_value =
+        base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
+    EXPECT_EQ("", error);
+    ASSERT_TRUE(valid_value.get());
+    scoped_refptr<Extension> extension(Extension::Create(
+        extension_path, extensions::Manifest::INVALID_LOCATION, *valid_value,
+        Extension::REQUIRE_KEY, &error));
+    ASSERT_TRUE(extension.get());
+    ASSERT_EQ("", error);
+    *pack = new BrowserThemePack;
+    BrowserThemePack::BuildFromExtension(extension.get(), *pack);
+    io_waiter_->Signal();
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
   scoped_refptr<BrowserThemePack> theme_pack_;
+  std::unique_ptr<base::WaitableEvent> io_waiter_;
 };
 
 // 'ntp_section' used to correspond to ThemeProperties::COLOR_NTP_SECTION,
@@ -569,7 +582,7 @@
   {
     base::FilePath star_gazing_path = GetStarGazingPath();
     scoped_refptr<BrowserThemePack> pack;
-    BuildFromUnpackedExtension(star_gazing_path, pack);
+    BuildFromUnpackedExtension(star_gazing_path, &pack);
     ASSERT_TRUE(pack->WriteToDisk(file));
     VerifyStarGazing(pack.get());
   }
@@ -593,7 +606,7 @@
   {
     base::FilePath hidpi_path = GetHiDpiThemePath();
     scoped_refptr<BrowserThemePack> pack;
-    BuildFromUnpackedExtension(hidpi_path, pack);
+    BuildFromUnpackedExtension(hidpi_path, &pack);
     ASSERT_TRUE(pack->WriteToDisk(file));
     VerifyHiDpiTheme(pack.get());
   }
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 6a9d0e1..34cd105 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -17,10 +17,12 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/browser_theme_pack.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
@@ -262,7 +264,7 @@
     case extensions::NOTIFICATION_EXTENSION_ENABLED: {
       const Extension* extension = Details<const Extension>(details).ptr();
       if (extension->is_theme())
-        SetTheme(extension);
+        DoSetTheme(extension, true);
       break;
     }
     default:
@@ -271,40 +273,18 @@
 }
 
 void ThemeService::SetTheme(const Extension* extension) {
+  DoSetTheme(extension, false);
+}
+
+void ThemeService::RevertToTheme(const Extension* extension) {
   DCHECK(extension->is_theme());
   ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
-  if (!service->IsExtensionEnabled(extension->id())) {
-    // |extension| is disabled when reverting to the previous theme via an
-    // infobar.
-    service->EnableExtension(extension->id());
-    // Enabling the extension will call back to SetTheme().
-    return;
-  }
-
-  std::string previous_theme_id = GetThemeID();
-
-  // Clear our image cache.
-  FreePlatformCaches();
-
-  BuildFromExtension(extension);
-  SaveThemeID(extension->id());
-
-  NotifyThemeChanged();
-  base::RecordAction(UserMetricsAction("Themes_Installed"));
-
-  if (previous_theme_id != kDefaultThemeID &&
-      previous_theme_id != extension->id() &&
-      service->GetInstalledExtension(previous_theme_id)) {
-    // Do not disable the previous theme if it is already uninstalled. Sending
-    // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
-    // uninstalled when the notification causes the remaining infobar to close
-    // and does not open any new infobars. See crbug.com/468280.
-
-    // Disable the old theme.
-    service->DisableExtension(previous_theme_id,
-                              extensions::Extension::DISABLE_USER_ACTION);
-  }
+  DCHECK(!service->IsExtensionEnabled(extension->id()));
+  // |extension| is disabled when reverting to the previous theme via an
+  // infobar.
+  service->EnableExtension(extension->id());
+  // Enabling the extension will call back to SetTheme().
 }
 
 void ThemeService::UseDefaultTheme() {
@@ -349,8 +329,10 @@
 void ThemeService::OnInfobarDestroyed() {
   number_of_infobars_--;
 
-  if (number_of_infobars_ == 0)
+  if (number_of_infobars_ == 0 &&
+      !build_extension_task_tracker_.HasTrackedTasks()) {
     RemoveUnusedThemes(false);
+  }
 }
 
 void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
@@ -375,8 +357,8 @@
   for (extensions::ExtensionSet::const_iterator it = extensions->begin();
        it != extensions->end(); ++it) {
     const extensions::Extension* extension = it->get();
-    if (extension->is_theme() &&
-        extension->id() != current_theme) {
+    if (extension->is_theme() && extension->id() != current_theme &&
+        extension->id() != building_extension_id_) {
       // Only uninstall themes which are not disabled or are disabled with
       // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
       // themes because externally installed themes are initially disabled.
@@ -578,7 +560,7 @@
 
   // If we don't have a file pack, we're updating from an old version.
   base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
-  if (path != base::FilePath()) {
+  if (!path.empty()) {
     path = path.Append(chrome::kThemePackFilename);
     SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
     if (theme_supplier_)
@@ -685,6 +667,15 @@
   return SkColorSetA(separator_color, alpha);
 }
 
+void ThemeService::DoSetTheme(const Extension* extension,
+                              bool suppress_infobar) {
+  DCHECK(extension->is_theme());
+  DCHECK(extensions::ExtensionSystem::Get(profile_)
+             ->extension_service()
+             ->IsExtensionEnabled(extension->id()));
+  BuildFromExtension(extension, suppress_infobar);
+}
+
 gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id, bool incognito) const {
   gfx::Image image = GetImageNamed(id, incognito);
   if (image.IsEmpty())
@@ -785,10 +776,6 @@
     // be recreated from the extension.
     MigrateTheme();
     set_ready();
-
-    // Send notification in case anyone requested data and cached it when the
-    // theme service was not ready yet.
-    NotifyThemeChanged();
   }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -806,15 +793,18 @@
 }
 
 void ThemeService::MigrateTheme() {
-  // TODO(erg): We need to pop up a dialog informing the user that their
-  // theme is being migrated.
   ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   const Extension* extension =
       service ? service->GetExtensionById(GetThemeID(), false) : nullptr;
   if (extension) {
     DLOG(ERROR) << "Migrating theme";
-    BuildFromExtension(extension);
+    // Theme migration is done on the UI thread. Blocking the UI from appearing
+    // until it's ready is deemed better than showing a blip of the default
+    // theme.
+    scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
+    BrowserThemePack::BuildFromExtension(extension, pack);
+    OnThemeBuiltFromExtension(extension->id(), pack, true);
     base::RecordAction(UserMetricsAction("Themes.Migrated"));
   } else {
     DLOG(ERROR) << "Theme is mysteriously gone.";
@@ -841,10 +831,26 @@
   profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
 }
 
-void ThemeService::BuildFromExtension(const Extension* extension) {
-  scoped_refptr<BrowserThemePack> pack(
-      BrowserThemePack::BuildFromExtension(extension));
-  if (!pack.get()) {
+void ThemeService::BuildFromExtension(const Extension* extension,
+                                      bool suppress_infobar) {
+  build_extension_task_tracker_.TryCancelAll();
+  building_extension_id_ = extension->id();
+  scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
+  auto task_runner = base::CreateTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
+  build_extension_task_tracker_.PostTaskAndReply(
+      task_runner.get(), FROM_HERE,
+      base::Bind(&BrowserThemePack::BuildFromExtension, extension, pack),
+      base::Bind(&ThemeService::OnThemeBuiltFromExtension,
+                 weak_ptr_factory_.GetWeakPtr(), extension->id(), pack,
+                 suppress_infobar));
+}
+
+void ThemeService::OnThemeBuiltFromExtension(
+    const extensions::ExtensionId& extension_id,
+    scoped_refptr<BrowserThemePack> pack,
+    bool suppress_infobar) {
+  if (!pack->is_valid()) {
     // TODO(erg): We've failed to install the theme; perhaps we should tell the
     // user? http://crbug.com/34780
     LOG(ERROR) << "Could not load theme.";
@@ -855,16 +861,57 @@
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   if (!service)
     return;
+  const Extension* extension = extensions::ExtensionRegistry::Get(profile_)
+                                   ->enabled_extensions()
+                                   .GetByID(extension_id);
+  if (!extension)
+    return;
 
   // Write the packed file to disk.
   service->GetFileTaskRunner()->PostTask(
       FROM_HERE, base::Bind(&WritePackToDiskCallback, base::RetainedRef(pack),
                             extension->path()));
 
+  const std::string previous_theme_id = GetThemeID();
+  const bool previous_using_system_theme = UsingSystemTheme();
+
   // Save only the extension path. The packed file will be loaded via
   // LoadThemePrefs().
   SavePackName(extension->path());
   SwapThemeSupplier(pack);
+
+  // Clear our image cache.
+  FreePlatformCaches();
+  SaveThemeID(extension->id());
+  NotifyThemeChanged();
+
+  // Same old theme, but the theme has changed (migrated) or auto-updated.
+  if (previous_theme_id == extension->id())
+    return;
+
+  base::RecordAction(UserMetricsAction("Themes_Installed"));
+
+  bool can_revert_theme = previous_theme_id == kDefaultThemeID;
+  if (previous_theme_id != kDefaultThemeID &&
+      service->GetInstalledExtension(previous_theme_id)) {
+    // Do not disable the previous theme if it is already uninstalled. Sending
+    // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
+    // uninstalled when the notification causes the remaining infobar to close
+    // and does not open any new infobars. See crbug.com/468280.
+
+    // Disable the old theme.
+    service->DisableExtension(previous_theme_id,
+                              extensions::Extension::DISABLE_USER_ACTION);
+
+    can_revert_theme = true;
+  }
+
+  // Offer to revert to the old theme.
+  if (can_revert_theme && !suppress_infobar) {
+    ThemeInstalledInfoBarDelegate::Create(
+        extension, profile_, previous_theme_id, previous_using_system_theme);
+  }
+  building_extension_id_.clear();
 }
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h
index 51c676c..c941a63 100644
--- a/chrome/browser/themes/theme_service.h
+++ b/chrome/browser/themes/theme_service.h
@@ -16,14 +16,17 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "build/build_config.h"
 #include "chrome/common/features.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/common/extension_id.h"
 #include "extensions/features/features.h"
 #include "ui/base/theme_provider.h"
 
+class BrowserThemePack;
 class CustomThemeSupplier;
 class ThemeSyncableService;
 class Profile;
@@ -82,6 +85,9 @@
   // ExtensionService.
   virtual void SetTheme(const extensions::Extension* extension);
 
+  // Similar to SetTheme, but doesn't show an undo infobar.
+  void RevertToTheme(const extensions::Extension* extension);
+
   // Reset the theme to default.
   virtual void UseDefaultTheme();
 
@@ -230,6 +236,9 @@
   // and contrasting with the foreground tab is the most important).
   static SkColor GetSeparatorColor(SkColor tab_color, SkColor frame_color);
 
+  void DoSetTheme(const extensions::Extension* extension,
+                  bool suppress_infobar);
+
   // These methods provide the implementation for ui::ThemeProvider (exposed
   // via BrowserThemeProvider).
   gfx::ImageSkia* GetImageSkiaNamed(int id, bool incognito) const;
@@ -270,8 +279,15 @@
   void SaveThemeID(const std::string& id);
 
   // Implementation of SetTheme() (and the fallback from LoadThemePrefs() in
-  // case we don't have a theme pack).
-  void BuildFromExtension(const extensions::Extension* extension);
+  // case we don't have a theme pack). |new_theme| indicates whether this is a
+  // newly installed theme or a migration.
+  void BuildFromExtension(const extensions::Extension* extension,
+                          bool new_theme);
+
+  // Callback when |pack| has finished or failed building.
+  void OnThemeBuiltFromExtension(const extensions::ExtensionId& extension_id,
+                                 scoped_refptr<BrowserThemePack> pack,
+                                 bool new_theme);
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // Returns true if the profile belongs to a supervised user.
@@ -328,6 +344,14 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // Allows us to cancel building a theme pack from an extension.
+  base::CancelableTaskTracker build_extension_task_tracker_;
+
+  // The ID of the theme that's currently being built on a different thread.
+  // We hold onto this just to be sure not to uninstall the extension view
+  // RemoveUnusedThemes while it's still being built.
+  std::string building_extension_id_;
+
   base::WeakPtrFactory<ThemeService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ThemeService);
diff --git a/chrome/browser/themes/theme_service_browsertest.cc b/chrome/browser/themes/theme_service_browsertest.cc
index 7ed17415..88de8b3 100644
--- a/chrome/browser/themes/theme_service_browsertest.cc
+++ b/chrome/browser/themes/theme_service_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_restrictions.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/test/test_utils.h"
 
 namespace {
 
@@ -58,7 +60,11 @@
   EXPECT_EQ(base::FilePath(),
             profile->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename));
 
+  content::WindowedNotificationObserver theme_change_observer(
+      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+      content::Source<ThemeService>(theme_service));
   InstallExtension(test_data_dir_.AppendASCII("theme"), 1);
+  theme_change_observer.Wait();
 
   // Check that the theme was installed.
   EXPECT_TRUE(UsingCustomTheme(*theme_service));
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 7f6807ee..83114e6 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -77,8 +78,7 @@
     installer->Load(temp_dir);
     std::string extension_id = observer.WaitForExtensionLoaded()->id();
 
-    // Let the ThemeService finish creating the theme pack.
-    base::RunLoop().RunUntilIdle();
+    WaitForThemeInstall();
 
     return extension_id;
   }
@@ -111,6 +111,14 @@
     return theme_service->get_theme_supplier();
   }
 
+  void WaitForThemeInstall() {
+    content::WindowedNotificationObserver theme_change_observer(
+        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+        content::Source<ThemeService>(
+            ThemeServiceFactory::GetForProfile(profile())));
+    theme_change_observer.Wait();
+  }
+
   // Alpha blends a non-opaque foreground color against an opaque background.
   // This is not the same as color_utils::AlphaBlend() since it gets the opacity
   // from the foreground color and then does not blend the two colors' alpha
@@ -163,8 +171,6 @@
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(profile_.get());
   theme_service->UseDefaultTheme();
-  // Let the ThemeService uninstall unused themes.
-  base::RunLoop().RunUntilIdle();
 
   base::ScopedTempDir temp_dir1;
   ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
@@ -188,19 +194,19 @@
 
   // 2) Enabling a disabled theme extension should swap the current theme.
   service_->EnableExtension(extension1_id);
-  base::RunLoop().RunUntilIdle();
+  WaitForThemeInstall();
   EXPECT_EQ(extension1_id, theme_service->GetThemeID());
   EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
   EXPECT_TRUE(registry_->GetExtensionById(extension2_id,
                                           ExtensionRegistry::DISABLED));
 
-  // 3) Using SetTheme() with a disabled theme should enable and set the
+  // 3) Using RevertToTheme() with a disabled theme should enable and set the
   // theme. This is the case when the user reverts to the previous theme
   // via an infobar.
   const extensions::Extension* extension2 =
       service_->GetInstalledExtension(extension2_id);
-  theme_service->SetTheme(extension2);
-  base::RunLoop().RunUntilIdle();
+  theme_service->RevertToTheme(extension2);
+  WaitForThemeInstall();
   EXPECT_EQ(extension2_id, theme_service->GetThemeID());
   EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
   EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
@@ -340,18 +346,21 @@
   // Show an infobar.
   theme_service->OnInfobarDisplayed();
 
-  // Install another theme. Emulate the infobar destroying itself (and
-  // causing unused themes to be uninstalled) as a result of the
-  // NOTIFICATION_BROWSER_THEME_CHANGED notification.
+  // Install another theme. The first extension shouldn't be uninstalled yet as
+  // it should be possible to revert to it. Emulate the infobar destroying
+  // itself as a result of the NOTIFICATION_BROWSER_THEME_CHANGED notification.
   {
     InfobarDestroyerOnThemeChange destroyer(profile_.get());
     const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.GetPath());
-    ASSERT_EQ(extension2_id, theme_service->GetThemeID());
-    ASSERT_FALSE(service_->GetInstalledExtension(extension1_id));
+    EXPECT_EQ(extension2_id, theme_service->GetThemeID());
   }
 
+  auto* extension1 = service_->GetInstalledExtension(extension1_id);
+  ASSERT_TRUE(extension1);
+
   // Check that it is possible to reinstall extension1.
-  ASSERT_EQ(extension1_id, LoadUnpackedThemeAt(temp_dir1.GetPath()));
+  ThemeServiceFactory::GetForProfile(profile_.get())->RevertToTheme(extension1);
+  WaitForThemeInstall();
   EXPECT_EQ(extension1_id, theme_service->GetThemeID());
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 630148f..374719ef 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1438,6 +1438,10 @@
       "autofill/save_card_bubble_controller_impl.h",
       "autofill/save_card_bubble_view.h",
       "desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h",
+
+      # This test header is included because it contains forward declarations
+      # needed for "friend" statements for use in tests.
+      "translate/translate_bubble_test_utils.h",
       "views/apps/app_info_dialog/app_info_dialog_container.cc",
       "views/apps/app_info_dialog/app_info_dialog_container.h",
       "views/apps/app_info_dialog/app_info_dialog_views.cc",
@@ -1596,6 +1600,8 @@
       "views/task_manager_view.h",
       "views/toolbar/toolbar_actions_bar_bubble_views.cc",
       "views/toolbar/toolbar_actions_bar_bubble_views.h",
+      "views/translate/translate_bubble_view.cc",
+      "views/translate/translate_bubble_view.h",
       "views/update_recommended_message_box.cc",
       "views/update_recommended_message_box.h",
     ]
@@ -1687,10 +1693,6 @@
     if (!is_mac || mac_views_browser) {
       sources += [
         "javascript_dialogs/javascript_dialog.cc",
-
-        # This test header is included because it contains forward declarations
-        # needed for "friend" statements for use in tests.
-        "translate/translate_bubble_test_utils.h",
         "views/accessibility/invert_bubble_view.cc",
         "views/accessibility/invert_bubble_view.h",
         "views/autofill/autofill_popup_base_view.cc",
@@ -1917,8 +1919,6 @@
         "views/toolbar/toolbar_view.cc",
         "views/toolbar/toolbar_view.h",
         "views/touch_uma/touch_uma.h",
-        "views/translate/translate_bubble_view.cc",
-        "views/translate/translate_bubble_view.h",
         "views/translate/translate_icon_view.cc",
         "views/translate/translate_icon_view.h",
         "views/validation_message_bubble_view.cc",
@@ -3031,6 +3031,8 @@
         "cocoa/toolbar/toolbar_controller.mm",
         "cocoa/toolbar/toolbar_view_cocoa.h",
         "cocoa/toolbar/toolbar_view_cocoa.mm",
+        "cocoa/translate/translate_bubble_bridge_views.h",
+        "cocoa/translate/translate_bubble_bridge_views.mm",
         "cocoa/translate/translate_bubble_controller.h",
         "cocoa/translate/translate_bubble_controller.mm",
         "cocoa/url_drop_target.h",
@@ -3113,6 +3115,8 @@
       "webui/cleanup_tool/cleanup_action_handler.h",
       "webui/cleanup_tool/cleanup_tool_ui.cc",
       "webui/cleanup_tool/cleanup_tool_ui.h",
+      "webui/conflicts_handler.cc",
+      "webui/conflicts_handler.h",
       "webui/conflicts_ui.cc",
       "webui/conflicts_ui.h",
       "webui/welcome_win10_handler.cc",
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index 27db6fd..aae349ce 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/ui/app_list/launcher_page_event_dispatcher.h"
 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
-#include "chrome/browser/ui/app_list/search_answer_web_contents_delegate.h"
 #include "chrome/browser/ui/app_list/start_page_service.h"
 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
@@ -154,7 +153,6 @@
       start_page_service->RemoveObserver(this);
     app_sync_ui_state_watcher_.reset();
     model_ = NULL;
-    search_answer_delegate_.reset();
   }
 
   template_url_service_observer_.RemoveAll();
@@ -188,10 +186,6 @@
     app_sync_ui_state_watcher_.reset(
         new AppSyncUIStateWatcher(profile_, model_));
 
-    search_answer_delegate_ =
-        base::MakeUnique<app_list::SearchAnswerWebContentsDelegate>(profile_,
-                                                                    model_);
-
     SetUpSearchUI();
     SetUpCustomLauncherPages();
     OnTemplateURLServiceChanged();
@@ -279,8 +273,6 @@
     search_controller_->Start();
     controller_->OnSearchStarted();
   }
-  if (search_answer_delegate_)
-    search_answer_delegate_->Update();
 }
 
 void AppListViewDelegate::StopSearch() {
@@ -495,10 +487,6 @@
   return web_views;
 }
 
-views::View* AppListViewDelegate::GetSearchAnswerWebView() {
-  return search_answer_delegate_->web_view();
-}
-
 void AppListViewDelegate::CustomLauncherPageAnimationChanged(double progress) {
   if (launcher_page_event_dispatcher_)
     launcher_page_event_dispatcher_->ProgressChanged(progress);
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h
index 923bf6b..0a5d7540 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.h
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.h
@@ -26,7 +26,6 @@
 namespace app_list {
 class CustomLauncherPageContents;
 class LauncherPageEventDispatcher;
-class SearchAnswerWebContentsDelegate;
 class SearchController;
 class SearchResourceManager;
 class SpeechUIModel;
@@ -80,7 +79,6 @@
   views::View* CreateStartPageWebView(const gfx::Size& size) override;
   std::vector<views::View*> CreateCustomPageWebViews(
       const gfx::Size& size) override;
-  views::View* GetSearchAnswerWebView() override;
   void CustomLauncherPageAnimationChanged(double progress) override;
   void CustomLauncherPagePopSubpage() override;
   bool IsSpeechRecognitionEnabled() override;
@@ -144,9 +142,6 @@
   // Registers for NOTIFICATION_APP_TERMINATING to unload custom launcher pages.
   content::NotificationRegistrar registrar_;
 
-  std::unique_ptr<app_list::SearchAnswerWebContentsDelegate>
-      search_answer_delegate_;
-
   DISALLOW_COPY_AND_ASSIGN(AppListViewDelegate);
 };
 
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 513a989..911b8dba 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -15,15 +15,17 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/app_search_provider.h"
 #include "chrome/browser/ui/app_list/search/history_factory.h"
+#include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h"
 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
 #include "chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider.h"
 #include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h"
+#include "chrome/browser/ui/app_list/search_answer_web_contents_delegate.h"
 #include "chrome/common/chrome_switches.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/search/mixer.h"
 #include "ui/app_list/search_controller.h"
-#include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h"
 
 namespace app_list {
 
@@ -68,6 +70,9 @@
   size_t apps_group_id = controller->AddGroup(kMaxAppsGroupResults, 1.0);
   size_t omnibox_group_id = controller->AddGroup(kMaxOmniboxResults, 1.0);
   size_t webstore_group_id = controller->AddGroup(kMaxWebstoreResults, 0.4);
+  // Multiplier 100 is used because the answer card is designed to be the most
+  // relevant result and must be on the top of the result list.
+  size_t answer_card_group_id = controller->AddGroup(1, 100.0);
 
   // Add search providers.
   controller->AddProvider(
@@ -81,6 +86,11 @@
   controller->AddProvider(
       webstore_group_id,
       base::MakeUnique<WebstoreProvider>(profile, list_controller));
+  if (features::IsAnswerCardEnabled()) {
+    controller->AddProvider(
+        answer_card_group_id,
+        base::MakeUnique<SearchAnswerWebContentsDelegate>(profile, model));
+  }
   if (IsSuggestionsSearchProviderEnabled()) {
     size_t suggestions_group_id =
         controller->AddGroup(kMaxSuggestionsResults, 1.0);
diff --git a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc
index 2246036a..47a56da 100644
--- a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc
+++ b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc
@@ -21,6 +21,7 @@
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search_box_model.h"
+#include "ui/app_list/search_result.h"
 #include "ui/views/controls/webview/web_contents_set_background_color.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/widget/widget.h"
@@ -52,7 +53,7 @@
   void VisibilityChanged(View* starting_from, bool is_visible) override {
     WebView::VisibilityChanged(starting_from, is_visible);
 
-    if (GetWidget()->IsVisible() && IsDrawn()) {
+    if (GetWidget() && GetWidget()->IsVisible() && IsDrawn()) {
       if (shown_time_.is_null())
         shown_time_ = base::TimeTicks::Now();
     } else {
@@ -73,6 +74,34 @@
   DISALLOW_COPY_AND_ASSIGN(SearchAnswerWebView);
 };
 
+class SearchAnswerResult : public SearchResult {
+ public:
+  SearchAnswerResult(Profile* profile,
+                     const std::string& result_url,
+                     views::View* web_view)
+      : profile_(profile) {
+    set_display_type(DISPLAY_CARD);
+    set_id(result_url);
+    set_relevance(1);
+    set_view(web_view);
+  }
+
+  // SearchResult overrides:
+  std::unique_ptr<SearchResult> Duplicate() const override {
+    return base::MakeUnique<SearchAnswerResult>(profile_, id(), view());
+  }
+
+  void Open(int event_flags) override {
+    chrome::NavigateParams params(profile_, GURL(id()),
+                                  ui::PAGE_TRANSITION_GENERATED);
+    params.disposition = ui::DispositionFromEventFlags(event_flags);
+    chrome::Navigate(&params);
+  }
+
+ private:
+  Profile* const profile_;
+};
+
 }  // namespace
 
 SearchAnswerWebContentsDelegate::SearchAnswerWebContentsDelegate(
@@ -97,10 +126,6 @@
   web_contents_->SetDelegate(this);
   web_view_->set_owned_by_client();
   web_view_->SetWebContents(web_contents_.get());
-  if (features::IsAnswerCardDarkRunEnabled())
-    web_view_->SetFocusBehavior(views::View::FocusBehavior::NEVER);
-
-  model->AddObserver(this);
 
   // Make the webview transparent since it's going to be shown on top of a
   // highlightable button.
@@ -110,25 +135,22 @@
 
 SearchAnswerWebContentsDelegate::~SearchAnswerWebContentsDelegate() {
   RecordReceivedAnswerFinalResult();
-  model_->RemoveObserver(this);
 }
 
 views::View* SearchAnswerWebContentsDelegate::web_view() {
   return web_view_.get();
 }
 
-void SearchAnswerWebContentsDelegate::Update() {
-  if (!answer_server_url_.is_valid())
-    return;
-
+void SearchAnswerWebContentsDelegate::Start(bool is_voice_query,
+                                            const base::string16& query) {
   RecordReceivedAnswerFinalResult();
   // Reset the state.
   received_answer_ = false;
-  model_->SetSearchAnswerAvailable(false);
+  OnResultAvailable(false);
   current_request_url_ = GURL();
   server_request_start_time_ = answer_loaded_time_ = base::TimeTicks();
 
-  if (model_->search_box()->is_voice_query()) {
+  if (is_voice_query) {
     // No need to send a server request and show a card because launcher
     // automatically closes upon voice queries.
     return;
@@ -137,12 +159,11 @@
   if (!model_->search_engine_is_google())
     return;
 
-  // Start a request to the answer server.
-  base::string16 query;
-  base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
   if (query.empty())
     return;
 
+  // Start a request to the answer server.
+
   // Lifetime of |prefixed_query| should be longer than the one of
   // |replacements|.
   base::string16 prefixed_query(base::UTF8ToUTF16("q=") + query);
@@ -164,8 +185,8 @@
 void SearchAnswerWebContentsDelegate::UpdatePreferredSize(
     content::WebContents* web_contents,
     const gfx::Size& pref_size) {
-  model_->SetSearchAnswerAvailable(received_answer_ && IsCardSizeOk() &&
-                                   !web_contents_->IsLoading());
+  OnResultAvailable(received_answer_ && IsCardSizeOk() &&
+                    !web_contents_->IsLoading());
   web_view_->SetPreferredSize(pref_size);
   if (!answer_loaded_time_.is_null()) {
     UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime",
@@ -223,10 +244,7 @@
   }
 
   if (!features::IsAnswerCardDarkRunEnabled()) {
-    const net::HttpResponseHeaders* headers =
-        navigation_handle->GetResponseHeaders();
-    if (!headers || headers->response_code() != net::HTTP_OK ||
-        !headers->HasHeaderValue("has_answer", "true")) {
+    if (!ParseResponseHeaders(navigation_handle->GetResponseHeaders())) {
       RecordRequestResult(SearchAnswerRequestResult::REQUEST_RESULT_NO_ANSWER);
       return;
     }
@@ -235,6 +253,8 @@
     dark_run_received_answer_ = !dark_run_received_answer_;
     if (!dark_run_received_answer_)
       return;
+    // SearchResult requires a non-empty id. This "url" will never be opened.
+    result_url_ = "some string";
   }
 
   received_answer_ = true;
@@ -247,7 +267,7 @@
     return;
 
   if (IsCardSizeOk())
-    model_->SetSearchAnswerAvailable(true);
+    OnResultAvailable(true);
   answer_loaded_time_ = base::TimeTicks::Now();
   UMA_HISTOGRAM_TIMES("SearchAnswer.LoadingTime",
                       answer_loaded_time_ - server_request_start_time_);
@@ -259,11 +279,6 @@
   base::RecordAction(base::UserMetricsAction("SearchAnswer_UserInteraction"));
 }
 
-void SearchAnswerWebContentsDelegate::OnSearchEngineIsGoogleChanged(
-    bool is_google) {
-  Update();
-}
-
 bool SearchAnswerWebContentsDelegate::IsCardSizeOk() const {
   if (features::IsAnswerCardDarkRunEnabled())
     return true;
@@ -287,4 +302,25 @@
                            REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE);
 }
 
+void SearchAnswerWebContentsDelegate::OnResultAvailable(bool is_available) {
+  SearchProvider::Results results;
+  if (is_available) {
+    results.reserve(1);
+    results.emplace_back(base::MakeUnique<SearchAnswerResult>(
+        profile_, result_url_, web_view_.get()));
+  }
+  SwapResults(&results);
+}
+
+bool SearchAnswerWebContentsDelegate::ParseResponseHeaders(
+    const net::HttpResponseHeaders* headers) {
+  if (!headers || headers->response_code() != net::HTTP_OK)
+    return false;
+  if (!headers->HasHeaderValue("SearchAnswer-HasResult", "true"))
+    return false;
+  if (!headers->GetNormalizedHeader("SearchAnswer-OpenResultUrl", &result_url_))
+    return false;
+  return true;
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.h b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.h
index b56c8531..ebbe661 100644
--- a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.h
+++ b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.h
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "ui/app_list/app_list_model_observer.h"
+#include "ui/app_list/search_provider.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -20,6 +20,10 @@
 class AppListModel;
 }
 
+namespace net {
+class HttpResponseHeaders;
+}
+
 namespace views {
 class View;
 class WebView;
@@ -27,25 +31,25 @@
 
 namespace app_list {
 
-// Manages the web contents for the search answer web view.
-class SearchAnswerWebContentsDelegate : public content::WebContentsDelegate,
-                                        public content::WebContentsObserver,
-                                        public AppListModelObserver {
+// Search provider for the answer card.
+class SearchAnswerWebContentsDelegate : public SearchProvider,
+                                        public content::WebContentsDelegate,
+                                        public content::WebContentsObserver {
  public:
   SearchAnswerWebContentsDelegate(Profile* profile,
                                   app_list::AppListModel* model);
 
   ~SearchAnswerWebContentsDelegate() override;
 
-  // Updates the state after the query string or any other relevant condition
-  // changes.
-  void Update();
-
   // Returns a pointer to the web view for the web contents managed by this
   // class. The object is owned by this class and has property
   // 'set_owned_by_client()' set.
   views::View* web_view();
 
+  // SearchProvider overrides:
+  void Start(bool is_voice_query, const base::string16& query) override;
+  void Stop() override {}
+
   // content::WebContentsDelegate overrides:
   void UpdatePreferredSize(content::WebContents* web_contents,
                            const gfx::Size& pref_size) override;
@@ -62,12 +66,11 @@
   void DidStopLoading() override;
   void DidGetUserInteraction(const blink::WebInputEvent::Type type) override;
 
-  // AppListModelObserver overrides:
-  void OnSearchEngineIsGoogleChanged(bool is_google) override;
-
  private:
   bool IsCardSizeOk() const;
   void RecordReceivedAnswerFinalResult();
+  void OnResultAvailable(bool is_available);
+  bool ParseResponseHeaders(const net::HttpResponseHeaders* headers);
 
   // Unowned pointer to the associated profile.
   Profile* const profile_;
@@ -101,6 +104,9 @@
   // response contains an answer.
   bool dark_run_received_answer_ = false;
 
+  // Url to open when the user clicks at the result.
+  std::string result_url_;
+
   DISALLOW_COPY_AND_ASSIGN(SearchAnswerWebContentsDelegate);
 };
 
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index 2111f5e..619d53c 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -294,7 +294,7 @@
           web_contents, security_origin, type, extension);
 }
 
-int ChromeAppDelegate::PreferredIconSize() {
+int ChromeAppDelegate::PreferredIconSize() const {
 #if defined(USE_ASH)
   return ash::kShelfSize;
 #else
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.h b/chrome/browser/ui/apps/chrome_app_delegate.h
index 3d687f4..31ec3de 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.h
+++ b/chrome/browser/ui/apps/chrome_app_delegate.h
@@ -63,7 +63,7 @@
       const GURL& security_origin,
       content::MediaStreamType type,
       const extensions::Extension* extension) override;
-  int PreferredIconSize() override;
+  int PreferredIconSize() const override;
   void SetWebContentsBlocked(content::WebContents* web_contents,
                              bool blocked) override;
   bool IsWebContentsVisible(content::WebContents* web_contents) override;
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
index 41d5953..e5a924c6 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
@@ -27,7 +27,7 @@
   aura::Window* window = app_window->GetNativeWindow();
   if (window)
     observed_windows_.Add(window);
-  UpdateLauncherItem();
+  UpdateShelfItemIcon();
 }
 
 AppWindowLauncherItemController::WindowList::iterator
@@ -51,7 +51,7 @@
     return;
   }
   windows_.erase(iter);
-  UpdateLauncherItem();
+  UpdateShelfItemIcon();
 }
 
 ui::BaseWindow* AppWindowLauncherItemController::GetAppWindow(
@@ -66,7 +66,7 @@
   ui::BaseWindow* app_window = GetAppWindow(window);
   if (app_window)
     last_active_window_ = app_window;
-  UpdateLauncherItem();
+  UpdateShelfItemIcon();
 }
 
 AppWindowLauncherItemController*
@@ -143,6 +143,8 @@
       status = ash::STATUS_RUNNING;
     }
     ChromeLauncherController::instance()->SetItemStatus(shelf_id(), status);
+  } else if (key == aura::client::kAppIconKey) {
+    UpdateShelfItemIcon();
   }
 }
 
@@ -174,3 +176,25 @@
   }
   return ash::SHELF_ACTION_NONE;
 }
+
+void AppWindowLauncherItemController::UpdateShelfItemIcon() {
+  // Set the shelf item icon from the kAppIconKey property of the current
+  // (or most recently) active window. If there is no valid icon, ask
+  // ChromeLauncherController to update the icon.
+  const gfx::ImageSkia* app_icon = nullptr;
+  ui::BaseWindow* last_active_window = GetLastActiveWindow();
+  if (last_active_window && last_active_window->GetNativeWindow()) {
+    app_icon = last_active_window->GetNativeWindow()->GetProperty(
+        aura::client::kAppIconKey);
+  }
+  // TODO(khmel): Remove using image_set_by_controller
+  if (app_icon && !app_icon->isNull()) {
+    set_image_set_by_controller(true);
+    ChromeLauncherController::instance()->SetLauncherItemImage(shelf_id(),
+                                                               *app_icon);
+  } else if (image_set_by_controller()) {
+    set_image_set_by_controller(false);
+    ChromeLauncherController::instance()->UpdateLauncherItemImage(
+        shelf_id().app_id);
+  }
+}
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
index e1fd876..791a18a 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
@@ -60,11 +60,6 @@
   // Activates the window at position |index|.
   void ActivateIndexedApp(size_t index);
 
-  // Called when launcher item may need to be updated, eg. label or icon.
-  // TODO(khmel): Use aura::Window property and observe property change
-  // http://crbug.com/724292
-  virtual void UpdateLauncherItem() {}
-
   const WindowList& windows() const { return windows_; }
 
  protected:
@@ -87,6 +82,10 @@
  private:
   WindowList::iterator GetFromNativeWindow(aura::Window* window);
 
+  // Handles the case when the app window in this controller has been changed,
+  // and sets the new controller icon based on the currently active window.
+  void UpdateShelfItemIcon();
+
   // List of associated app windows
   WindowList windows_;
 
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
index 97c20fc..3cd9cbd3 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h"
 
-#include "ash/shelf/shelf_model.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -116,7 +116,7 @@
   app_controller_map_.erase(it);
   if (need_close_item)
     owner_->CloseLauncherItem(shelf_id);
-  UpdateApp(safe_app_id);
+  UpdateShelfItemIcon(safe_app_id);
 }
 
 void ArcAppDeferredLauncherController::OnAppReadyChanged(
@@ -163,10 +163,9 @@
   return it->second->GetActiveTime();
 }
 
-void ArcAppDeferredLauncherController::UpdateApp(const std::string& app_id) {
-  AppIconLoader* icon_loader = owner_->GetAppIconLoaderForApp(app_id);
-  if (icon_loader)
-    icon_loader->UpdateImage(app_id);
+void ArcAppDeferredLauncherController::UpdateShelfItemIcon(
+    const std::string& app_id) {
+  owner_->UpdateLauncherItemImage(app_id);
 }
 
 void ArcAppDeferredLauncherController::UpdateApps() {
@@ -175,7 +174,7 @@
 
   RegisterNextUpdate();
   for (const auto pair : app_controller_map_)
-    UpdateApp(pair.first);
+    UpdateShelfItemIcon(pair.first);
 }
 
 void ArcAppDeferredLauncherController::RegisterNextUpdate() {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h
index 02e6145..3f8fe10 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h
@@ -62,7 +62,7 @@
       std::map<std::string, ArcAppDeferredLauncherItemController*>;
 
   void UpdateApps();
-  void UpdateApp(const std::string& app_id);
+  void UpdateShelfItemIcon(const std::string& app_id);
   void RegisterNextUpdate();
 
   // Unowned pointers.
diff --git a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
index e4cd3745..22bf4c8 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/cpp/shelf_item_delegate.h"
-#include "ash/shelf/shelf_model.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/wm/window_util.h"
 #include "base/macros.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window.cc b/chrome/browser/ui/ash/launcher/arc_app_window.cc
index d47bf54..cc201ce 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window.cc
@@ -14,6 +14,7 @@
 #include "ui/aura/window.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
 
 ArcAppWindow::ArcAppWindow(int task_id,
@@ -33,8 +34,6 @@
     ArcAppWindowLauncherItemController* controller) {
   DCHECK(!controller_ || !controller);
   controller_ = controller;
-  if (controller_)
-    controller_->UpdateLauncherItem();
 }
 
 void ArcAppWindow::SetFullscreenMode(FullScreenMode mode) {
@@ -49,7 +48,7 @@
     GetNativeWindow()->SetTitle(base::UTF8ToUTF16(title));
   ImageDecoder::Cancel(this);
   if (unsafe_icon_data_png.empty()) {
-    ResetIcon();
+    SetIcon(gfx::ImageSkia());
     return;
   }
 
@@ -157,21 +156,25 @@
   NOTREACHED();
 }
 
-void ArcAppWindow::ResetIcon() {
-  if (icon_.isNull())
+void ArcAppWindow::SetIcon(const gfx::ImageSkia& icon) {
+  if (!exo::ShellSurface::GetMainSurface(GetNativeWindow())) {
+    // Support unit tests where we don't have exo system initialized.
+    views::NativeWidgetAura::AssignIconToAuraWindow(
+        GetNativeWindow(), gfx::ImageSkia() /* window_icon */,
+        icon /* app_icon */);
     return;
-  icon_ = gfx::ImageSkia();
-  if (controller_)
-    controller_->UpdateLauncherItem();
+  }
+  exo::ShellSurface* shell_surface = static_cast<exo::ShellSurface*>(
+      widget_->widget_delegate()->GetContentsView());
+  if (!shell_surface)
+    return;
+  shell_surface->SetIcon(icon);
 }
 
 void ArcAppWindow::OnImageDecoded(const SkBitmap& decoded_image) {
-  // TODO(khmel): Use aura::Window property http://crbug.com/724292
-  icon_ = gfx::ImageSkiaOperations::CreateResizedImage(
+  SetIcon(gfx::ImageSkiaOperations::CreateResizedImage(
       gfx::ImageSkia(gfx::ImageSkiaRep(decoded_image, 1.0f)),
       skia::ImageOperations::RESIZE_BEST,
       gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
-                extension_misc::EXTENSION_ICON_SMALL));
-  if (controller_)
-    controller_->UpdateLauncherItem();
+                extension_misc::EXTENSION_ICON_SMALL)));
 }
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window.h b/chrome/browser/ui/ash/launcher/arc_app_window.h
index ffef2ba0..4886855 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window.h
@@ -13,10 +13,12 @@
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
 #include "ui/base/base_window.h"
-#include "ui/gfx/image/image_skia.h"
 
 class ArcAppWindowLauncherController;
 class ArcAppWindowLauncherItemController;
+namespace gfx {
+class ImageSkia;
+}
 
 namespace views {
 class Widget;
@@ -63,8 +65,6 @@
 
   ArcAppWindowLauncherItemController* controller() { return controller_; }
 
-  const gfx::ImageSkia& icon() const { return icon_; }
-
   // ui::BaseWindow:
   bool IsActive() const override;
   bool IsMaximized() const override;
@@ -89,8 +89,8 @@
   void SetAlwaysOnTop(bool always_on_top) override;
 
  private:
-  // Resets the icon and updates |controller_|'s active icon as needed.
-  void ResetIcon();
+  // Sets the icon for the window.
+  void SetIcon(const gfx::ImageSkia& icon);
 
   // ImageDecoder::ImageRequest:
   void OnImageDecoded(const SkBitmap& decoded_image) override;
@@ -103,8 +103,6 @@
   ash::ShelfID shelf_id_;
   // Keeps current full-screen mode.
   FullScreenMode fullscreen_mode_ = FullScreenMode::NOT_DEFINED;
-  // Contains custom icon if it was set.
-  gfx::ImageSkia icon_;
   // Unowned pointers
   views::Widget* const widget_;
   ArcAppWindowLauncherController* const owner_;
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index 88779bd..0b9a37e 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -6,9 +6,9 @@
 #include <string>
 
 #include "ash/display/screen_orientation_controller_chromeos.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shared/app_types.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/window_state.h"
@@ -547,7 +547,7 @@
 
   std::unique_ptr<ArcAppWindowLauncherItemController> controller =
       base::MakeUnique<ArcAppWindowLauncherItemController>(
-          app_shelf_id.ToString(), owner());
+          app_shelf_id.ToString());
   ArcAppWindowLauncherItemController* item_controller = controller.get();
   const ash::ShelfID shelf_id(app_shelf_id.ToString());
   if (!owner()->GetItem(shelf_id)) {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
index a2d0041..d307b11 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
@@ -20,10 +20,8 @@
 #include "ui/base/base_window.h"
 
 ArcAppWindowLauncherItemController::ArcAppWindowLauncherItemController(
-    const std::string& arc_app_id,
-    ChromeLauncherController* owner)
-    : AppWindowLauncherItemController(ash::ShelfID(arc_app_id)),
-      owner_(owner) {}
+    const std::string& arc_app_id)
+    : AppWindowLauncherItemController(ash::ShelfID(arc_app_id)) {}
 
 ArcAppWindowLauncherItemController::~ArcAppWindowLauncherItemController() {}
 
@@ -67,8 +65,8 @@
 ash::MenuItemList ArcAppWindowLauncherItemController::GetAppMenuItems(
     int event_flags) {
   ash::MenuItemList items;
-  base::string16 app_title =
-      LauncherControllerHelper::GetAppTitle(owner_->profile(), app_id());
+  base::string16 app_title = LauncherControllerHelper::GetAppTitle(
+      ChromeLauncherController::instance()->profile(), app_id());
   for (auto it = windows().begin(); it != windows().end(); ++it) {
     // TODO(khmel): resolve correct icon here.
     size_t i = std::distance(windows().begin(), it);
@@ -82,21 +80,3 @@
 
   return items;
 }
-
-void ArcAppWindowLauncherItemController::UpdateLauncherItem() {
-  const ArcAppWindow* arc_app_window =
-      static_cast<const ArcAppWindow*>(GetLastActiveWindow());
-  if (!arc_app_window || arc_app_window->icon().isNull()) {
-    if (!image_set_by_controller())
-      return;
-    set_image_set_by_controller(false);
-    owner_->SetLauncherItemImage(shelf_id(), gfx::ImageSkia());
-    AppIconLoader* icon_loader = owner_->GetAppIconLoaderForApp(app_id());
-    if (icon_loader)
-      icon_loader->UpdateImage(app_id());
-    return;
-  }
-
-  owner_->SetLauncherItemImage(shelf_id(), arc_app_window->icon());
-  set_image_set_by_controller(true);
-}
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
index ec8af9c..3ea97b2 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
@@ -12,14 +12,12 @@
 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
 
 class ArcAppWindow;
-class ChromeLauncherController;
 
 // Shelf item delegate for ARC app windows.
 class ArcAppWindowLauncherItemController
     : public AppWindowLauncherItemController {
  public:
-  ArcAppWindowLauncherItemController(const std::string& arc_app_id,
-                                     ChromeLauncherController* owner);
+  explicit ArcAppWindowLauncherItemController(const std::string& arc_app_id);
 
   ~ArcAppWindowLauncherItemController() override;
 
@@ -35,18 +33,12 @@
   void RemoveTaskId(int task_id);
   bool HasAnyTasks() const;
 
-  // AppWindowLauncherItemController:
-  void UpdateLauncherItem() override;
-
  private:
   // Update the shelf item's icon for the active window.
   void UpdateIcon(ArcAppWindow* arc_app_window);
 
   std::unordered_set<int> task_ids_;
 
-  // Unowned property.
-  ChromeLauncherController* const owner_;
-
   DISALLOW_COPY_AND_ASSIGN(ArcAppWindowLauncherItemController);
 };
 
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index f2b20a0..a093bfc 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -8,9 +8,9 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/resources/grit/ash_resources.h"
-#include "ash/shelf/shelf_model.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 8908e85..657d1bf 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -7,10 +7,10 @@
 #include "ash/multi_profile_uma.h"
 #include "ash/public/cpp/remote_shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -422,6 +422,7 @@
 void ChromeLauncherController::SetLauncherItemImage(
     const ash::ShelfID& shelf_id,
     const gfx::ImageSkia& image) {
+  DCHECK(!image.isNull());
   const ash::ShelfItem* item = GetItem(shelf_id);
   if (item) {
     ash::ShelfItem new_item = *item;
@@ -430,6 +431,13 @@
   }
 }
 
+void ChromeLauncherController::UpdateLauncherItemImage(
+    const std::string& app_id) {
+  AppIconLoader* icon_loader = GetAppIconLoaderForApp(app_id);
+  if (icon_loader)
+    icon_loader->UpdateImage(app_id);
+}
+
 void ChromeLauncherController::UpdateAppState(content::WebContents* contents,
                                               AppState app_state) {
   ash::ShelfID shelf_id(launcher_controller_helper_->GetAppID(contents));
@@ -686,16 +694,6 @@
   return arc_deferred_launcher_.get();
 }
 
-AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp(
-    const std::string& app_id) {
-  for (const auto& app_icon_loader : app_icon_loaders_) {
-    if (app_icon_loader->CanLoadImageForApp(app_id))
-      return app_icon_loader.get();
-  }
-
-  return nullptr;
-}
-
 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
   if (!ConnectToShelfController() || updating_shelf_pref_from_observer_)
     return;
@@ -761,6 +759,16 @@
   model_->UnpinAppWithID(app_id);
 }
 
+AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp(
+    const std::string& app_id) {
+  for (const auto& app_icon_loader : app_icon_loaders_) {
+    if (app_icon_loader->CanLoadImageForApp(app_id))
+      return app_icon_loader.get();
+  }
+
+  return nullptr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // LauncherAppUpdater::Delegate:
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 00d59c4..0c59a46 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -11,9 +11,9 @@
 
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/interfaces/shelf.mojom.h"
-#include "ash/shelf/shelf_model_observer.h"
 #include "base/auto_reset.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -44,7 +44,6 @@
 
 namespace ash {
 class Shelf;
-struct ShelfItem;
 class ShelfModel;
 }  // namespace ash
 
@@ -150,6 +149,9 @@
   void SetLauncherItemImage(const ash::ShelfID& shelf_id,
                             const gfx::ImageSkia& image);
 
+  // Updates the image for a specific shelf item from the app's icon loader.
+  void UpdateLauncherItemImage(const std::string& app_id);
+
   // Notify the controller that the state of an non platform app's tabs
   // have changed,
   void UpdateAppState(content::WebContents* contents, AppState app_state);
@@ -219,8 +221,6 @@
   // Controller to launch ARC apps in deferred mode.
   ArcAppDeferredLauncherController* GetArcDeferredLauncher();
 
-  AppIconLoader* GetAppIconLoaderForApp(const std::string& app_id);
-
   // Sets the shelf auto-hide and/or alignment behavior from prefs.
   void SetShelfAutoHideBehaviorFromPrefs();
   void SetShelfAlignmentFromPrefs();
@@ -389,6 +389,9 @@
   // An internal helper to unpin a shelf item; this does not update prefs.
   void UnpinShelfItemInternal(const ash::ShelfID& id);
 
+  // Resolves the app icon image loader for the app.
+  AppIconLoader* GetAppIconLoaderForApp(const std::string& app_id);
+
   static ChromeLauncherController* instance_;
 
   // The currently loaded profile used for prefs and loading extensions. This is
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 68545599..58c1fc0 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -7,12 +7,12 @@
 #include <stddef.h>
 
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -51,6 +51,7 @@
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/settings_window_manager.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_app_window_icon_observer.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
@@ -130,32 +131,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestEvent);
 };
 
-class TestAppWindowRegistryObserver
-    : public extensions::AppWindowRegistry::Observer {
- public:
-  explicit TestAppWindowRegistryObserver(Profile* profile)
-      : profile_(profile), icon_updates_(0) {
-    extensions::AppWindowRegistry::Get(profile_)->AddObserver(this);
-  }
-
-  ~TestAppWindowRegistryObserver() override {
-    extensions::AppWindowRegistry::Get(profile_)->RemoveObserver(this);
-  }
-
-  // Overridden from AppWindowRegistry::Observer:
-  void OnAppWindowIconChanged(AppWindow* app_window) override {
-    ++icon_updates_;
-  }
-
-  int icon_updates() { return icon_updates_; }
-
- private:
-  Profile* profile_;
-  int icon_updates_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAppWindowRegistryObserver);
-};
-
 // Click the "All Apps" button from the app launcher start page. Assumes that
 // the app launcher is open to the start page.
 // |display_origin| is the top-left corner of the active display, in screen
@@ -853,28 +828,52 @@
   EXPECT_EQ(ash::STATUS_RUNNING, shelf_model()->ItemByID(item_id1)->status);
 }
 
-// Flaky on Linux ChromiumOS bot -- crbug/720601.
 // Test that opening an app sets the correct icon
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, DISABLED_SetIcon) {
-  TestAppWindowRegistryObserver test_observer(browser()->profile());
+IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, SetIcon) {
+  TestAppWindowIconObserver test_observer(browser()->profile());
 
   // Enable experimental APIs to allow panel creation.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       extensions::switches::kEnableExperimentalExtensionApis);
 
   int base_shelf_item_count = shelf_model()->item_count();
-  ExtensionTestMessageListener completed_listener("Completed", false);
+  ExtensionTestMessageListener ready_listener("ready", true);
   LoadAndLaunchPlatformApp("app_icon", "Launched");
 
-  ASSERT_TRUE(completed_listener.WaitUntilSatisfied());
+  // Create panel window.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("createPanelWindow");
+  ready_listener.Reset();
+  // Default app icon + extension icon updates.
+  test_observer.WaitForIconUpdates(2);
 
-  // Now wait until the WebContent has decoded the icons and chrome has
-  // processed it. This needs to be in a loop since the renderer runs in a
-  // different process.
-  while (test_observer.icon_updates() < 4) {
-    base::RunLoop run_loop;
-    run_loop.RunUntilIdle();
-  }
+  // Set panel window icon.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("setPanelWindowIcon");
+  ready_listener.Reset();
+  // Custom icon update.
+  test_observer.WaitForIconUpdate();
+
+  // Create non-shelf window.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("createNonShelfWindow");
+  ready_listener.Reset();
+  // Default app icon + extension icon updates.
+  test_observer.WaitForIconUpdates(2);
+
+  // Create shelf window.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("createShelfWindow");
+  ready_listener.Reset();
+  // Default app icon + extension icon updates.
+  test_observer.WaitForIconUpdates(2);
+
+  // Set shelf window icon.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("setShelfWindowIcon");
+  ready_listener.Reset();
+  // Custom icon update.
+  test_observer.WaitForIconUpdate();
 
   // This test creates one app window, one app window with custom icon and one
   // panel window.
@@ -892,7 +891,7 @@
   const ash::ShelfItemDelegate* app_item_delegate =
       GetShelfItemDelegate(app_item.id);
   ASSERT_TRUE(app_item_delegate);
-  EXPECT_FALSE(app_item_delegate->image_set_by_controller());
+  EXPECT_TRUE(app_item_delegate->image_set_by_controller());
 
   const ash::ShelfItemDelegate* app_custom_icon_item_delegate =
       GetShelfItemDelegate(app_custom_icon_item.id);
@@ -900,10 +899,23 @@
   EXPECT_TRUE(app_custom_icon_item_delegate->image_set_by_controller());
 
   // Ensure icon heights are correct (see test.js in app_icon/ test directory)
+#if defined(USE_ASH)
+  EXPECT_EQ(ash::kShelfSize, app_item.image.height());
+#else
   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALL, app_item.image.height());
+#endif
   EXPECT_EQ(extension_misc::EXTENSION_ICON_LARGE,
             app_custom_icon_item.image.height());
   EXPECT_EQ(64, panel_item.image.height());
+
+  // No more icon updates.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(8, test_observer.icon_updates());
+
+  // Exit.
+  ready_listener.WaitUntilSatisfied();
+  ready_listener.Reply("exit");
+  ready_listener.Reset();
 }
 
 // Test that we can launch an app with a shortcut.
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index bff40444..e24620c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -18,11 +18,11 @@
 #include "ash/display/screen_orientation_controller_chromeos.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/shelf/shelf_application_menu_model.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_controller.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shelf/shelf_model_observer.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/shell_test_api.h"
@@ -2329,6 +2329,7 @@
 
   // Set custom icon on active item. Icon should change to custom.
   arc_test_.app_instance()->SendTaskDescription(2, std::string(), png_data);
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(item_delegate->image_set_by_controller());
 
   // Switch back to the item without custom icon. Icon should be changed to
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
index b8da64f..3baa7106 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h"
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/window_properties.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/wm/window_util.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
@@ -75,24 +75,6 @@
   registry_.insert(registry);
 }
 
-void ExtensionAppWindowLauncherController::OnAppWindowIconChanged(
-    AppWindow* app_window) {
-  const ash::ShelfID shelf_id = GetShelfId(app_window);
-  AppControllerMap::iterator iter = app_controller_map_.find(shelf_id);
-  if (iter == app_controller_map_.end())
-    return;
-
-  // Check if the window actually overrides its default icon. Otherwise use app
-  // icon loader provided by owner.
-  if (!app_window->HasCustomIcon() || app_window->app_icon().IsEmpty())
-    return;
-
-  ExtensionAppWindowLauncherItemController* controller = iter->second;
-  controller->set_image_set_by_controller(true);
-  owner()->SetLauncherItemImage(controller->shelf_id(),
-                                app_window->app_icon().AsImageSkia());
-}
-
 void ExtensionAppWindowLauncherController::OnAppWindowShown(
     AppWindow* app_window,
     bool was_hidden) {
@@ -149,21 +131,18 @@
     std::unique_ptr<ExtensionAppWindowLauncherItemController> controller =
         base::MakeUnique<ExtensionAppWindowLauncherItemController>(shelf_id);
     app_controller_map_[shelf_id] = controller.get();
-    controller->AddAppWindow(app_window);
 
     // Check for any existing pinned shelf item with a matching |shelf_id|.
     if (!owner()->GetItem(shelf_id)) {
       owner()->CreateAppLauncherItem(std::move(controller), status);
-      // Restore any existing app icon and flag as set.
-      if (app_window->HasCustomIcon() && !app_window->app_icon().IsEmpty()) {
-        owner()->SetLauncherItemImage(shelf_id,
-                                      app_window->app_icon().AsImageSkia());
-        app_controller_map_[shelf_id]->set_image_set_by_controller(true);
-      }
     } else {
       owner()->shelf_model()->SetShelfItemDelegate(shelf_id,
                                                    std::move(controller));
     }
+
+    // Register the window after a shelf item is created to let the controller
+    // set the shelf icon property.
+    app_controller_map_[shelf_id]->AddAppWindow(app_window);
   }
 
   owner()->SetItemStatus(shelf_id, status);
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h
index 9a4c4df7..fc0a5a4 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h
@@ -45,7 +45,6 @@
       aura::Window* window) override;
 
   // Overridden from AppWindowRegistry::Observer:
-  void OnAppWindowIconChanged(extensions::AppWindow* app_window) override;
   void OnAppWindowShown(extensions::AppWindow* app_window,
                         bool was_hidden) override;
   void OnAppWindowHidden(extensions::AppWindow* app_window) override;
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
index 04bc180a..78c557b0 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_controller.cc
@@ -58,8 +58,15 @@
         favicon::ContentFaviconDriver::FromWebContents(
             app_window->web_contents());
     gfx::Image icon = favicon_driver->GetFavicon();
-    if (icon.IsEmpty())
-      icon = app_window->app_icon();
+    if (icon.IsEmpty()) {
+      const gfx::ImageSkia* app_icon = nullptr;
+      if (app_window->GetNativeWindow()) {
+        app_icon = app_window->GetNativeWindow()->GetProperty(
+            aura::client::kAppIconKey);
+      }
+      if (app_icon && !app_icon->isNull())
+        icon = gfx::Image(*app_icon);
+    }
     if (!icon.IsEmpty())
       item->image = *icon.ToSkBitmap();
     items.push_back(std::move(item));
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index 3639dfd..82b7fdf 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -6,8 +6,8 @@
 
 #include <string>
 
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 835fd975..0970603 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -79,6 +79,7 @@
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
+#include "chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.h"
 #import "chrome/browser/ui/cocoa/translate/translate_bubble_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
@@ -1627,6 +1628,11 @@
                                      step:(translate::TranslateStep)step
                                 errorType:(translate::TranslateErrors::Type)
                                 errorType {
+  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    ShowTranslateBubbleViews([self window], [self locationBarBridge], contents,
+                             step, errorType, true);
+    return;
+  }
   // TODO(hajimehoshi): The similar logic exists at TranslateBubbleView::
   // ShowBubble. This should be unified.
   if (translateBubbleController_) {
diff --git a/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.h b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.h
new file mode 100644
index 0000000..669223c
--- /dev/null
+++ b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_TRANSLATE_BUBBLE_BRIDGE_VIEWS_H_
+#define CHROME_BROWSER_UI_COCOA_TRANSLATE_BUBBLE_BRIDGE_VIEWS_H_
+
+#include <Cocoa/Cocoa.h>
+
+#include "components/translate/core/browser/translate_step.h"
+#include "components/translate/core/common/translate_errors.h"
+
+namespace content {
+class WebContents;
+}
+
+class LocationBarViewMac;
+
+void ShowTranslateBubbleViews(NSWindow* parent_window,
+                              LocationBarViewMac* location_bar,
+                              content::WebContents* web_contents,
+                              translate::TranslateStep step,
+                              translate::TranslateErrors::Type error_type,
+                              bool is_user_gesture);
+
+#endif  // CHROME_BROWSER_UI_COCOA_TRANSLATE_BUBBLE_BRIDGE_VIEWS_H_
diff --git a/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm
new file mode 100644
index 0000000..6bd5681
--- /dev/null
+++ b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.h"
+
+#include "chrome/browser/ui/cocoa/bubble_anchor_helper_views.h"
+#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#include "chrome/browser/ui/cocoa/location_bar/translate_decoration.h"
+#include "chrome/browser/ui/views/translate/translate_bubble_view.h"
+#include "ui/base/cocoa/cocoa_base_utils.h"
+#include "ui/gfx/mac/coordinate_conversion.h"
+
+void ShowTranslateBubbleViews(NSWindow* parent_window,
+                              LocationBarViewMac* location_bar,
+                              content::WebContents* web_contents,
+                              translate::TranslateStep step,
+                              translate::TranslateErrors::Type error_type,
+                              bool is_user_gesture) {
+  gfx::Point anchor_point =
+      gfx::ScreenPointFromNSPoint(ui::ConvertPointFromWindowToScreen(
+          parent_window, location_bar->GetBubblePointForDecoration(
+                             location_bar->translate_decoration())));
+  TranslateBubbleView::DisplayReason reason =
+      is_user_gesture ? TranslateBubbleView::USER_GESTURE
+                      : TranslateBubbleView::AUTOMATIC;
+  TranslateBubbleView::ShowBubble(nullptr, anchor_point, web_contents, step,
+                                  error_type, reason);
+  if (TranslateBubbleView::GetCurrentBubble()) {
+    KeepBubbleAnchored(TranslateBubbleView::GetCurrentBubble(),
+                       location_bar->translate_decoration());
+  }
+}
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.cc b/chrome/browser/ui/extensions/extension_install_ui_default.cc
index 53fda5b..6114b11 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.cc
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.cc
@@ -9,7 +9,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -141,25 +140,13 @@
     content::BrowserContext* context)
     : profile_(Profile::FromBrowserContext(context)),
       skip_post_install_ui_(false),
-      previous_using_system_theme_(false),
-      use_app_installed_bubble_(false) {
-  // |profile| can be NULL during tests.
-  if (profile_) {
-    // Remember the current theme in case the user presses undo.
-    const Extension* previous_theme =
-        ThemeServiceFactory::GetThemeForProfile(profile_);
-    if (previous_theme)
-      previous_theme_id_ = previous_theme->id();
-    previous_using_system_theme_ =
-        ThemeServiceFactory::GetForProfile(profile_)->UsingSystemTheme();
-  }
-}
+      use_app_installed_bubble_(false) {}
 
 ExtensionInstallUIDefault::~ExtensionInstallUIDefault() {}
 
 void ExtensionInstallUIDefault::OnInstallSuccess(const Extension* extension,
                                                  const SkBitmap* icon) {
-  if (skip_post_install_ui_)
+  if (skip_post_install_ui_ || extension->is_theme())
     return;
 
   if (!profile_) {
@@ -169,12 +156,6 @@
     return;
   }
 
-  if (extension->is_theme()) {
-    ThemeInstalledInfoBarDelegate::Create(
-        extension, profile_, previous_theme_id_, previous_using_system_theme_);
-    return;
-  }
-
   // Extensions aren't enabled by default in incognito so we confirm
   // the install in a normal window.
   Profile* current_profile = profile_->GetOriginalProfile();
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.h b/chrome/browser/ui/extensions/extension_install_ui_default.h
index 32d40e1..2586f2e 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.h
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.h
@@ -35,10 +35,6 @@
   // Whether or not to show the default UI after completing the installation.
   bool skip_post_install_ui_;
 
-  // Used to undo theme installation.
-  std::string previous_theme_id_;
-  bool previous_using_system_theme_;
-
   // Whether to show an installed bubble on app install, or use the default
   // action of opening a new tab page.
   bool use_app_installed_bubble_;
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 7bc09d7..e61e99b4 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -291,8 +291,7 @@
   void InstallThemeAndVerify(const std::string& theme_dir,
                              const std::string& theme_name) {
     const extensions::Extension* theme =
-        ThemeServiceFactory::GetThemeForProfile(
-            ExtensionBrowserTest::browser()->profile());
+        ThemeServiceFactory::GetThemeForProfile(profile());
     // If there is already a theme installed, the current theme should be
     // disabled and the new one installed + enabled.
     int expected_change = theme ? 0 : 1;
@@ -300,9 +299,13 @@
     const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_dir);
     ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(
         theme_path, expected_change, ExtensionBrowserTest::browser()));
+    content::WindowedNotificationObserver theme_change_observer(
+        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+        content::Source<ThemeService>(
+            ThemeServiceFactory::GetForProfile(profile())));
+    theme_change_observer.Wait();
     const extensions::Extension* new_theme =
-        ThemeServiceFactory::GetThemeForProfile(
-            ExtensionBrowserTest::browser()->profile());
+        ThemeServiceFactory::GetThemeForProfile(profile());
     ASSERT_NE(static_cast<extensions::Extension*>(NULL), new_theme);
     ASSERT_EQ(new_theme->name(), theme_name);
   }
diff --git a/chrome/browser/ui/test/test_app_window_icon_observer.cc b/chrome/browser/ui/test/test_app_window_icon_observer.cc
new file mode 100644
index 0000000..afa26683
--- /dev/null
+++ b/chrome/browser/ui/test/test_app_window_icon_observer.cc
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/test/test_app_window_icon_observer.h"
+
+#include "base/run_loop.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+
+TestAppWindowIconObserver::TestAppWindowIconObserver(
+    content::BrowserContext* context)
+    : context_(context) {
+  extensions::AppWindowRegistry::Get(context_)->AddObserver(this);
+}
+
+TestAppWindowIconObserver::~TestAppWindowIconObserver() {
+  extensions::AppWindowRegistry::Get(context_)->RemoveObserver(this);
+  for (aura::Window* window : windows_)
+    window->RemoveObserver(this);
+}
+
+void TestAppWindowIconObserver::WaitForIconUpdate() {
+  WaitForIconUpdates(1);
+}
+
+void TestAppWindowIconObserver::WaitForIconUpdates(int updates) {
+  base::RunLoop run_loop;
+  expected_icon_updates_ = updates + icon_updates_;
+  icon_updated_callback_ = run_loop.QuitClosure();
+  run_loop.Run();
+}
+
+void TestAppWindowIconObserver::OnAppWindowAdded(
+    extensions::AppWindow* app_window) {
+  aura::Window* window = app_window->GetNativeWindow();
+  window->AddObserver(this);
+  windows_.push_back(window);
+}
+
+void TestAppWindowIconObserver::OnAppWindowRemoved(
+    extensions::AppWindow* app_window) {
+  aura::Window* window = app_window->GetNativeWindow();
+  if (window) {
+    windows_.erase(std::find(windows_.begin(), windows_.end(), window));
+    window->RemoveObserver(this);
+  }
+}
+
+void TestAppWindowIconObserver::OnWindowPropertyChanged(aura::Window* window,
+                                                        const void* key,
+                                                        intptr_t old) {
+  if (key == aura::client::kAppIconKey) {
+    ++icon_updates_;
+    if (icon_updates_ == expected_icon_updates_ &&
+        !icon_updated_callback_.is_null()) {
+      base::ResetAndReturn(&icon_updated_callback_).Run();
+    }
+  }
+}
diff --git a/chrome/browser/ui/test/test_app_window_icon_observer.h b/chrome/browser/ui/test/test_app_window_icon_observer.h
new file mode 100644
index 0000000..c83d1e9
--- /dev/null
+++ b/chrome/browser/ui/test/test_app_window_icon_observer.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TEST_TEST_APP_WINDOW_ICON_OBSERVER_H_
+#define CHROME_BROWSER_UI_TEST_TEST_APP_WINDOW_ICON_OBSERVER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "extensions/browser/app_window/app_window_registry.h"
+#include "ui/aura/window_observer.h"
+
+namespace content {
+class BrowserContext;
+}
+
+// A test helper that waits for AppWindow icon property updates.
+class TestAppWindowIconObserver
+    : public extensions::AppWindowRegistry::Observer,
+      public aura::WindowObserver {
+ public:
+  explicit TestAppWindowIconObserver(content::BrowserContext* context);
+  ~TestAppWindowIconObserver() override;
+
+  // Waits for one icon update.
+  void WaitForIconUpdate();
+  // Waits for |updates| number of icon updates.
+  void WaitForIconUpdates(int updates);
+
+  int icon_updates() const { return icon_updates_; }
+
+ private:
+  // AppWindowRegistry::Observer:
+  void OnAppWindowAdded(extensions::AppWindow* app_window) override;
+  void OnAppWindowRemoved(extensions::AppWindow* app_window) override;
+
+  // aura::WindowObserver:
+  void OnWindowPropertyChanged(aura::Window* window,
+                               const void* key,
+                               intptr_t old) override;
+
+  content::BrowserContext* const context_;
+  int icon_updates_ = 0;
+  int expected_icon_updates_ = 0;
+  std::vector<aura::Window*> windows_;
+  base::OnceClosure icon_updated_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAppWindowIconObserver);
+};
+
+#endif  // CHROME_BROWSER_UI_TEST_TEST_APP_WINDOW_ICON_OBSERVER_H_
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
index c834d03..d7584ee 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -21,8 +21,8 @@
 #include "ui/views/widget/widget.h"
 
 #if defined(USE_ASH)
-#include "ash/shelf/shelf_model.h"  // nogncheck
-#include "ash/shell.h"              // nogncheck
+#include "ash/public/cpp/shelf_model.h"  // nogncheck
+#include "ash/shell.h"                   // nogncheck
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"  // nogncheck
 #endif
 
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel_unittest.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel_unittest.cc
index f778456..fb9b37e 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel_unittest.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -55,6 +56,12 @@
         .Build();
   }
 
+  void SetUp() override {
+    // Set the ChromeLayoutProvider as the default layout provider.
+    views_delegate_.set_layout_provider(
+        ChromeLayoutProvider::CreateLayoutProvider());
+  }
+
   // We need the UI thread in order to construct UI elements in the view.
   content::TestBrowserThreadBundle thread_bundle_;
   views::TestViewsDelegate views_delegate_;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index ae6f17b..af8347b 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -13,11 +13,16 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/extensions/chrome_app_icon.h"
+#include "chrome/browser/extensions/chrome_app_icon_service.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/zoom/page_zoom.h"
 #include "components/zoom/zoom_controller.h"
+#include "extensions/browser/app_window/app_delegate.h"
+#include "ui/gfx/image/image_skia_operations.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/widget/widget.h"
 
@@ -250,14 +255,38 @@
 // views::WidgetDelegate implementation.
 
 gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowAppIcon() {
-  gfx::Image app_icon = app_window()->app_icon();
-  if (app_icon.IsEmpty())
-    return GetWindowIcon();
-  else
-    return *app_icon.ToImageSkia();
+  // Resulting icon is cached in aura::client::kAppIconKey window property.
+  const gfx::Image& custom_image = app_window()->custom_app_icon();
+  if (app_window()->app_icon_url().is_valid() &&
+      app_window()->show_in_shelf()) {
+    EnsureAppIconCreated();
+    gfx::Image base_image =
+        !custom_image.IsEmpty()
+            ? custom_image
+            : gfx::Image(extensions::util::GetDefaultAppIcon());
+    // Scale the icon to EXTENSION_ICON_LARGE.
+    const int large_icon_size = extension_misc::EXTENSION_ICON_LARGE;
+    if (base_image.Width() != large_icon_size ||
+        base_image.Height() != large_icon_size) {
+      gfx::ImageSkia resized_image =
+          gfx::ImageSkiaOperations::CreateResizedImage(
+              base_image.AsImageSkia(), skia::ImageOperations::RESIZE_BEST,
+              gfx::Size(large_icon_size, large_icon_size));
+      return gfx::ImageSkiaOperations::CreateIconWithBadge(
+          resized_image, app_icon_->image_skia());
+    }
+    return gfx::ImageSkiaOperations::CreateIconWithBadge(
+        base_image.AsImageSkia(), app_icon_->image_skia());
+  }
+
+  if (!custom_image.IsEmpty())
+    return *custom_image.ToImageSkia();
+  EnsureAppIconCreated();
+  return app_icon_->image_skia();
 }
 
 gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowIcon() {
+  // Resulting icon is cached in aura::client::kWindowIconKey window property.
   content::WebContents* web_contents = app_window()->web_contents();
   if (web_contents) {
     favicon::FaviconDriver* favicon_driver =
@@ -366,3 +395,25 @@
       extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
       NULL));
 }
+
+void ChromeNativeAppWindowViews::EnsureAppIconCreated() {
+  if (app_icon_ && app_icon_->IsValid())
+    return;
+
+  // To avoid recursive call, reset the smart pointer. It will be checked in
+  // OnIconUpdated to determine if this is a real update or the initial callback
+  // on icon creation.
+  app_icon_.reset();
+  app_icon_ =
+      extensions::ChromeAppIconService::Get(app_window()->browser_context())
+          ->CreateIcon(this, app_window()->extension_id(),
+                       app_window()->app_delegate()->PreferredIconSize());
+}
+
+void ChromeNativeAppWindowViews::OnIconUpdated(
+    extensions::ChromeAppIcon* icon) {
+  if (!app_icon_)
+    return;
+  DCHECK_EQ(app_icon_.get(), icon);
+  UpdateWindowIcon();
+}
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
index 7809977..3234213 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
@@ -8,12 +8,14 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "chrome/browser/extensions/chrome_app_icon_delegate.h"
 #include "extensions/components/native_app_window/native_app_window_views.h"
 
 class ExtensionKeybindingRegistryViews;
 
 class ChromeNativeAppWindowViews
-    : public native_app_window::NativeAppWindowViews {
+    : public native_app_window::NativeAppWindowViews,
+      public extensions::ChromeAppIconDelegate {
  public:
   ChromeNativeAppWindowViews();
   ~ChromeNativeAppWindowViews() override;
@@ -69,6 +71,12 @@
       const extensions::AppWindow::CreateParams& create_params) override;
 
  private:
+  // Ensures that the Chrome app icon is created.
+  void EnsureAppIconCreated();
+
+  // extensions::ChromeAppIconDelegate:
+  void OnIconUpdated(extensions::ChromeAppIcon* icon) override;
+
   // Custom shape of the window. If this is not set then the window has a
   // default shape, usually rectangular.
   std::unique_ptr<SkRegion> shape_;
@@ -81,6 +89,10 @@
   std::unique_ptr<ExtensionKeybindingRegistryViews>
       extension_keybinding_registry_;
 
+  // Contains the default Chrome app icon. It is used in case the custom icon
+  // for the extension app window is not set, or as a part of composite image.
+  std::unique_ptr<extensions::ChromeAppIcon> app_icon_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeNativeAppWindowViews);
 };
 
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
index aab4bb8..ff60812fb 100644
--- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
@@ -5,11 +5,11 @@
 #include "chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h"
 
 #include "ash/public/cpp/mus_property_mirror_ash.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/interfaces/window_pin_type.mojom.h"
 #include "ash/root_window_controller.h"
-#include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chrome_browser_main.h"
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index 875173c..13758ba 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -24,7 +24,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
-#include "ui/views/background.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
@@ -78,9 +77,6 @@
   description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   AddChildView(description_label_);
 
-  const SkColor bg_color = GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_DialogBackground);
-
   if (screen_list) {
     source_types_.push_back(DesktopMediaID::TYPE_SCREEN);
 
@@ -117,7 +113,6 @@
         kGenericScreenStyle.item_size.height(),
         kGenericScreenStyle.item_size.height() * 2);
     screen_scroll_view->set_hide_horizontal_scrollbar(true);
-    screen_scroll_view->SetBackground(views::CreateSolidBackground(bg_color));
 
     pane_->AddTab(screen_title_text, screen_scroll_view);
     pane_->set_listener(this);
@@ -147,7 +142,6 @@
     window_scroll_view->ClipHeightTo(kWindowStyle.item_size.height(),
                                      kWindowStyle.item_size.height() * 2);
     window_scroll_view->set_hide_horizontal_scrollbar(true);
-    window_scroll_view->SetBackground(views::CreateSolidBackground(bg_color));
 
     pane_->AddTab(window_title_text, window_scroll_view);
     pane_->set_listener(this);
@@ -177,7 +171,6 @@
     tab_scroll_view->ClipHeightTo(kTabStyle.item_size.height(),
                                   kTabStyle.item_size.height() * 10);
     tab_scroll_view->set_hide_horizontal_scrollbar(true);
-    tab_scroll_view->SetBackground(views::CreateSolidBackground(bg_color));
 
     pane_->AddTab(tab_title_text, tab_scroll_view);
     pane_->set_listener(this);
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index c15d2b7..7b76bb7 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -503,12 +503,6 @@
   return layout;
 }
 
-void ExtensionInstallDialogView::OnNativeThemeChanged(
-    const ui::NativeTheme* theme) {
-  scroll_view_->SetBackgroundColor(
-      theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground));
-}
-
 int ExtensionInstallDialogView::GetDialogButtons() const {
   int buttons = prompt_->GetDialogButtons();
   // Simply having just an OK button is *not* supported. See comment on function
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index de6da73..14e29abc 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -54,7 +54,6 @@
 
  private:
   // views::View:
-  void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
 
   // views::DialogDelegateView:
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index e12e075..4c6a5a8 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -180,7 +180,7 @@
   }
 
   scroll_view_ = new views::ScrollView();
-  scroll_view_->EnableViewPortLayer();
+  scroll_view_->SetBackgroundColor(SK_ColorWHITE);
   scroll_view_->SetContents(scrollable_view);
   // Setting a customized ScrollBar which is shown only when the mouse pointer
   // is inside the ScrollView.
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
index f02bb75b0..c3461f9 100644
--- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -145,6 +145,7 @@
   };
 
   BorderedScrollView() : views::ScrollView() {
+    SetBackgroundColor(SK_ColorWHITE);
     SetBorder(views::CreateBorderPainter(
         base::MakeUnique<BorderedScrollViewBorderPainter>(
             GetNativeTheme()->GetSystemColor(
@@ -239,7 +240,6 @@
 
   scroll_ = base::MakeUnique<BorderedScrollView>();
   scroll_->set_owned_by_client();
-  scroll_->EnableViewPortLayer();
   scroll_->set_hide_horizontal_scrollbar(true);
   scroll_->SetContents(pane_);
   layout->AddView(scroll_.get());
diff --git a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc
index 274f13e0..867139e 100644
--- a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc
+++ b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc
@@ -35,6 +35,9 @@
       max_height_(0),
       toolbar_actions_bar_observer_(this),
       weak_factory_(this) {
+  // Use a transparent background so that the menu's background shows through.
+  // None of the children use layers, so this should be ok.
+  SetBackgroundColor(SK_ColorTRANSPARENT);
   BrowserActionsContainer* main =
       BrowserView::GetBrowserViewForBrowser(browser_)
           ->toolbar()->browser_actions();
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index ef9ffcd2..72eaaa1 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -345,9 +345,9 @@
   }
 
   views::Widget* bubble_widget = TranslateBubbleView::ShowBubble(
-      anchor_view, web_contents, step,
-      error_type, is_user_gesture ? TranslateBubbleView::USER_GESTURE
-                                  : TranslateBubbleView::AUTOMATIC);
+      anchor_view, gfx::Point(), web_contents, step, error_type,
+      is_user_gesture ? TranslateBubbleView::USER_GESTURE
+                      : TranslateBubbleView::AUTOMATIC);
   if (bubble_widget && translate_icon_view)
     bubble_widget->AddObserver(translate_icon_view);
 }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 4a2aa3e7..4f5f3af2 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -91,6 +91,7 @@
 // static
 views::Widget* TranslateBubbleView::ShowBubble(
     views::View* anchor_view,
+    const gfx::Point& anchor_point,
     content::WebContents* web_contents,
     translate::TranslateStep step,
     translate::TranslateErrors::Type error_type,
@@ -131,7 +132,7 @@
   std::unique_ptr<TranslateBubbleModel> model(
       new TranslateBubbleModelImpl(step, std::move(ui_delegate)));
   TranslateBubbleView* view = new TranslateBubbleView(
-      anchor_view, std::move(model), error_type, web_contents);
+      anchor_view, anchor_point, std::move(model), error_type, web_contents);
   views::Widget* bubble_widget =
       views::BubbleDialogDelegateView::CreateBubble(view);
   view->ShowForReason(reason);
@@ -345,10 +346,11 @@
 
 TranslateBubbleView::TranslateBubbleView(
     views::View* anchor_view,
+    const gfx::Point& anchor_point,
     std::unique_ptr<TranslateBubbleModel> model,
     translate::TranslateErrors::Type error_type,
     content::WebContents* web_contents)
-    : LocationBarBubbleDelegateView(anchor_view, web_contents),
+    : LocationBarBubbleDelegateView(anchor_view, anchor_point, web_contents),
       WebContentsObserver(web_contents),
       before_translate_view_(NULL),
       translating_view_(NULL),
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index 00e58619..d4921bc 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -69,6 +69,7 @@
   // |is_user_gesture| is true when the bubble is shown on the user's deliberate
   // action.
   static views::Widget* ShowBubble(views::View* anchor_view,
+                                   const gfx::Point& anchor_point,
                                    content::WebContents* web_contents,
                                    translate::TranslateStep step,
                                    translate::TranslateErrors::Type error_type,
@@ -174,6 +175,7 @@
   FRIEND_TEST_ALL_PREFIXES(TranslateBubbleViewTest, CancelButtonReturningError);
 
   TranslateBubbleView(views::View* anchor_view,
+                      const gfx::Point& anchor_point,
                       std::unique_ptr<TranslateBubbleModel> model,
                       translate::TranslateErrors::Type error_type,
                       content::WebContents* web_contents);
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
index 40984d47..f28fddbc5 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
@@ -169,7 +169,7 @@
   void CreateAndShowBubble() {
     std::unique_ptr<TranslateBubbleModel> model(mock_model_);
     bubble_ = new TranslateBubbleView(anchor_widget_->GetContentsView(),
-                                      std::move(model),
+                                      gfx::Point(), std::move(model),
                                       translate::TranslateErrors::NONE, NULL);
     views::BubbleDialogDelegateView::CreateBubble(bubble_)->Show();
   }
diff --git a/chrome/browser/ui/webui/conflicts_handler.cc b/chrome/browser/ui/webui/conflicts_handler.cc
new file mode 100644
index 0000000..75c06bd
--- /dev/null
+++ b/chrome/browser/ui/webui/conflicts_handler.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/conflicts_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+ConflictsHandler::ConflictsHandler() : observer_(this) {}
+ConflictsHandler::~ConflictsHandler() = default;
+
+void ConflictsHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "requestModuleList",
+      base::Bind(&ConflictsHandler::HandleRequestModuleList,
+                 base::Unretained(this)));
+}
+
+void ConflictsHandler::HandleRequestModuleList(const base::ListValue* args) {
+  // The request is handled asynchronously, and will callback via
+  // OnScanCompleted on completion.
+  auto* model = EnumerateModulesModel::GetInstance();
+
+  // The JS shouldn't be abusive and call 'requestModuleList' twice, but it's
+  // easy enough to defend against this.
+  if (!observer_.IsObserving(model)) {
+    observer_.Add(model);
+
+    // Ask the scan to be performed immediately, and not in background mode.
+    // This ensures the results are available ASAP for the UI.
+    model->ScanNow(false);
+  }
+}
+
+void ConflictsHandler::SendModuleList() {
+  auto* loaded_modules = EnumerateModulesModel::GetInstance();
+  base::ListValue* list = loaded_modules->GetModuleList();
+  base::DictionaryValue results;
+  results.Set("moduleList", list);
+
+  // Add the section title and the total count for bad modules found.
+  int confirmed_bad = loaded_modules->confirmed_bad_modules_detected();
+  int suspected_bad = loaded_modules->suspected_bad_modules_detected();
+  base::string16 table_title;
+  if (!confirmed_bad && !suspected_bad) {
+    table_title += l10n_util::GetStringFUTF16(
+        IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE,
+        base::IntToString16(list->GetSize()));
+  } else {
+    table_title += l10n_util::GetStringFUTF16(
+        IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO,
+        base::IntToString16(list->GetSize()),
+        base::IntToString16(confirmed_bad), base::IntToString16(suspected_bad));
+  }
+  results.SetString("modulesTableTitle", table_title);
+
+  web_ui()->CallJavascriptFunctionUnsafe("returnModuleList", results);
+}
+
+void ConflictsHandler::OnScanCompleted() {
+  SendModuleList();
+  observer_.Remove(EnumerateModulesModel::GetInstance());
+}
diff --git a/chrome/browser/ui/webui/conflicts_handler.h b/chrome/browser/ui/webui/conflicts_handler.h
new file mode 100644
index 0000000..1b7cfae
--- /dev/null
+++ b/chrome/browser/ui/webui/conflicts_handler.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/win/enumerate_modules_model.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class ConflictsHandler : public content::WebUIMessageHandler,
+                         public EnumerateModulesModel::Observer {
+ public:
+  ConflictsHandler();
+  ~ConflictsHandler() override;
+
+  // WebUIMessageHandler implementation.
+  void RegisterMessages() override;
+
+  // Callback for the "requestModuleList" message.
+  void HandleRequestModuleList(const base::ListValue* args);
+
+ private:
+  void SendModuleList();
+
+  // EnumerateModulesModel::Observer implementation.
+  void OnScanCompleted() override;
+
+  ScopedObserver<EnumerateModulesModel, EnumerateModulesModel::Observer>
+      observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConflictsHandler);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/conflicts_ui.cc b/chrome/browser/ui/webui/conflicts_ui.cc
index 7ed3878..440f7fe3 100644
--- a/chrome/browser/ui/webui/conflicts_ui.cc
+++ b/chrome/browser/ui/webui/conflicts_ui.cc
@@ -4,45 +4,20 @@
 
 #include "chrome/browser/ui/webui/conflicts_ui.h"
 
-#if defined(OS_WIN)
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/user_metrics.h"
-#include "base/scoped_observer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/win/enumerate_modules_model.h"
+#include "chrome/browser/ui/webui/conflicts_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/strings/grit/components_strings.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/layout.h"
 #include "ui/base/resource/resource_bundle.h"
 
-using base::UserMetricsAction;
-using content::WebContents;
-using content::WebUIMessageHandler;
-
 namespace {
 
 content::WebUIDataSource* CreateConflictsUIHTMLSource() {
@@ -73,94 +48,6 @@
   return source;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-//
-// ConflictsDOMHandler
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// The handler for JavaScript messages for the about:conflicts page.
-class ConflictsDOMHandler : public WebUIMessageHandler,
-                            public EnumerateModulesModel::Observer {
- public:
-  ConflictsDOMHandler();
-  ~ConflictsDOMHandler() override {}
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
-  // Callback for the "requestModuleList" message.
-  void HandleRequestModuleList(const base::ListValue* args);
-
- private:
-  void SendModuleList();
-
-  // EnumerateModulesModel::Observer implementation.
-  void OnScanCompleted() override;
-
-  ScopedObserver<EnumerateModulesModel,
-                 EnumerateModulesModel::Observer> observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(ConflictsDOMHandler);
-};
-
-ConflictsDOMHandler::ConflictsDOMHandler()
-    : observer_(this) {
-}
-
-void ConflictsDOMHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback("requestModuleList",
-      base::Bind(&ConflictsDOMHandler::HandleRequestModuleList,
-                 base::Unretained(this)));
-}
-
-void ConflictsDOMHandler::HandleRequestModuleList(const base::ListValue* args) {
-  // The request is handled asynchronously, and will callback via
-  // OnScanCompleted on completion.
-  auto* model = EnumerateModulesModel::GetInstance();
-
-  // The JS shouldn't be abusive and call 'requestModuleList' twice, but it's
-  // easy enough to defend against this.
-  if (!observer_.IsObserving(model)) {
-    observer_.Add(model);
-
-    // Ask the scan to be performed immediately, and not in background mode.
-    // This ensures the results are available ASAP for the UI.
-    model->ScanNow(false);
-  }
-}
-
-void ConflictsDOMHandler::SendModuleList() {
-  auto* loaded_modules = EnumerateModulesModel::GetInstance();
-  base::ListValue* list = loaded_modules->GetModuleList();
-  base::DictionaryValue results;
-  results.Set("moduleList", list);
-
-  // Add the section title and the total count for bad modules found.
-  int confirmed_bad = loaded_modules->confirmed_bad_modules_detected();
-  int suspected_bad = loaded_modules->suspected_bad_modules_detected();
-  base::string16 table_title;
-  if (!confirmed_bad && !suspected_bad) {
-    table_title += l10n_util::GetStringFUTF16(
-        IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE,
-            base::IntToString16(list->GetSize()));
-  } else {
-    table_title += l10n_util::GetStringFUTF16(
-        IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO,
-            base::IntToString16(list->GetSize()),
-            base::IntToString16(confirmed_bad),
-            base::IntToString16(suspected_bad));
-  }
-  results.SetString("modulesTableTitle", table_title);
-
-  web_ui()->CallJavascriptFunctionUnsafe("returnModuleList", results);
-}
-
-void ConflictsDOMHandler::OnScanCompleted() {
-  SendModuleList();
-  observer_.Remove(EnumerateModulesModel::GetInstance());
-}
-
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -169,9 +56,10 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-ConflictsUI::ConflictsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
-  base::RecordAction(UserMetricsAction("ViewAboutConflicts"));
-  web_ui->AddMessageHandler(base::MakeUnique<ConflictsDOMHandler>());
+ConflictsUI::ConflictsUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  base::RecordAction(base::UserMetricsAction("ViewAboutConflicts"));
+  web_ui->AddMessageHandler(base::MakeUnique<ConflictsHandler>());
 
   // Set up the about:conflicts source.
   Profile* profile = Profile::FromWebUI(web_ui);
@@ -185,5 +73,3 @@
       ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
           IDR_CONFLICT_FAVICON, scale_factor));
 }
-
-#endif
diff --git a/chrome/browser/ui/webui/conflicts_ui.h b/chrome/browser/ui/webui/conflicts_ui.h
index 46735f14..0618ae5 100644
--- a/chrome/browser/ui/webui/conflicts_ui.h
+++ b/chrome/browser/ui/webui/conflicts_ui.h
@@ -10,8 +10,6 @@
 #include "content/public/browser/web_ui_controller.h"
 #include "ui/base/layout.h"
 
-#if defined(OS_WIN)
-
 namespace base {
 class RefCountedMemory;
 }
@@ -28,6 +26,4 @@
   DISALLOW_COPY_AND_ASSIGN(ConflictsUI);
 };
 
-#endif
-
 #endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
diff --git a/chrome/common/extensions/api/accessibility_private.json b/chrome/common/extensions/api/accessibility_private.json
index 0b9f629..cc34f2b 100644
--- a/chrome/common/extensions/api/accessibility_private.json
+++ b/chrome/common/extensions/api/accessibility_private.json
@@ -100,6 +100,24 @@
             "description": "True to darken screen; false to undarken screen."
           }
         ]
+      },
+      {
+        "name": "setSwitchAccessKeys",
+        "type": "function",
+        "description": "Change the keyboard keys captured by Switch Access.",
+        "parameters": [
+          {
+            "name": "key_codes",
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "minimum": 48, // '0' key
+              "maximum": 90  // 'z' key
+            },
+            "description": "The key codes for the keys that will be captured."
+          }
+        ],
+        "platforms": ["chromeos"]
       }
     ],
     "events": [
diff --git a/chrome/service/cloud_print/cloud_print_token_store.cc b/chrome/service/cloud_print/cloud_print_token_store.cc
index 84006ee..bebf595 100644
--- a/chrome/service/cloud_print/cloud_print_token_store.cc
+++ b/chrome/service/cloud_print/cloud_print_token_store.cc
@@ -15,6 +15,7 @@
     base::ThreadLocalPointer<CloudPrintTokenStore>>::DestructorAtExit lazy_tls =
     LAZY_INSTANCE_INITIALIZER;
 
+// static
 CloudPrintTokenStore* CloudPrintTokenStore::current() {
   return lazy_tls.Pointer()->Get();
 }
@@ -24,12 +25,12 @@
 }
 
 CloudPrintTokenStore::~CloudPrintTokenStore() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   lazy_tls.Pointer()->Set(NULL);
 }
 
 void CloudPrintTokenStore::SetToken(const std::string& token) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   token_ = token;
 }
 
diff --git a/chrome/service/cloud_print/cloud_print_token_store.h b/chrome/service/cloud_print/cloud_print_token_store.h
index b61ed9ee..c8c46823 100644
--- a/chrome/service/cloud_print/cloud_print_token_store.h
+++ b/chrome/service/cloud_print/cloud_print_token_store.h
@@ -8,7 +8,7 @@
 #include <string>
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/sequence_checker.h"
+#include "base/threading/thread_checker.h"
 
 // This class serves as the single repository for cloud print auth tokens. This
 // is only used within the CloudPrintProxyCoreThread.
@@ -26,14 +26,15 @@
 
   void SetToken(const std::string& token);
   std::string token() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     return token_;
   }
 
  private:
   std::string token_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  // Thread-affine per use of TLS in impl.
+  THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(CloudPrintTokenStore);
 };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cef0c0a..9e73427 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -223,6 +223,10 @@
     sources += [
       "../browser/chromeos/accessibility/speech_monitor.cc",
       "../browser/chromeos/accessibility/speech_monitor.h",
+      "../browser/chromeos/ownership/fake_owner_settings_service.cc",
+      "../browser/chromeos/ownership/fake_owner_settings_service.h",
+      "../browser/chromeos/settings/scoped_cros_settings_test_helper.cc",
+      "../browser/chromeos/settings/scoped_cros_settings_test_helper.h",
     ]
     public_deps += [
       "//components/ownership",
@@ -2025,7 +2029,11 @@
       }
     }
     if (use_aura) {
-      sources += [ "../browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc" ]
+      sources += [
+        "../browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc",
+        "../browser/ui/test/test_app_window_icon_observer.cc",
+        "../browser/ui/test/test_app_window_icon_observer.h",
+      ]
     }
     if (use_aura || toolkit_views) {
       deps += [ "//ui/events:test_support" ]
@@ -2231,8 +2239,6 @@
         "../browser/chromeos/login/webview_login_browsertest.cc",
         "../browser/chromeos/login/wizard_controller_browsertest.cc",
         "../browser/chromeos/net/network_portal_detector_impl_browsertest.cc",
-        "../browser/chromeos/ownership/fake_owner_settings_service.cc",
-        "../browser/chromeos/ownership/fake_owner_settings_service.h",
         "../browser/chromeos/policy/affiliation_test_helper.cc",
         "../browser/chromeos/policy/affiliation_test_helper.h",
         "../browser/chromeos/policy/blocking_login_browsertest.cc",
@@ -2262,8 +2268,6 @@
         "../browser/chromeos/power/peripheral_battery_observer_browsertest.cc",
         "../browser/chromeos/preferences_chromeos_browsertest.cc",
         "../browser/chromeos/profiles/profile_helper_browsertest.cc",
-        "../browser/chromeos/settings/scoped_cros_settings_test_helper.cc",
-        "../browser/chromeos/settings/scoped_cros_settings_test_helper.h",
         "../browser/chromeos/shutdown_policy_browsertest.cc",
         "../browser/chromeos/system/device_disabling_browsertest.cc",
         "../browser/chromeos/system/tray_accessibility_browsertest.cc",
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 58041030..f67cb424 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/chrome_history_client.h"
@@ -426,6 +427,13 @@
              content::BrowserThread::UI) ||
          content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
+#if defined(OS_CHROMEOS)
+  if (!chromeos::CrosSettings::IsInitialized()) {
+    scoped_cros_settings_test_helper_.reset(
+        new chromeos::ScopedCrosSettingsTestHelper);
+  }
+#endif
+
   set_is_guest_profile(guest_session_);
 
   BrowserContext::Initialize(this, profile_path_);
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 9fff4af..2384f0c 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -19,6 +19,10 @@
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "extensions/features/features.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#endif
+
 class BrowserContextDependencyManager;
 class ExtensionSpecialStoragePolicy;
 class HostContentSettingsMap;
@@ -438,6 +442,11 @@
 
   std::string profile_name_;
 
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<chromeos::ScopedCrosSettingsTestHelper>
+      scoped_cros_settings_test_helper_;
+#endif  // defined(OS_CHROMEOS)
+
   std::unique_ptr<policy::PolicyService> policy_service_;
 };
 
diff --git a/chrome/test/data/extensions/platform_apps/app_icon/test.js b/chrome/test/data/extensions/platform_apps/app_icon/test.js
index 3b3887a..bebd82e0 100644
--- a/chrome/test/data/extensions/platform_apps/app_icon/test.js
+++ b/chrome/test/data/extensions/platform_apps/app_icon/test.js
@@ -2,29 +2,54 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-chrome.app.runtime.onLaunched.addListener(function() {
-  chrome.test.sendMessage("Launched");
-  // Create a panel window first
-  chrome.app.window.create(
-    'main.html', { type: "panel" },
-    function (win) {
-      // Set the panel window icon
-      win.setIcon("icon64.png")
+var panelWindow;
+var nonShelfWindow;
+var shelfWindow;
+
+function processNextCommand() {
+  chrome.test.sendMessage('ready', function(response) {
+    if (response == 'Exit') {
+      return;
+    }
+    if (response == 'createPanelWindow') {
+      chrome.app.window.create('main.html', { type: 'panel' }, function (win) {
+         panelWindow = win;
+      });
+    }
+    if (response == 'setPanelWindowIcon') {
+      panelWindow.setIcon('icon64.png')
+    }
+    if (response == 'createNonShelfWindow') {
       // Create the shell window; it should use the app icon, and not affect
       // the panel icon.
       chrome.app.window.create(
-        'main.html', { type: "shell" },
+        'main.html', { id: 'win',
+                       type: 'shell' },
         function (win) {
-          // Create the shell window which is shown in shelf; it should use
-          // another custom app icon.
-          chrome.app.window.create(
-            'main.html', { id: "win_with_icon",
-                           type: "shell",
-                           icon: "icon48.png",
-                           showInShelf: true },
-            function (win) {
-              chrome.test.sendMessage("Completed");
-            });
+            nonShelfWindow = win;
         });
-    });
+    }
+    if (response == 'createShelfWindow') {
+      // Create the shell window which is shown in shelf; it should use
+      // another custom app icon.
+      chrome.app.window.create(
+        'main.html', { id: 'win_with_icon',
+                       type: 'shell',
+                       showInShelf: true },
+        function (win) {
+          shelfWindow = win;
+        });
+    }
+    if (response == 'setShelfWindowIcon') {
+      shelfWindow.setIcon('icon32.png')
+    }
+    processNextCommand();
+  });
+};
+
+
+chrome.app.runtime.onLaunched.addListener(function() {
+  chrome.test.sendMessage('Launched');
+  processNextCommand();
 });
+
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index 670ec072..3039a08 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -12,6 +12,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/user_metrics.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
 #include "components/autofill/core/browser/autofill_field.h"
@@ -80,6 +82,10 @@
   NUM_FIELD_TYPE_GROUPS_FOR_METRICS
 };
 
+const int KMaxFieldTypeGroupMetric =
+    (NUM_FIELD_TYPE_GROUPS_FOR_METRICS << 8) |
+    AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS;
+
 std::string PreviousSaveCreditCardPromptUserDecisionToString(
     int previous_save_credit_card_prompt_user_decision) {
   DCHECK_LT(previous_save_credit_card_prompt_user_decision,
@@ -228,9 +234,12 @@
       break;
   }
 
-  // Interpolate the |metric| with the |group|, so that all metrics for a given
-  // |group| are adjacent.
-  return (group * AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS) + metric;
+  // Use bits 8-15 for the group and bits 0-7 for the metric.
+  static_assert(AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS <= UINT8_MAX,
+                "maximum field type quality metric must fit into 8 bits");
+  static_assert(NUM_FIELD_TYPE_GROUPS_FOR_METRICS <= UINT8_MAX,
+                "number of field type groups must fit into 8 bits");
+  return (group << 8) | metric;
 }
 
 namespace {
@@ -268,40 +277,194 @@
   histogram->AddTime(duration);
 }
 
-// Logs a type quality metric.  The primary histogram name is constructed based
-// on |base_name|.  The field-specific histogram name also factors in the
-// |field_type|.  Logs a sample of |metric|, which should be in the range
-// [0, |num_possible_metrics|). May log a suffixed version of the metric
-// depending on |metric_type|.
-void LogTypeQualityMetric(const std::string& base_name,
-                          AutofillMetrics::FieldTypeQualityMetric metric,
-                          ServerFieldType field_type,
-                          AutofillMetrics::QualityMetricType metric_type) {
-  DCHECK_LT(metric, AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
-
-  std::string suffix;
+const char* GetQualityMetricTypeSuffix(
+    AutofillMetrics::QualityMetricType metric_type) {
   switch (metric_type) {
-    case AutofillMetrics::TYPE_SUBMISSION:
-      break;
-    case AutofillMetrics::TYPE_NO_SUBMISSION:
-      suffix = ".NoSubmission";
-      break;
-    case AutofillMetrics::TYPE_AUTOCOMPLETE_BASED:
-      suffix = ".BasedOnAutocomplete";
-      break;
     default:
       NOTREACHED();
-  }
-  LogUMAHistogramEnumeration(base_name + suffix, metric,
-                             AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+    // Fall through...
 
-  int field_type_group_metric = GetFieldTypeGroupMetric(field_type, metric);
-  int num_field_type_group_metrics =
-      AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS *
-      NUM_FIELD_TYPE_GROUPS_FOR_METRICS;
-  LogUMAHistogramEnumeration(base_name + ".ByFieldType" + suffix,
-                             field_type_group_metric,
-                             num_field_type_group_metrics);
+    case AutofillMetrics::TYPE_SUBMISSION:
+      return "";
+    case AutofillMetrics::TYPE_NO_SUBMISSION:
+      return ".NoSubmission";
+    case AutofillMetrics::TYPE_AUTOCOMPLETE_BASED:
+      return ".BasedOnAutocomplete";
+  }
+}
+
+// Given a set of |possible_types| for a field, select the best type to use as
+// the "actual" field type when calculating metrics. If the |predicted_type| is
+// among the |possible_types] then use that as the best type (i.e., the
+// prediction is deemed to have been correct).
+ServerFieldType GetActualFieldType(const ServerFieldTypeSet& possible_types,
+                                   ServerFieldType predicted_type) {
+  DCHECK_NE(possible_types.size(), 0u);
+
+  if (possible_types.count(EMPTY_TYPE)) {
+    DCHECK_EQ(possible_types.size(), 1u);
+    return EMPTY_TYPE;
+  }
+
+  if (possible_types.count(UNKNOWN_TYPE)) {
+    DCHECK_EQ(possible_types.size(), 1u);
+    return UNKNOWN_TYPE;
+  }
+
+  if (possible_types.count(predicted_type))
+    return predicted_type;
+
+  // Collapse field types that Chrome treats as identical, e.g. home and
+  // billing address fields.
+  ServerFieldTypeSet collapsed_field_types;
+  for (const auto& type : possible_types) {
+    DCHECK_NE(type, EMPTY_TYPE);
+    DCHECK_NE(type, UNKNOWN_TYPE);
+
+    // A phone number that's only missing its country code is (for metrics
+    // purposes) the same as the whole phone number.
+    if (type == PHONE_HOME_CITY_AND_NUMBER)
+      collapsed_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
+    else
+      collapsed_field_types.insert(AutofillType(type).GetStorableType());
+  }
+
+  // Capture the field's type, if it is unambiguous.
+  ServerFieldType actual_type = AMBIGUOUS_TYPE;
+  if (collapsed_field_types.size() == 1)
+    actual_type = *collapsed_field_types.begin();
+
+  DVLOG(2) << "Inferred Type: " << AutofillType(actual_type).ToString();
+  return actual_type;
+}
+
+// Logs field type prediction quality metrics.  The primary histogram name is
+// constructed based on |source| The field-specific histogram name also factors
+// possible and predicted field types (|possible_types| and |predicted_type|,
+// respectively). May log a suffixed version of the metric depending on
+// |metric_type|.
+void LogPredictionQualityMetrics(
+    const base::StringPiece& source,
+    const ServerFieldTypeSet& possible_types,
+    ServerFieldType predicted_type,
+    AutofillMetrics::QualityMetricType metric_type) {
+  // Generate histogram names.
+  const char* const suffix = GetQualityMetricTypeSuffix(metric_type);
+  std::string raw_data_histogram =
+      base::JoinString({"Autofill.FieldPrediction.", source, suffix}, "");
+  std::string aggregate_histogram = base::JoinString(
+      {"Autofill.FieldPredictionQuality.Aggregate.", source, suffix}, "");
+  std::string type_specific_histogram = base::JoinString(
+      {"Autofill.FieldPredictionQuality.ByFieldType.", source, suffix}, "");
+
+  // Get the best type classification we can for the field.
+  ServerFieldType actual_type =
+      GetActualFieldType(possible_types, predicted_type);
+
+  DVLOG(2) << "Predicted: " << AutofillType(predicted_type).ToString() << "; "
+           << "Actual: " << AutofillType(actual_type).ToString();
+
+  DCHECK_LE(predicted_type, UINT16_MAX);
+  DCHECK_LE(actual_type, UINT16_MAX);
+  UMA_HISTOGRAM_SPARSE_SLOWLY(raw_data_histogram,
+                              (predicted_type << 16) | actual_type);
+
+  // NO_SERVER_DATA is the equivalent of predicting UNKNOWN.
+  if (predicted_type == NO_SERVER_DATA)
+    predicted_type = UNKNOWN_TYPE;
+
+  // The actual type being EMPTY_TYPE is the same as UNKNOWN_TYPE for comparison
+  // purposes, but remember whether or not it was empty for more precise logging
+  // later.
+  bool is_empty = (actual_type == EMPTY_TYPE);
+  bool is_ambiguous = (actual_type == AMBIGUOUS_TYPE);
+  if (is_empty || is_ambiguous)
+    actual_type = UNKNOWN_TYPE;
+
+  // If the predicted and actual types match then it's either a true positive
+  // or a true negative (if they are both unknown). Do not log type specific
+  // true negatives (instead log a true positive for the "Ambiguous" type).
+  if (predicted_type == actual_type) {
+    if (actual_type == UNKNOWN_TYPE) {
+      // Only log aggregate true negative; do not log type specific metrics
+      // for UNKNOWN/EMPTY.
+      DVLOG(2) << "TRUE NEGATIVE";
+      LogUMAHistogramEnumeration(
+          aggregate_histogram,
+          (is_empty ? AutofillMetrics::TRUE_NEGATIVE_EMPTY
+                    : (is_ambiguous ? AutofillMetrics::TRUE_NEGATIVE_AMBIGUOUS
+                                    : AutofillMetrics::TRUE_NEGATIVE_UNKNOWN)),
+          AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+      return;
+    }
+
+    DVLOG(2) << "TRUE POSITIVE";
+    // Log both aggregate and type specific true positive if we correctly
+    // predict that type with which the field was filled.
+    LogUMAHistogramEnumeration(aggregate_histogram,
+                               AutofillMetrics::TRUE_POSITIVE,
+                               AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+    LogUMAHistogramEnumeration(
+        type_specific_histogram,
+        GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE),
+        KMaxFieldTypeGroupMetric);
+    return;
+  }
+
+  // Note: At this point predicted_type != actual type
+  // If actual type is UNKNOWN_TYPE then the prediction is a false positive.
+  // Further specialize the type of false positive by whether the field was
+  // empty or contained an unknown value.
+  if (actual_type == UNKNOWN_TYPE) {
+    DVLOG(2) << "FALSE POSITIVE";
+    auto metric =
+        (is_empty ? AutofillMetrics::FALSE_POSITIVE_EMPTY
+                  : (is_ambiguous ? AutofillMetrics::FALSE_POSITIVE_AMBIGUOUS
+                                  : AutofillMetrics::FALSE_POSITIVE_UNKNOWN));
+    LogUMAHistogramEnumeration(aggregate_histogram, metric,
+                               AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+    LogUMAHistogramEnumeration(type_specific_histogram,
+                               GetFieldTypeGroupMetric(predicted_type, metric),
+                               KMaxFieldTypeGroupMetric);
+    return;
+  }
+
+  // Note: At this point predicted_type != actual type, actual_type != UNKNOWN.
+  // If predicted type is UNKNOWN_TYPE then the prediction is a false negative
+  // unknown.
+  if (predicted_type == UNKNOWN_TYPE) {
+    DVLOG(2) << "FALSE NEGATIVE";
+    LogUMAHistogramEnumeration(aggregate_histogram,
+                               AutofillMetrics::FALSE_NEGATIVE_UNKNOWN,
+                               AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+    LogUMAHistogramEnumeration(
+        type_specific_histogram,
+        GetFieldTypeGroupMetric(actual_type,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        KMaxFieldTypeGroupMetric);
+    return;
+  }
+
+  DVLOG(2) << "MISMATCH";
+
+  // Note: At this point predicted_type != actual type, actual_type != UNKNOWN,
+  //       predicted_type != UNKNOWN.
+  // This is a mismatch. From the reference of the actual type, this is a false
+  // negative (it was T, but predicted U). From the reference of the prediction,
+  // this is a false positive (predicted it was T, but it was U).
+  LogUMAHistogramEnumeration(aggregate_histogram,
+                             AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+                             AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+  LogUMAHistogramEnumeration(
+      type_specific_histogram,
+      GetFieldTypeGroupMetric(actual_type,
+                              AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+      KMaxFieldTypeGroupMetric);
+  LogUMAHistogramEnumeration(
+      type_specific_histogram,
+      GetFieldTypeGroupMetric(predicted_type,
+                              AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+      KMaxFieldTypeGroupMetric);
 }
 
 }  // namespace
@@ -500,29 +663,28 @@
                             NUM_DEVELOPER_ENGAGEMENT_METRICS);
 }
 
-// static
-void AutofillMetrics::LogHeuristicTypePrediction(
-    FieldTypeQualityMetric metric,
-    ServerFieldType field_type,
-    QualityMetricType metric_type) {
-  LogTypeQualityMetric("Autofill.Quality.HeuristicType", metric, field_type,
-                       metric_type);
+void AutofillMetrics::LogHeuristicPredictionQualityMetrics(
+    const ServerFieldTypeSet& possible_types,
+    ServerFieldType predicted_type,
+    AutofillMetrics::QualityMetricType metric_type) {
+  LogPredictionQualityMetrics("Heuristic", possible_types, predicted_type,
+                              metric_type);
 }
 
-// static
-void AutofillMetrics::LogOverallTypePrediction(FieldTypeQualityMetric metric,
-                                               ServerFieldType field_type,
-                                               QualityMetricType metric_type) {
-  LogTypeQualityMetric("Autofill.Quality.PredictedType", metric, field_type,
-                       metric_type);
+void AutofillMetrics::LogServerPredictionQualityMetrics(
+    const ServerFieldTypeSet& possible_types,
+    ServerFieldType predicted_type,
+    AutofillMetrics::QualityMetricType metric_type) {
+  LogPredictionQualityMetrics("Server", possible_types, predicted_type,
+                              metric_type);
 }
 
-// static
-void AutofillMetrics::LogServerTypePrediction(FieldTypeQualityMetric metric,
-                                              ServerFieldType field_type,
-                                              QualityMetricType metric_type) {
-  LogTypeQualityMetric("Autofill.Quality.ServerType", metric, field_type,
-                       metric_type);
+void AutofillMetrics::LogOverallPredictionQualityMetrics(
+    const ServerFieldTypeSet& possible_types,
+    ServerFieldType predicted_type,
+    AutofillMetrics::QualityMetricType metric_type) {
+  LogPredictionQualityMetrics("Overall", possible_types, predicted_type,
+                              metric_type);
 }
 
 // static
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 974f0cbf..d849ce1 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_profile.h"
@@ -319,33 +320,92 @@
     NUM_SAVE_CARD_PROMPT_METRICS,
   };
 
-  // Metrics measuring how well we predict field types.  Exactly three such
-  // metrics are logged for each fillable field in a submitted form: for
-  // the heuristic prediction, for the crowd-sourced prediction, and for the
-  // overall prediction.
+  // Metrics measuring how well we predict field types.  These metric values are
+  // logged for each field in a submitted form for:
+  //     - the heuristic prediction
+  //     - the crowd-sourced (server) prediction
+  //     - for the overall prediction
+  //
+  // For each of these prediction types, these metrics are also logged by
+  // actual and predicted field type.
   enum FieldTypeQualityMetric {
-    // The field was found to be of type T, but autofill made no prediction.
-    TYPE_UNKNOWN = 0,
     // The field was found to be of type T, which matches the predicted type.
-    TYPE_MATCH,
-    // The field was found to be of type T, autofill predicted some other type.
-    TYPE_MISMATCH,
-    // The field was left empty and autofil predicted that the field type would
-    // be UNKNOWN.
-    TYPE_MATCH_EMPTY,
-    // The field was populated with data that did not match any part of the
-    // user's profile (it's type could not be determined). Autofill predicted
-    // the field's type would be UNKNOWN.
-    TYPE_MATCH_UNKNOWN,
-    // The field was left empty, autofill predicted the user would populate it
-    // with autofillable data.
-    TYPE_MISMATCH_EMPTY,
-    // The field was populated with data that did not match any part of the
-    // user's profile (it's type could not be determined). Autofill predicted
-    // the user would populate it with autofillable data.
-    TYPE_MISMATCH_UNKNOWN,
-    // This must be the last value.
-    NUM_FIELD_TYPE_QUALITY_METRICS,
+    // i.e. actual_type == predicted type == T
+    //
+    // This is captured as a type-specific log entry for T. Is is also captured
+    // as an aggregate (non-type-specific) log entry.
+    TRUE_POSITIVE = 0,
+
+    // The field type is AMBIGUOUS and autofill made no prediction.
+    // i.e. actual_type == AMBIGUOUS,predicted type == UNKNOWN|NO_SERVER_DATA.
+    //
+    // This is captured as an aggregate (non-type-specific) log entry. It is
+    // NOT captured by type-specific logging.
+    TRUE_NEGATIVE_AMBIGUOUS,
+
+    // The field type is UNKNOWN and autofill made no prediction.
+    // i.e. actual_type == UNKNOWN and predicted type == UNKNOWN|NO_SERVER_DATA.
+    //
+    // This is captured as an aggregate (non-type-specific) log entry. It is
+    // NOT captured by type-specific logging.
+    TRUE_NEGATIVE_UNKNOWN,
+
+    // The field type is EMPTY and autofill predicted UNKNOWN
+    // i.e. actual_type == EMPTY and predicted type == UNKNOWN|NO_SERVER_DATA.
+    //
+    // This is captured as an aggregate (non-type-specific) log entry. It is
+    // NOT captured by type-specific logging.
+    TRUE_NEGATIVE_EMPTY,
+
+    // Autofill predicted type T, but the field actually had a different type.
+    // i.e., actual_type == T, predicted_type = U, T != U,
+    //       UNKNOWN not in (T,U).
+    //
+    // This is captured as a type-specific log entry for U. It is NOT captured
+    // as an aggregate (non-type-specific) entry as this would double count with
+    // FALSE_NEGATIVE_MISMATCH logging captured for T.
+    FALSE_POSITIVE_MISMATCH,
+
+    // Autofill predicted type T, but the field actually matched multiple
+    // pieces of autofill data, none of which are T.
+    // i.e., predicted_type == T, actual_type = {U, V, ...),
+    //       T not in {U, V, ...}.
+    //
+    // This is captured as a type-specific log entry for T. It is also captured
+    // as an aggregate (non-type-specific) log entry.
+    FALSE_POSITIVE_AMBIGUOUS,
+
+    // The field type is UNKNOWN, but autofill predicted it to be of type T.
+    // i.e., actual_type == UNKNOWN, predicted_type = T, T != UNKNOWN
+    //
+    // This is captured as a type-specific log entry for T. Is is also captured
+    // as an aggregate (non-type-specific) log entry.
+    FALSE_POSITIVE_UNKNOWN,
+
+    // The field type is EMPTY, but autofill predicted it to be of type T.
+    // i.e., actual_type == EMPTY, predicted_type = T, T != UNKNOWN
+    //
+    // This is captured as a type-specific log entry for T. Is is also captured
+    // as an aggregate (non-type-specific) log entry.
+    FALSE_POSITIVE_EMPTY,
+
+    // The field is of type T, but autofill did not make a type prediction.
+    // i.e., actual_type == T, predicted_type = UNKNOWN, T != UNKNOWN.
+    //
+    // This is captured as a type-specific log entry for T. Is is also captured
+    // as an aggregate (non-type-specific) log entry.
+    FALSE_NEGATIVE_UNKNOWN,
+
+    // The field is of type T, but autofill predicted it to be of type U.
+    // i.e., actual_type == T, predicted_type = U, T != U,
+    //       UNKNOWN not in (T,U).
+    //
+    // This is captured as a type-specific log entry for T. Is is also captured
+    // as an aggregate (non-type-specific) log entry.
+    FALSE_NEGATIVE_MISMATCH,
+
+    // This must be last.
+    NUM_FIELD_TYPE_QUALITY_METRICS
   };
 
   enum QualityMetricType {
@@ -675,15 +735,18 @@
 
   static void LogDeveloperEngagementMetric(DeveloperEngagementMetric metric);
 
-  static void LogHeuristicTypePrediction(FieldTypeQualityMetric metric,
-                                         ServerFieldType field_type,
-                                         QualityMetricType metric_type);
-  static void LogOverallTypePrediction(FieldTypeQualityMetric metric,
-                                       ServerFieldType field_type,
-                                       QualityMetricType metric_type);
-  static void LogServerTypePrediction(FieldTypeQualityMetric metric,
-                                      ServerFieldType field_type,
-                                      QualityMetricType metric_type);
+  static void LogHeuristicPredictionQualityMetrics(
+      const ServerFieldTypeSet& possible_types,
+      ServerFieldType predicted_type,
+      QualityMetricType metric_type);
+  static void LogServerPredictionQualityMetrics(
+      const ServerFieldTypeSet& possible_types,
+      ServerFieldType predicted_type,
+      QualityMetricType metric_type);
+  static void LogOverallPredictionQualityMetrics(
+      const ServerFieldTypeSet& possible_types,
+      ServerFieldType predicted_type,
+      QualityMetricType metric_type);
 
   static void LogServerQueryMetric(ServerQueryMetric metric);
 
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 2e0eee8..65c3b283f 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -189,6 +189,19 @@
 
   bool IsAutofillEnabled() const override { return autofill_enabled_; }
 
+  void CreateAmbiguousProfiles() {
+    web_profiles_.clear();
+    CreateTestAutofillProfiles(&web_profiles_);
+
+    auto profile = base::MakeUnique<AutofillProfile>();
+    test::SetProfileInfo(profile.get(), "John", "Decca", "Public",
+                         "john@gmail.com", "Company", "123 Main St.", "unit 7",
+                         "Springfield", "Texas", "79401", "US", "2345678901");
+    profile->set_guid("00000000-0000-0000-0000-000000000003");
+    web_profiles_.push_back(std::move(profile));
+    Refresh();
+  }
+
  private:
   void CreateTestAutofillProfiles(
       std::vector<std::unique_ptr<AutofillProfile>>* profiles) {
@@ -522,111 +535,259 @@
   autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // Heuristic predictions.
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MISMATCH),
-      1);
+  {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate.Heuristic";
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType.Heuristic";
 
-  // Server predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MISMATCH), 1);
+    // Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE, 2);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER,
+                                AutofillMetrics::TRUE_POSITIVE),
+        1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS,
+                                AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_NUMBER,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+    // False Positive Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_NUMBER,
+                                AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
+        1);
+    // False Positive Empty:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL,
+                                AutofillMetrics::FALSE_POSITIVE_EMPTY),
+        1);
 
-  // Overall predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MISMATCH), 1);
+    // Sanity Check:
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
+    histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
+  }
+
+  // Server overrides heuristic so Overall and Server are the same predictions
+  // (as there were no test fields where server == NO_SERVER_DATA and heuristic
+  // != UNKNOWN_TYPE).
+  for (const std::string source : {"Server", "Overall"}) {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate." + source;
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source;
+
+    // Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE, 2);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
+                                AutofillMetrics::TRUE_POSITIVE),
+        1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL,
+                                AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FIRST,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+
+    // False Positive Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS,
+                                AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
+        1);
+    // False Positive Empty:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FIRST,
+                                AutofillMetrics::FALSE_POSITIVE_EMPTY),
+        1);
+
+    // Sanity Check:
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
+    histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
+  }
 }
 
 // Tests the true negatives (empty + no prediction and unknown + no prediction)
 // and false positives (empty + bad prediction and unknown + bad prediction)
 // are counted correctly.
 
-struct UnrecognizedOrEmptyFieldsCase {
+struct QualityMetricsTestCase {
+  const ServerFieldType predicted_field_type;
   const ServerFieldType actual_field_type;
-  const bool make_prediction;
-  const AutofillMetrics::FieldTypeQualityMetric metric_to_test;
 };
 
-class UnrecognizedOrEmptyFieldsTest
+class QualityMetricsTest
     : public AutofillMetricsTest,
-      public testing::WithParamInterface<UnrecognizedOrEmptyFieldsCase> {};
+      public testing::WithParamInterface<QualityMetricsTestCase> {
+ public:
+  const char* ValueForType(ServerFieldType type) {
+    switch (type) {
+      case EMPTY_TYPE:
+        return "";
+      case UNKNOWN_TYPE:
+        return "unknown";
+      case COMPANY_NAME:
+        return "RCA";
+      case NAME_FIRST:
+        return "Elvis";
+      case NAME_MIDDLE:
+        return "Aaron";
+      case NAME_LAST:
+        return "Presley";
+      case NAME_FULL:
+        return "Elvis Aaron Presley";
+      case EMAIL_ADDRESS:
+        return "buddy@gmail.com";
+      case PHONE_HOME_NUMBER:
+      case PHONE_HOME_WHOLE_NUMBER:
+      case PHONE_HOME_CITY_AND_NUMBER:
+        return "2345678901";
+      case ADDRESS_HOME_STREET_ADDRESS:
+        return "123 Apple St.\nunit 6";
+      case ADDRESS_HOME_LINE1:
+        return "123 Apple St.";
+      case ADDRESS_HOME_LINE2:
+        return "unit 6";
+      case ADDRESS_HOME_CITY:
+        return "Lubbock";
+      case ADDRESS_HOME_STATE:
+        return "Texas";
+      case ADDRESS_HOME_ZIP:
+        return "79401";
+      case ADDRESS_HOME_COUNTRY:
+        return "US";
+      case AMBIGUOUS_TYPE:
+        // This occurs as both a company name and a middle name once ambiguous
+        // profiles are created.
+        personal_data_->CreateAmbiguousProfiles();
+        return "Decca";
 
-TEST_P(UnrecognizedOrEmptyFieldsTest, QualityMetrics) {
+      default:
+        NOTREACHED();  // Fall through
+        return "unexpected!";
+    }
+  }
+
+  bool IsExampleOf(AutofillMetrics::FieldTypeQualityMetric metric,
+                   ServerFieldType predicted_type,
+                   ServerFieldType actual_type) {
+    switch (metric) {
+      case AutofillMetrics::TRUE_POSITIVE:
+        return unknown_equivalent_types_.count(actual_type) == 0 &&
+               predicted_type == actual_type;
+
+      case AutofillMetrics::TRUE_NEGATIVE_AMBIGUOUS:
+        return actual_type == AMBIGUOUS_TYPE && predicted_type == UNKNOWN_TYPE;
+
+      case AutofillMetrics::TRUE_NEGATIVE_UNKNOWN:
+        return actual_type == UNKNOWN_TYPE && predicted_type == UNKNOWN_TYPE;
+
+      case AutofillMetrics::TRUE_NEGATIVE_EMPTY:
+        return actual_type == EMPTY_TYPE && predicted_type == UNKNOWN_TYPE;
+
+      case AutofillMetrics::FALSE_POSITIVE_AMBIGUOUS:
+        return actual_type == AMBIGUOUS_TYPE && predicted_type != UNKNOWN_TYPE;
+
+      case AutofillMetrics::FALSE_POSITIVE_UNKNOWN:
+        return actual_type == UNKNOWN_TYPE && predicted_type != UNKNOWN_TYPE;
+
+      case AutofillMetrics::FALSE_POSITIVE_EMPTY:
+        return actual_type == EMPTY_TYPE && predicted_type != UNKNOWN_TYPE;
+
+      // False negative mismatch and false positive mismatch trigger on the same
+      // conditions:
+      //   - False positive prediction of predicted type
+      //   - False negative prediction of actual type
+      case AutofillMetrics::FALSE_POSITIVE_MISMATCH:
+      case AutofillMetrics::FALSE_NEGATIVE_MISMATCH:
+        return unknown_equivalent_types_.count(actual_type) == 0 &&
+               actual_type != predicted_type && predicted_type != UNKNOWN_TYPE;
+
+      case AutofillMetrics::FALSE_NEGATIVE_UNKNOWN:
+        return unknown_equivalent_types_.count(actual_type) == 0 &&
+               actual_type != predicted_type && predicted_type == UNKNOWN_TYPE;
+
+      default:
+        NOTREACHED();
+    }
+    return false;
+  }
+
+  static int FieldTypeCross(ServerFieldType predicted_type,
+                            ServerFieldType actual_type) {
+    EXPECT_LE(predicted_type, UINT16_MAX);
+    EXPECT_LE(actual_type, UINT16_MAX);
+    return (predicted_type << 16) | actual_type;
+  }
+
+  const ServerFieldTypeSet unknown_equivalent_types_{UNKNOWN_TYPE, EMPTY_TYPE,
+                                                     AMBIGUOUS_TYPE};
+};
+
+TEST_P(QualityMetricsTest, Classification) {
+  const std::vector<std::string> prediction_sources{"Heuristic", "Server",
+                                                    "Overall"};
   // Setup the test parameters.
-  const ServerFieldType actual_field_type = GetParam().actual_field_type;
-  const ServerFieldType heuristic_type =
-      GetParam().make_prediction ? EMAIL_ADDRESS : UNKNOWN_TYPE;
-  const ServerFieldType server_type =
-      GetParam().make_prediction ? EMAIL_ADDRESS : NO_SERVER_DATA;
-  const AutofillMetrics::FieldTypeQualityMetric metric_to_test =
-      GetParam().metric_to_test;
+  ServerFieldType actual_field_type = GetParam().actual_field_type;
+  ServerFieldType predicted_type = GetParam().predicted_field_type;
+
+  VLOG(2) << "Test Case = Predicted: "
+          << AutofillType(predicted_type).ToString() << "; "
+          << "Actual: " << AutofillType(actual_field_type).ToString();
 
   // Set up our form data.
   FormData form;
@@ -638,27 +799,26 @@
   AutofillField field;
 
   // Add a first name field, that is predicted correctly.
-  test::CreateTestFormField("first", "first", "Elvis", "text", &field);
-  field.set_possible_types({NAME_FIRST});
+  test::CreateTestFormField("first", "first", ValueForType(NAME_FIRST), "text",
+                            &field);
   form.fields.push_back(field);
   heuristic_types.push_back(NAME_FIRST);
   server_types.push_back(NAME_FIRST);
 
   // Add a last name field, that is predicted correctly.
-  test::CreateTestFormField("last", "last", "Presley", "test", &field);
-  field.set_possible_types({NAME_LAST});
+  test::CreateTestFormField("last", "last", ValueForType(NAME_LAST), "test",
+                            &field);
   form.fields.push_back(field);
   heuristic_types.push_back(NAME_LAST);
   server_types.push_back(NAME_LAST);
 
   // Add an empty or unknown field, that is predicted as per the test params.
   test::CreateTestFormField("Unknown", "Unknown",
-                            (actual_field_type == EMPTY_TYPE ? "" : "unknown"),
-                            "text", &field);
-  field.set_possible_types({actual_field_type});
+                            ValueForType(actual_field_type), "text", &field);
   form.fields.push_back(field);
-  heuristic_types.push_back(heuristic_type);
-  server_types.push_back(server_type);
+  heuristic_types.push_back(predicted_type);
+  server_types.push_back(predicted_type == UNKNOWN_TYPE ? NO_SERVER_DATA
+                                                        : predicted_type);
 
   // Simulate having seen this form on page load.
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
@@ -667,65 +827,122 @@
   base::HistogramTester histogram_tester;
   autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
-  // Validate the histogram counter values.
+  // Resolve any field type ambiguity.
+  if (actual_field_type == AMBIGUOUS_TYPE) {
+    if (predicted_type == COMPANY_NAME || predicted_type == NAME_MIDDLE)
+      actual_field_type = predicted_type;
+  }
+
+  // Validate the total samples and the crossed (predicted-to-actual) samples.
+  for (const auto& source : prediction_sources) {
+    const std::string crossed_histogram = "Autofill.FieldPrediction." + source;
+    const std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate." + source;
+    const std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source;
+
+    // Sanity Check:
+    histogram_tester.ExpectTotalCount(crossed_histogram, 3);
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 3);
+    histogram_tester.ExpectTotalCount(
+        by_field_type_histogram,
+        2 +
+            (predicted_type != UNKNOWN_TYPE &&
+             predicted_type != actual_field_type) +
+            (unknown_equivalent_types_.count(actual_field_type) == 0));
+
+    // The Crossed Histogram:
+    histogram_tester.ExpectBucketCount(
+        crossed_histogram, FieldTypeCross(NAME_FIRST, NAME_FIRST), 1);
+    histogram_tester.ExpectBucketCount(crossed_histogram,
+                                       FieldTypeCross(NAME_LAST, NAME_LAST), 1);
+    histogram_tester.ExpectBucketCount(
+        crossed_histogram,
+        FieldTypeCross((source == "Server" && predicted_type == UNKNOWN_TYPE
+                            ? NO_SERVER_DATA
+                            : predicted_type),
+                       actual_field_type),
+        1);
+  }
+
+  // Validate the individual histogram counter values.
   for (int i = 0; i < AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS; ++i) {
     // The metric enum value we're currently examining.
     auto metric = static_cast<AutofillMetrics::FieldTypeQualityMetric>(i);
 
-    // For the overall metric counts...
-    // If the current metric is the metric we're testing, then we expect its
-    // count to be 1. Otherwise, the metric's count should be zero (0) except
-    // for the TYPE_MATCH metric which should be 2 (because of the matching
-    // first and last name fields)
-    int overall_expected_count =
-        (metric == metric_to_test)
-            ? 1
-            : ((metric == AutofillMetrics::TYPE_MATCH) ? 2 : 0);
+    // The type specific expected count is 1 if (predicted, actual) is an
+    // example
+    int basic_expected_count =
+        IsExampleOf(metric, predicted_type, actual_field_type) ? 1 : 0;
 
-    histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType", metric,
-                                       overall_expected_count);
-    histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType", metric,
-                                       overall_expected_count);
-    histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType", metric,
-                                       overall_expected_count);
+    // For aggregate metrics don't capture aggregate FALSE_POSITIVE_MISMATCH.
+    // Note there are two true positive values (first and last name) hard-
+    // coded into the test.
+    int aggregate_expected_count =
+        (metric == AutofillMetrics::TRUE_POSITIVE ? 2 : 0) +
+        (metric == AutofillMetrics::FALSE_POSITIVE_MISMATCH
+             ? 0
+             : basic_expected_count);
 
-    // For the ByFieldType metric counts...
-    // We only examine the counter for the field_type being tested. If the
-    // current metric is the metric we're testing, then we expect its
-    // count to be 1 otherwise it should be 0.
-    int field_type_expected_count = (metric == metric_to_test) ? 1 : 0;
+    // If this test exercises the ambiguous middle name match, then validation
+    // of the name-specific metrics must include the true-positives created by
+    // the first and last name fields.
+    if (metric == AutofillMetrics::TRUE_POSITIVE &&
+        predicted_type == NAME_MIDDLE && actual_field_type == NAME_MIDDLE) {
+      basic_expected_count += 2;
+    }
 
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.HeuristicType.ByFieldType",
-        GetFieldTypeGroupMetric(actual_field_type, metric),
-        field_type_expected_count);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.ServerType.ByFieldType",
-        GetFieldTypeGroupMetric(actual_field_type, metric),
-        field_type_expected_count);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.PredictedType.ByFieldType",
-        GetFieldTypeGroupMetric(actual_field_type, metric),
-        field_type_expected_count);
+    // For metrics keyed to the actual field type, we don't capture unknown,
+    // empty or ambiguous and we don't capture false positive mismatches.
+    int expected_count_for_actual_type =
+        (unknown_equivalent_types_.count(actual_field_type) == 0 &&
+         metric != AutofillMetrics::FALSE_POSITIVE_MISMATCH)
+            ? basic_expected_count
+            : 0;
+
+    // For metrics keyed to the predicted field type, we don't capture unknown
+    // (empty is not a predictable value) and we don't capture false negative
+    // mismatches.
+    int expected_count_for_predicted_type =
+        (predicted_type != UNKNOWN_TYPE &&
+         metric != AutofillMetrics::FALSE_NEGATIVE_MISMATCH)
+            ? basic_expected_count
+            : 0;
+
+    for (const auto& source : prediction_sources) {
+      std::string aggregate_histogram =
+          "Autofill.FieldPredictionQuality.Aggregate." + source;
+      std::string by_field_type_histogram =
+          "Autofill.FieldPredictionQuality.ByFieldType." + source;
+      histogram_tester.ExpectBucketCount(aggregate_histogram, metric,
+                                         aggregate_expected_count);
+      histogram_tester.ExpectBucketCount(
+          by_field_type_histogram,
+          GetFieldTypeGroupMetric(actual_field_type, metric),
+          expected_count_for_actual_type);
+      histogram_tester.ExpectBucketCount(
+          by_field_type_histogram,
+          GetFieldTypeGroupMetric(predicted_type, metric),
+          expected_count_for_predicted_type);
+    }
   }
 }
 
 INSTANTIATE_TEST_CASE_P(
     AutofillMetricsTest,
-    UnrecognizedOrEmptyFieldsTest,
-    testing::Values(
-        UnrecognizedOrEmptyFieldsCase{EMPTY_TYPE,
-                                      /* make_prediction = */ false,
-                                      AutofillMetrics::TYPE_MATCH_EMPTY},
-        UnrecognizedOrEmptyFieldsCase{UNKNOWN_TYPE,
-                                      /* make_prediction = */ false,
-                                      AutofillMetrics::TYPE_MATCH_UNKNOWN},
-        UnrecognizedOrEmptyFieldsCase{EMPTY_TYPE,
-                                      /* make_prediction = */ true,
-                                      AutofillMetrics::TYPE_MISMATCH_EMPTY},
-        UnrecognizedOrEmptyFieldsCase{UNKNOWN_TYPE,
-                                      /* make_prediction = */ true,
-                                      AutofillMetrics::TYPE_MISMATCH_UNKNOWN}));
+    QualityMetricsTest,
+    testing::Values(QualityMetricsTestCase{UNKNOWN_TYPE, EMPTY_TYPE},
+                    QualityMetricsTestCase{UNKNOWN_TYPE, UNKNOWN_TYPE},
+                    QualityMetricsTestCase{UNKNOWN_TYPE, AMBIGUOUS_TYPE},
+                    QualityMetricsTestCase{UNKNOWN_TYPE, EMAIL_ADDRESS},
+                    QualityMetricsTestCase{EMAIL_ADDRESS, EMPTY_TYPE},
+                    QualityMetricsTestCase{EMAIL_ADDRESS, UNKNOWN_TYPE},
+                    QualityMetricsTestCase{EMAIL_ADDRESS, AMBIGUOUS_TYPE},
+                    QualityMetricsTestCase{EMAIL_ADDRESS, EMAIL_ADDRESS},
+                    QualityMetricsTestCase{EMAIL_ADDRESS, COMPANY_NAME},
+                    QualityMetricsTestCase{COMPANY_NAME, EMAIL_ADDRESS},
+                    QualityMetricsTestCase{NAME_MIDDLE, AMBIGUOUS_TYPE},
+                    QualityMetricsTestCase{COMPANY_NAME, AMBIGUOUS_TYPE}));
 
 // Ensures that metrics that measure timing some important Autofill functions
 // actually are recorded and retrieved.
@@ -831,92 +1048,127 @@
   autofill_manager_->RunRunLoop();
 
   // Heuristic predictions.
-  // Unknown:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.NoSubmission",
-      AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.NoSubmission",
-      AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.NoSubmission",
-      AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MISMATCH),
-      1);
+  {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate.Heuristic.NoSubmission";
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType.Heuristic.NoSubmission";
+    // False Negative:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE, 2);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
+                                AutofillMetrics::TRUE_POSITIVE),
+        1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS,
+                                AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_NUMBER,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+    // False Positives:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL,
+                                AutofillMetrics::FALSE_POSITIVE_EMPTY),
+        1);
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_NUMBER,
+                                AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
+        1);
 
-  // Server predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType.NoSubmission",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType.NoSubmission",
-                                     AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType.NoSubmission",
-                                     AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MISMATCH), 1);
+    // Sanity Check:
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
+    histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
+  }
 
-  // Overall predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.NoSubmission",
-      AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.NoSubmission",
-      AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MATCH), 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                              AutofillMetrics::TYPE_MATCH),
-      1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.NoSubmission",
-      AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType.NoSubmission",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MISMATCH), 1);
+  // Server predictions override heuristics, so server and overall will be the
+  // same.
+  for (const std::string source : {"Server", "Overall"}) {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate." + source + ".NoSubmission";
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source +
+        ".NoSubmission";
+
+    // Unknown.
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE, 2);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
+                                AutofillMetrics::TRUE_POSITIVE),
+        1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL,
+                                AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FIRST,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+
+    // False Positives:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FIRST,
+                                AutofillMetrics::FALSE_POSITIVE_EMPTY),
+        1);
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS,
+                                AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
+        1);
+
+    // Sanity Check:
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
+    histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
+  }
 }
 
 // Test that we log quality metrics for heuristics and server predictions based
@@ -987,53 +1239,46 @@
   EXPECT_EQ(ADDRESS_HOME_ZIP,
             form_structure_ptr->field(2)->Type().GetStorableType());
 
-  // Heuristic predictions.
-  // Unknown:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP, AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_MATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(NAME_LAST, AutofillMetrics::TYPE_MATCH), 1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(NAME_MIDDLE, AutofillMetrics::TYPE_MISMATCH), 1);
+  for (const std::string source : {"Heuristic", "Server"}) {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate." + source +
+        ".BasedOnAutocomplete";
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source +
+        ".BasedOnAutocomplete";
 
-  // Server predictions.
-  // Unknown:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP, AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_MATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(NAME_LAST, AutofillMetrics::TYPE_MATCH), 1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.BasedOnAutocomplete",
-      AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType.BasedOnAutocomplete",
-      GetFieldTypeGroupMetric(NAME_MIDDLE, AutofillMetrics::TYPE_MISMATCH), 1);
+    // Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_LAST, AutofillMetrics::TRUE_POSITIVE), 1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FIRST,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_MIDDLE,
+                                AutofillMetrics::FALSE_POSITIVE_MISMATCH),
+        1);
+
+    // Sanity check.
+    histogram_tester.ExpectTotalCount(aggregate_histogram, 3);
+    histogram_tester.ExpectTotalCount(by_field_type_histogram, 4);
+  }
 }
 
 // Test that we log UPI Virtual Payment Address.
@@ -1077,195 +1322,6 @@
       "Autofill.UserHappiness", AutofillMetrics::USER_DID_ENTER_UPI_VPA, 1);
 }
 
-// Test that we do not log RAPPOR metrics when the number of mismatches is not
-// high enough.
-TEST_F(AutofillMetricsTest, Rappor_LowMismatchRate_NoMetricsReported) {
-  // Set up our form data.
-  FormData form;
-  form.name = ASCIIToUTF16("TestForm");
-  form.origin = GURL("http://example.com/form.html");
-  form.action = GURL("http://example.com/submit.html");
-
-  std::vector<ServerFieldType> heuristic_types, server_types;
-  FormFieldData field;
-
-  test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley",
-                            "text", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(NAME_FULL);
-  server_types.push_back(NAME_FULL);
-
-  test::CreateTestFormField("Autofill Failed", "autofillfailed",
-                            "buddy@gmail.com", "text", &field);
-  field.is_autofilled = false;
-  form.fields.push_back(field);
-  heuristic_types.push_back(EMAIL_ADDRESS);
-  server_types.push_back(NAME_LAST);
-
-  test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
-  server_types.push_back(EMAIL_ADDRESS);
-
-  // Simulate having seen this form on page load.
-  autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-
-  // Simulate form submission.
-  autofill_manager_->SubmitForm(form, TimeTicks::Now());
-
-  // The number of mismatches did not trigger the RAPPOR metric logging.
-  EXPECT_EQ(0, autofill_client_.test_rappor_service()->GetReportsCount());
-}
-
-// Test that we don't log RAPPOR metrics in the case heuristics and/or server
-// have no data.
-TEST_F(AutofillMetricsTest, Rappor_NoDataServerAndHeuristic_NoMetricsReported) {
-  // Set up our form data.
-  FormData form;
-  form.name = ASCIIToUTF16("TestForm");
-  form.origin = GURL("http://example.com/form.html");
-  form.action = GURL("http://example.com/submit.html");
-
-  std::vector<ServerFieldType> heuristic_types, server_types;
-  FormFieldData field;
-
-  test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley",
-                            "text", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(UNKNOWN_TYPE);
-  server_types.push_back(NO_SERVER_DATA);
-
-  test::CreateTestFormField("Autofill Failed", "autofillfailed",
-                            "buddy@gmail.com", "text", &field);
-  field.is_autofilled = false;
-  form.fields.push_back(field);
-  heuristic_types.push_back(UNKNOWN_TYPE);
-  server_types.push_back(NO_SERVER_DATA);
-
-  test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(UNKNOWN_TYPE);
-  server_types.push_back(NO_SERVER_DATA);
-
-  // Simulate having seen this form on page load.
-  autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-
-  // Simulate form submission.
-  autofill_manager_->SubmitForm(form, TimeTicks::Now());
-
-  // No RAPPOR metrics are logged in the case of multiple UNKNOWN_TYPE and
-  // NO_SERVER_DATA for heuristics and server predictions, respectively.
-  EXPECT_EQ(0, autofill_client_.test_rappor_service()->GetReportsCount());
-}
-
-// Test that we log high number of mismatches for the server prediction.
-TEST_F(AutofillMetricsTest, Rappor_HighServerMismatchRate_MetricsReported) {
-  // Set up our form data.
-  FormData form;
-  form.name = ASCIIToUTF16("TestForm");
-  form.origin = GURL("http://example.com/form.html");
-  form.action = GURL("http://example.com/submit.html");
-
-  std::vector<ServerFieldType> heuristic_types, server_types;
-  FormFieldData field;
-
-  test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley",
-                            "text", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(NAME_FULL);
-  server_types.push_back(NAME_FIRST);
-
-  test::CreateTestFormField("Autofill Failed", "autofillfailed",
-                            "buddy@gmail.com", "text", &field);
-  field.is_autofilled = false;
-  form.fields.push_back(field);
-  heuristic_types.push_back(PHONE_HOME_NUMBER);
-  server_types.push_back(NAME_LAST);
-
-  test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
-  server_types.push_back(EMAIL_ADDRESS);
-
-  // Simulate having seen this form on page load.
-  autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-
-  // Simulate form submission.
-  autofill_manager_->SubmitForm(form, TimeTicks::Now());
-
-  // The number of mismatches did trigger the RAPPOR metric logging for server
-  // predictions.
-  EXPECT_EQ(1, autofill_client_.test_rappor_service()->GetReportsCount());
-  std::string sample;
-  rappor::RapporType type;
-  EXPECT_FALSE(
-      autofill_client_.test_rappor_service()->GetRecordedSampleForMetric(
-          "Autofill.HighNumberOfHeuristicMismatches", &sample, &type));
-  EXPECT_TRUE(
-      autofill_client_.test_rappor_service()->GetRecordedSampleForMetric(
-          "Autofill.HighNumberOfServerMismatches", &sample, &type));
-  EXPECT_EQ("example.com", sample);
-  EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
-}
-
-// Test that we log high number of mismatches for the heuristic predictions.
-TEST_F(AutofillMetricsTest, Rappor_HighHeuristicMismatchRate_MetricsReported) {
-  // Set up our form data.
-  FormData form;
-  form.name = ASCIIToUTF16("TestForm");
-  form.origin = GURL("http://example.com/form.html");
-  form.action = GURL("http://example.com/submit.html");
-
-  std::vector<ServerFieldType> heuristic_types, server_types;
-  FormFieldData field;
-
-  test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley",
-                            "text", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(NAME_FIRST);
-  server_types.push_back(NAME_FULL);
-
-  test::CreateTestFormField("Autofill Failed", "autofillfailed",
-                            "buddy@gmail.com", "text", &field);
-  field.is_autofilled = false;
-  form.fields.push_back(field);
-  heuristic_types.push_back(PHONE_HOME_NUMBER);
-  server_types.push_back(NAME_LAST);
-
-  test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field);
-  field.is_autofilled = true;
-  form.fields.push_back(field);
-  heuristic_types.push_back(EMAIL_ADDRESS);
-  server_types.push_back(PHONE_HOME_WHOLE_NUMBER);
-
-  // Simulate having seen this form on page load.
-  autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-
-  // Simulate form submission.
-  autofill_manager_->SubmitForm(form, TimeTicks::Now());
-
-  // The number of mismatches did trigger the RAPPOR metric logging for
-  // heuristic predictions.
-  EXPECT_EQ(1, autofill_client_.test_rappor_service()->GetReportsCount());
-  std::string sample;
-  rappor::RapporType type;
-  EXPECT_FALSE(
-      autofill_client_.test_rappor_service()->GetRecordedSampleForMetric(
-          "Autofill.HighNumberOfServerMismatches", &sample, &type));
-  EXPECT_TRUE(
-      autofill_client_.test_rappor_service()->GetRecordedSampleForMetric(
-          "Autofill.HighNumberOfHeuristicMismatches", &sample, &type));
-  EXPECT_EQ("example.com", sample);
-  EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
-}
-
 // Verify that when a field is annotated with the autocomplete attribute, its
 // predicted type is remembered when quality metrics are logged.
 TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) {
@@ -1293,69 +1349,43 @@
 
   std::vector<FormData> forms(1, form);
 
-  {
-    base::HistogramTester histogram_tester;
-    autofill_manager_->OnFormsSeen(forms, TimeTicks());
-    // We change the value of the text fields to change the default/seen values
-    // (hence the values are not cleared in UpdateFromCache). The new values
-    // match what is in the test profile.
-    form.fields[1].value = base::ASCIIToUTF16("79401");
-    form.fields[2].value = base::ASCIIToUTF16("2345678901");
-    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+  base::HistogramTester histogram_tester;
+  autofill_manager_->OnFormsSeen(forms, TimeTicks());
 
+  // We change the value of the text fields to change the default/seen values
+  // (hence the values are not cleared in UpdateFromCache). The new values
+  // match what is in the test profile.
+  form.fields[1].value = base::ASCIIToUTF16("79401");
+  form.fields[2].value = base::ASCIIToUTF16("2345678901");
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
+
+  for (const std::string source : {"Heuristic", "Server", "Overall"}) {
+    std::string histogram_name =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source;
     // First verify that country was not predicted by client or server.
     histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.ServerType.ByFieldType",
+        histogram_name,
         GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.HeuristicType.ByFieldType",
-        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    // We expect a match for country because it had |autocomplete_attribute|.
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.PredictedType.ByFieldType",
-        GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY,
-                                AutofillMetrics::TYPE_MATCH),
+                                source == "Overall"
+                                    ? AutofillMetrics::TRUE_POSITIVE
+                                    : AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
         1);
 
     // We did not predict zip code or phone number, because they did not have
     // |autocomplete_attribute|, nor client or server predictions.
     histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.ServerType.ByFieldType",
+        histogram_name,
         GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP,
-                                AutofillMetrics::TYPE_UNKNOWN),
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
         1);
     histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.HeuristicType.ByFieldType",
-        GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.PredictedType.ByFieldType",
-        GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.ServerType.ByFieldType",
+        histogram_name,
         GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.HeuristicType.ByFieldType",
-        GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                                AutofillMetrics::TYPE_UNKNOWN),
-        1);
-    histogram_tester.ExpectBucketCount(
-        "Autofill.Quality.PredictedType.ByFieldType",
-        GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER,
-                                AutofillMetrics::TYPE_UNKNOWN),
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
         1);
 
     // Sanity check.
-    histogram_tester.ExpectTotalCount("Autofill.Quality.PredictedType", 3);
+    histogram_tester.ExpectTotalCount(histogram_name, 3);
   }
 }
 
@@ -1415,88 +1445,45 @@
   base::HistogramTester histogram_tester;
   autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
-  // Heuristic predictions.
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_STATE,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_MATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_CITY, AutofillMetrics::TYPE_MATCH),
-      1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MATCH), 1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType",
-                                     AutofillMetrics::TYPE_MISMATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.HeuristicType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MISMATCH),
-      1);
+  for (const std::string source : {"Heuristic", "Server", "Overall"}) {
+    std::string aggregate_histogram =
+        "Autofill.FieldPredictionQuality.Aggregate." + source;
+    std::string by_field_type_histogram =
+        "Autofill.FieldPredictionQuality.ByFieldType." + source;
 
-  // Server predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_STATE,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_MATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MATCH), 1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType",
-                                     AutofillMetrics::TYPE_MISMATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_CITY,
-                              AutofillMetrics::TYPE_MISMATCH),
-      1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.ServerType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MISMATCH),
-      1);
-
-  // Overall predictions:
-  // Unknown:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_UNKNOWN, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_STATE,
-                              AutofillMetrics::TYPE_UNKNOWN),
-      1);
-  // Match:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_MATCH, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MATCH), 1);
-  // Mismatch:
-  histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType",
-                                     AutofillMetrics::TYPE_MISMATCH, 2);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(ADDRESS_HOME_CITY,
-                              AutofillMetrics::TYPE_MISMATCH),
-      1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.Quality.PredictedType.ByFieldType",
-      GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TYPE_MISMATCH),
-      1);
+    // Unknown:
+    histogram_tester.ExpectBucketCount(
+        aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_STATE,
+                                AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
+        1);
+    // Match:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::TRUE_POSITIVE,
+                                       source == "Heuristic" ? 2 : 1);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1);
+    // Mismatch:
+    histogram_tester.ExpectBucketCount(aggregate_histogram,
+                                       AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+                                       source == "Heuristic" ? 1 : 2);
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(EMAIL_ADDRESS,
+                                AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+    // Source dependent:
+    histogram_tester.ExpectBucketCount(
+        by_field_type_histogram,
+        GetFieldTypeGroupMetric(ADDRESS_HOME_CITY,
+                                source == "Heuristic"
+                                    ? AutofillMetrics::TRUE_POSITIVE
+                                    : AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
+        1);
+  }
 }
 
 // Verify that when submitting an autofillable form, the stored profile metric
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 2b97a2f..b58d1a13 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -131,6 +131,7 @@
 
     case NO_SERVER_DATA:
     case EMPTY_TYPE:
+    case AMBIGUOUS_TYPE:
     case PHONE_FAX_NUMBER:
     case PHONE_FAX_CITY_CODE:
     case PHONE_FAX_COUNTRY_CODE:
@@ -771,6 +772,9 @@
     case CONFIRMATION_PASSWORD:
       return "CONFIRMATION_PASSWORD";
 
+    case AMBIGUOUS_TYPE:
+      return "AMBIGUOUS_TYPE";
+
     case MAX_VALID_FIELD_TYPE:
       return std::string();
   }
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index cee305f..1b1ab99 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -163,9 +163,14 @@
   // forms.
   CONFIRMATION_PASSWORD = 95,
 
+  // The data entered by the user matches multiple pieces of autofill data,
+  // none of which were predicted by autofill. This value is used for metrics
+  // only, it is not a predicted nor uploaded type.
+  AMBIGUOUS_TYPE = 96,
+
   // No new types can be added without a corresponding change to the Autofill
   // server.
-  MAX_VALID_FIELD_TYPE = 96,
+  MAX_VALID_FIELD_TYPE = 97,
 };
 
 // The list of all HTML autocomplete field type hints supported by Chrome.
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 9a7b87d..10a83809 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -47,10 +47,6 @@
 const char kBillingMode[] = "billing";
 const char kShippingMode[] = "shipping";
 
-// A form is considered to have a high prediction mismatch rate if the number of
-// mismatches exceeds this threshold.
-const int kNumberOfMismatchesThreshold = 3;
-
 // Only removing common name prefixes if we have a minimum number of fields and
 // a minimum prefix length. These values are chosen to avoid cases such as two
 // fields with "address1" and "address2" and be effective against web frameworks
@@ -701,8 +697,6 @@
     bool did_show_suggestions,
     bool observed_submission) const {
   size_t num_detected_field_types = 0;
-  size_t num_server_mismatches = 0;
-  size_t num_heuristic_mismatches = 0;
   size_t num_edited_autofilled_fields = 0;
   bool did_autofill_all_possible_fields = true;
   bool did_autofill_some_possible_fields = false;
@@ -741,93 +735,21 @@
     const ServerFieldTypeSet& field_types = field->possible_types();
     DCHECK(!field_types.empty());
 
-    // If the field data is empty, or unrecognized, log whether or not autofill
-    // predicted that it would be populated with an autofillable data type.
-    bool has_empty_data = field_types.count(EMPTY_TYPE) != 0;
-    bool has_unrecognized_data = field_types.count(UNKNOWN_TYPE) != 0;
-    if (has_empty_data || has_unrecognized_data) {
-      AutofillMetrics::FieldTypeQualityMetric match_empty_or_unknown =
-          has_empty_data ? AutofillMetrics::TYPE_MATCH_EMPTY
-                         : AutofillMetrics::TYPE_MATCH_UNKNOWN;
-      AutofillMetrics::FieldTypeQualityMetric mismatch_empty_or_unknown =
-          has_empty_data ? AutofillMetrics::TYPE_MISMATCH_EMPTY
-                         : AutofillMetrics::TYPE_MISMATCH_UNKNOWN;
-      ServerFieldType field_type = has_empty_data ? EMPTY_TYPE : UNKNOWN_TYPE;
-      AutofillMetrics::LogHeuristicTypePrediction(
-          (heuristic_type == UNKNOWN_TYPE ? match_empty_or_unknown
-                                          : mismatch_empty_or_unknown),
-          field_type, metric_type);
-      AutofillMetrics::LogServerTypePrediction(
-          (server_type == NO_SERVER_DATA ? match_empty_or_unknown
-                                         : mismatch_empty_or_unknown),
-          field_type, metric_type);
-      AutofillMetrics::LogOverallTypePrediction(
-          (predicted_type == UNKNOWN_TYPE ? match_empty_or_unknown
-                                          : mismatch_empty_or_unknown),
-          field_type, metric_type);
+    AutofillMetrics::LogHeuristicPredictionQualityMetrics(
+        field_types, heuristic_type, metric_type);
+    AutofillMetrics::LogServerPredictionQualityMetrics(field_types, server_type,
+                                                       metric_type);
+    AutofillMetrics::LogOverallPredictionQualityMetrics(
+        field_types, predicted_type, metric_type);
+
+    if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE))
       continue;
-    }
 
     ++num_detected_field_types;
     if (field->is_autofilled)
       did_autofill_some_possible_fields = true;
     else
       did_autofill_all_possible_fields = false;
-
-    // Collapse field types that Chrome treats as identical, e.g. home and
-    // billing address fields.
-    ServerFieldTypeSet collapsed_field_types;
-    for (const auto& it : field_types) {
-      // Since we currently only support US phone numbers, the (city code + main
-      // digits) number is almost always identical to the whole phone number.
-      // TODO(isherman): Improve this logic once we add support for
-      // international numbers.
-      if (it == PHONE_HOME_CITY_AND_NUMBER)
-        collapsed_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
-      else
-        collapsed_field_types.insert(AutofillType(it).GetStorableType());
-    }
-
-    // Capture the field's type, if it is unambiguous.
-    ServerFieldType field_type = UNKNOWN_TYPE;
-    if (collapsed_field_types.size() == 1)
-      field_type = *collapsed_field_types.begin();
-
-    // Log heuristic, server, and overall type quality metrics.
-    if (heuristic_type == UNKNOWN_TYPE) {
-      AutofillMetrics::LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
-                                                  field_type, metric_type);
-    } else if (field_types.count(heuristic_type)) {
-      AutofillMetrics::LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
-                                                  field_type, metric_type);
-    } else {
-      ++num_heuristic_mismatches;
-      AutofillMetrics::LogHeuristicTypePrediction(
-          AutofillMetrics::TYPE_MISMATCH, field_type, metric_type);
-    }
-
-    if (server_type == NO_SERVER_DATA) {
-      AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
-                                               field_type, metric_type);
-    } else if (field_types.count(server_type)) {
-      AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
-                                               field_type, metric_type);
-    } else {
-      ++num_server_mismatches;
-      AutofillMetrics::LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
-                                               field_type, metric_type);
-    }
-
-    if (predicted_type == UNKNOWN_TYPE) {
-      AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
-                                                field_type, metric_type);
-    } else if (field_types.count(predicted_type)) {
-      AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
-                                                field_type, metric_type);
-    } else {
-      AutofillMetrics::LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
-                                                field_type, metric_type);
-    }
   }
 
   AutofillMetrics::LogNumberOfEditedAutofilledFields(
@@ -852,18 +774,6 @@
             AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS;
       }
 
-      // Log some RAPPOR metrics for problematic cases.
-      if (num_server_mismatches >= kNumberOfMismatchesThreshold) {
-        rappor::SampleDomainAndRegistryFromGURL(
-            rappor_service, "Autofill.HighNumberOfServerMismatches",
-            source_url_);
-      }
-      if (num_heuristic_mismatches >= kNumberOfMismatchesThreshold) {
-        rappor::SampleDomainAndRegistryFromGURL(
-            rappor_service, "Autofill.HighNumberOfHeuristicMismatches",
-            source_url_);
-      }
-
       // Unlike the other times, the |submission_time| should always be
       // available.
       DCHECK(!submission_time.is_null());
@@ -904,38 +814,20 @@
 }
 
 void FormStructure::LogQualityMetricsBasedOnAutocomplete() const {
+  const AutofillMetrics::QualityMetricType metric_type =
+      AutofillMetrics::TYPE_AUTOCOMPLETE_BASED;
   for (const auto& field : fields_) {
     if (field->html_type() != HTML_TYPE_UNSPECIFIED &&
         field->html_type() != HTML_TYPE_UNRECOGNIZED) {
       // The type inferred by the autocomplete attribute.
-      AutofillType type(field->html_type(), field->html_mode());
-      ServerFieldType actual_field_type = type.GetStorableType();
+      ServerFieldTypeSet actual_field_type_set{
+          AutofillType(field->html_type(), field->html_mode())
+              .GetStorableType()};
 
-      const AutofillMetrics::QualityMetricType metric_type =
-          AutofillMetrics::TYPE_AUTOCOMPLETE_BASED;
-      // Log the quality of our heuristics predictions.
-      if (field->heuristic_type() == UNKNOWN_TYPE) {
-        AutofillMetrics::LogHeuristicTypePrediction(
-            AutofillMetrics::TYPE_UNKNOWN, actual_field_type, metric_type);
-      } else if (field->heuristic_type() == actual_field_type) {
-        AutofillMetrics::LogHeuristicTypePrediction(
-            AutofillMetrics::TYPE_MATCH, actual_field_type, metric_type);
-      } else {
-        AutofillMetrics::LogHeuristicTypePrediction(
-            AutofillMetrics::TYPE_MISMATCH, actual_field_type, metric_type);
-      }
-
-      // Log the quality of our server predictions.
-      if (field->server_type() == NO_SERVER_DATA) {
-        AutofillMetrics::LogServerTypePrediction(
-            AutofillMetrics::TYPE_UNKNOWN, actual_field_type, metric_type);
-      } else if (field->server_type() == actual_field_type) {
-        AutofillMetrics::LogServerTypePrediction(
-            AutofillMetrics::TYPE_MATCH, actual_field_type, metric_type);
-      } else {
-        AutofillMetrics::LogServerTypePrediction(
-            AutofillMetrics::TYPE_MISMATCH, actual_field_type, metric_type);
-      }
+      AutofillMetrics::LogHeuristicPredictionQualityMetrics(
+          actual_field_type_set, field->heuristic_type(), metric_type);
+      AutofillMetrics::LogServerPredictionQualityMetrics(
+          actual_field_type_set, field->server_type(), metric_type);
     }
   }
 }
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index a87a22a..b22b47bc 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -492,6 +492,14 @@
     widget_->UpdateWindowTitle();
 }
 
+void ShellSurface::SetIcon(const gfx::ImageSkia& icon) {
+  TRACE_EVENT0("exo", "ShellSurface::SetIcon");
+
+  icon_ = icon;
+  if (widget_)
+    widget_->UpdateWindowIcon();
+}
+
 void ShellSurface::SetSystemModal(bool system_modal) {
   // System modal container is used by clients to implement client side
   // managed system modal dialogs using a single ShellSurface instance.
@@ -829,6 +837,10 @@
   return title_;
 }
 
+gfx::ImageSkia ShellSurface::GetWindowIcon() {
+  return icon_;
+}
+
 void ShellSurface::SaveWindowPlacement(const gfx::Rect& bounds,
                                        ui::WindowShowState show_state) {
   if (bounds_mode_ != BoundsMode::CLIENT)
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index d528f11..6991ccaf 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -140,9 +140,12 @@
   // Set whether the surface is always on top.
   void SetAlwaysOnTop(bool always_on_top);
 
-  // Set title for surface.
+  // Set title for the surface.
   void SetTitle(const base::string16& title);
 
+  // Set icon for the surface.
+  void SetIcon(const gfx::ImageSkia& icon);
+
   // Sets the system modality.
   void SetSystemModal(bool system_modal);
 
@@ -227,6 +230,7 @@
   bool CanMaximize() const override;
   bool CanMinimize() const override;
   base::string16 GetWindowTitle() const override;
+  gfx::ImageSkia GetWindowIcon() override;
   void SaveWindowPlacement(const gfx::Rect& bounds,
                            ui::WindowShowState show_state) override;
   bool GetSavedWindowPlacement(const views::Widget* widget,
@@ -373,6 +377,7 @@
   bool shadow_underlay_in_surface_ = true;
   bool pending_shadow_underlay_in_surface_ = true;
   bool system_modal_ = false;
+  gfx::ImageSkia icon_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellSurface);
 };
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 1aa9dc0..e900253 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -85,6 +85,32 @@
 // Used for testing whether zero suggest is ever available.
 constexpr char kArbitraryInsecureUrlString[] = "http://www.google.com/";
 
+// Returns true if the folowing conditions are valid:
+// * The |default_provider| is Google.
+// * The user is in the ZeroSuggestRedirectToChrome field trial.
+// * The field trial provides a valid server address where the suggest request
+//   is redirected.
+// * The suggest request is over HTTPS. This avoids leaking the current page URL
+//   or personal data in unencrypted network traffic.
+// Note: these checks are in addition to CanSendUrl() on the default contextual
+// suggestion URL.
+bool UseExperimentalSuggestService(const TemplateURLService& default_provider) {
+  const TemplateURL& default_provider_url =
+      *default_provider.GetDefaultSearchProvider();
+  const SearchTermsData& search_terms_data =
+      default_provider.search_terms_data();
+  if ((default_provider_url.GetEngineType(search_terms_data) !=
+       SEARCH_ENGINE_GOOGLE) ||
+      !OmniboxFieldTrial::InZeroSuggestRedirectToChromeFieldTrial())
+    return false;
+  // Check that the suggest URL for redirect to chrome field trial is valid.
+  const GURL suggest_url(
+      OmniboxFieldTrial::ZeroSuggestRedirectToChromeServerAddress());
+  if (!suggest_url.is_valid())
+    return false;
+  return suggest_url.SchemeIsCryptographic();
+}
+
 }  // namespace
 
 // static
@@ -120,8 +146,7 @@
   current_page_classification_ = input.current_page_classification();
   current_url_match_ = MatchForCurrentURL();
 
-  std::string url_string = GetContextualSuggestionsUrl();
-  GURL suggest_url(url_string);
+  GURL suggest_url(GetContextualSuggestionsUrl());
   if (!suggest_url.is_valid())
     return;
 
@@ -153,11 +178,11 @@
       !OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() &&
       !OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) {
     // Update suggest_url to include the current_page_url.
-    if (OmniboxFieldTrial::InZeroSuggestRedirectToChromeFieldTrial()) {
-      url_string +=
+    if (UseExperimentalSuggestService(*template_url_service)) {
+      suggest_url = GURL(
+          OmniboxFieldTrial::ZeroSuggestRedirectToChromeServerAddress() +
           "/url=" + net::EscapePath(current_query_) +
-          OmniboxFieldTrial::ZeroSuggestRedirectToChromeAdditionalFields();
-      suggest_url = GURL(url_string);
+          OmniboxFieldTrial::ZeroSuggestRedirectToChromeAdditionalFields());
     } else {
       base::string16 prefix;
       TemplateURLRef::SearchTermsArgs search_term_args(prefix);
@@ -553,8 +578,6 @@
 }
 
 std::string ZeroSuggestProvider::GetContextualSuggestionsUrl() const {
-  // Without a default search provider, refuse to do anything (even if the user
-  // is in the redirect-to-chrome field trial).
   const TemplateURLService* template_url_service =
       client()->GetTemplateURLService();
   const TemplateURL* default_provider =
@@ -562,8 +585,6 @@
   if (default_provider == nullptr)
     return std::string();
 
-  if (OmniboxFieldTrial::InZeroSuggestRedirectToChromeFieldTrial())
-    return OmniboxFieldTrial::ZeroSuggestRedirectToChromeServerAddress();
   base::string16 prefix;
   TemplateURLRef::SearchTermsArgs search_term_args(prefix);
   return default_provider->suggestions_url_ref().ReplaceSearchTerms(
diff --git a/components/reading_list/ios/BUILD.gn b/components/reading_list/ios/BUILD.gn
index 5d605fb..eb4ed058 100644
--- a/components/reading_list/ios/BUILD.gn
+++ b/components/reading_list/ios/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("ios") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "favicon_web_state_dispatcher.h",
     "reading_list_model_bridge_observer.h",
diff --git a/components/reading_list/ios/reading_list_model_bridge_observer.h b/components/reading_list/ios/reading_list_model_bridge_observer.h
index 33a433e..dde8f6c 100644
--- a/components/reading_list/ios/reading_list_model_bridge_observer.h
+++ b/components/reading_list/ios/reading_list_model_bridge_observer.h
@@ -75,6 +75,8 @@
                                   const GURL& url) override;
 
   __unsafe_unretained id<ReadingListModelBridgeObserver> observer_;
+
+  // TODO(crbug.com/729015): Refactor to remove the naked pointer.
   ReadingListModel* model_;  // weak
 
   DISALLOW_COPY_AND_ASSIGN(ReadingListModelBridge);
diff --git a/components/reading_list/ios/reading_list_model_bridge_observer.mm b/components/reading_list/ios/reading_list_model_bridge_observer.mm
index a54a1a4..ff5836e 100644
--- a/components/reading_list/ios/reading_list_model_bridge_observer.mm
+++ b/components/reading_list/ios/reading_list_model_bridge_observer.mm
@@ -7,6 +7,10 @@
 #include "components/reading_list/core/reading_list_entry.h"
 #include "components/reading_list/core/reading_list_model.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 ReadingListModelBridge::ReadingListModelBridge(
     id<ReadingListModelBridgeObserver> observer,
     ReadingListModel* model)
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc
index 3b37d7b5..c17dbde9 100644
--- a/components/safe_browsing_db/v4_local_database_manager.cc
+++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -66,7 +66,9 @@
                SB_THREAT_TYPE_URL_MALWARE),
       ListInfo(kSyncAlways, "UrlUws.store", GetUrlUwsId(),
                SB_THREAT_TYPE_URL_UNWANTED),
-      ListInfo(kSyncAlways, "UrlMalBin.store", GetUrlMalBinId(),
+      // The GetUrlMalBinId list is not working for non-GoogleChrome builds
+      // currently so making it Chrome-only. See: http://crbug.com/728757
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlMalBin.store", GetUrlMalBinId(),
                SB_THREAT_TYPE_BINARY_MALWARE_URL),
       ListInfo(kSyncAlways, "ChromeExtMalware.store", GetChromeExtMalwareId(),
                SB_THREAT_TYPE_EXTENSION),
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index db4ac7a..20818081 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -105,7 +105,7 @@
 }
 
 void UserManagerBase::Shutdown() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 }
 
 const UserList& UserManagerBase::GetUsers() const {
@@ -128,7 +128,7 @@
 void UserManagerBase::UserLoggedIn(const AccountId& account_id,
                                    const std::string& username_hash,
                                    bool browser_restart) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   if (!last_session_active_account_id_initialized_) {
     last_session_active_account_id_ =
@@ -259,14 +259,14 @@
 }
 
 void UserManagerBase::OnSessionStarted() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   CallUpdateLoginState();
   GetLocalState()->CommitPendingWrite();
 }
 
 void UserManagerBase::OnProfileInitialized(User* user) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   // Mark the user as having an initialized session and persist this in
   // the known_user DB.
@@ -277,7 +277,7 @@
 
 void UserManagerBase::RemoveUser(const AccountId& account_id,
                                  RemoveUserDelegate* delegate) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   if (!CanUserBeRemoved(FindUser(account_id)))
     return;
@@ -302,7 +302,7 @@
 }
 
 void UserManagerBase::RemoveUserFromList(const AccountId& account_id) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   RemoveNonCryptohomeData(account_id);
   if (user_loading_stage_ == STAGE_LOADED) {
     DeleteUser(RemoveRegularOrSupervisedUserFromList(account_id));
@@ -328,38 +328,38 @@
 }
 
 const User* UserManagerBase::FindUser(const AccountId& account_id) const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   if (active_user_ && active_user_->GetAccountId() == account_id)
     return active_user_;
   return FindUserInList(account_id);
 }
 
 User* UserManagerBase::FindUserAndModify(const AccountId& account_id) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   if (active_user_ && active_user_->GetAccountId() == account_id)
     return active_user_;
   return FindUserInListAndModify(account_id);
 }
 
 const User* UserManagerBase::GetActiveUser() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return active_user_;
 }
 
 User* UserManagerBase::GetActiveUser() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return active_user_;
 }
 
 const User* UserManagerBase::GetPrimaryUser() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return primary_user_;
 }
 
 void UserManagerBase::SaveUserOAuthStatus(
     const AccountId& account_id,
     User::OAuthTokenStatus oauth_token_status) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   DVLOG(1) << "Saving user OAuth token status in Local State";
   User* user = FindUserAndModify(account_id);
@@ -382,7 +382,7 @@
 
 void UserManagerBase::SaveForceOnlineSignin(const AccountId& account_id,
                                             bool force_online_signin) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   // Do not update local state if data stored or cached outside the user's
   // cryptohome is to be treated as ephemeral.
@@ -400,7 +400,7 @@
 
 void UserManagerBase::SaveUserDisplayName(const AccountId& account_id,
                                           const base::string16& display_name) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   if (User* user = FindUserAndModify(account_id)) {
     user->set_display_name(display_name);
@@ -424,7 +424,7 @@
 
 void UserManagerBase::SaveUserDisplayEmail(const AccountId& account_id,
                                            const std::string& display_email) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   User* user = FindUserAndModify(account_id);
   if (!user) {
@@ -452,7 +452,7 @@
 
 void UserManagerBase::SaveUserType(const AccountId& account_id,
                                    const UserType& user_type) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   User* user = FindUserAndModify(account_id);
   if (!user) {
@@ -474,7 +474,7 @@
 void UserManagerBase::UpdateUserAccountData(
     const AccountId& account_id,
     const UserAccountData& account_data) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   SaveUserDisplayName(account_id, account_data.display_name());
 
@@ -517,76 +517,76 @@
 }
 
 bool UserManagerBase::IsCurrentUserOwner() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return !owner_account_id_.empty() && active_user_ &&
          active_user_->GetAccountId() == owner_account_id_;
 }
 
 bool UserManagerBase::IsCurrentUserNew() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return is_current_user_new_;
 }
 
 bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() &&
          IsUserNonCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
 }
 
 bool UserManagerBase::IsCurrentUserCryptohomeDataEphemeral() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() &&
          IsUserCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
 }
 
 bool UserManagerBase::CanCurrentUserLock() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->can_lock();
 }
 
 bool UserManagerBase::IsUserLoggedIn() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return active_user_;
 }
 
 bool UserManagerBase::IsLoggedInAsUserWithGaiaAccount() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->HasGaiaAccount();
 }
 
 bool UserManagerBase::IsLoggedInAsChildUser() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_CHILD;
 }
 
 bool UserManagerBase::IsLoggedInAsPublicAccount() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() &&
          active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT;
 }
 
 bool UserManagerBase::IsLoggedInAsGuest() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_GUEST;
 }
 
 bool UserManagerBase::IsLoggedInAsSupervisedUser() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_SUPERVISED;
 }
 
 bool UserManagerBase::IsLoggedInAsKioskApp() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_KIOSK_APP;
 }
 
 bool UserManagerBase::IsLoggedInAsArcKioskApp() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_ARC_KIOSK_APP;
 }
 
 bool UserManagerBase::IsLoggedInAsStub() const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   return IsUserLoggedIn() && IsStubAccountId(active_user_->GetAccountId());
 }
 
@@ -648,41 +648,41 @@
 }
 
 void UserManagerBase::AddObserver(UserManager::Observer* obs) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   observer_list_.AddObserver(obs);
 }
 
 void UserManagerBase::RemoveObserver(UserManager::Observer* obs) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   observer_list_.RemoveObserver(obs);
 }
 
 void UserManagerBase::AddSessionStateObserver(
     UserManager::UserSessionStateObserver* obs) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   session_state_observer_list_.AddObserver(obs);
 }
 
 void UserManagerBase::RemoveSessionStateObserver(
     UserManager::UserSessionStateObserver* obs) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   session_state_observer_list_.RemoveObserver(obs);
 }
 
 void UserManagerBase::NotifyLocalStateChanged() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : observer_list_)
     observer.LocalStateChanged(this);
 }
 
 void UserManagerBase::NotifyUserImageChanged(const User& user) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : observer_list_)
     observer.OnUserImageChanged(user);
 }
 
 void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : observer_list_)
     observer.OnUserProfileImageUpdateFailed(user);
 }
@@ -690,7 +690,7 @@
 void UserManagerBase::NotifyUserProfileImageUpdated(
     const User& user,
     const gfx::ImageSkia& profile_image) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : observer_list_)
     observer.OnUserProfileImageUpdated(user, profile_image);
 }
@@ -752,7 +752,7 @@
 }
 
 void UserManagerBase::EnsureUsersLoaded() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   if (!GetLocalState())
     return;
 
@@ -863,7 +863,7 @@
 }
 
 void UserManagerBase::GuestUserLoggedIn() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   active_user_ = User::CreateGuestUser(GetGuestAccountId());
 }
 
@@ -898,14 +898,14 @@
 
 void UserManagerBase::RegularUserLoggedInAsEphemeral(
     const AccountId& account_id) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   SetIsCurrentUserNew(true);
   is_current_user_ephemeral_regular_user_ = true;
   active_user_ = User::CreateRegularUser(account_id);
 }
 
 void UserManagerBase::NotifyOnLogin() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   NotifyActiveUserHashChanged(active_user_->username_hash());
   NotifyActiveUserChanged(active_user_);
@@ -914,7 +914,7 @@
 
 User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus(
     const AccountId& account_id) const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   const base::DictionaryValue* prefs_oauth_status =
       GetLocalState()->GetDictionary(kUserOAuthTokenStatus);
@@ -932,7 +932,7 @@
 }
 
 bool UserManagerBase::LoadForceOnlineSignin(const AccountId& account_id) const {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
 
   const base::DictionaryValue* prefs_force_online =
       GetLocalState()->GetDictionary(kUserForceOnlineSignin);
@@ -997,26 +997,26 @@
 }
 
 void UserManagerBase::NotifyActiveUserChanged(const User* active_user) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : session_state_observer_list_)
     observer.ActiveUserChanged(active_user);
 }
 
 void UserManagerBase::NotifyUserAddedToSession(const User* added_user,
                                                bool user_switch_pending) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : session_state_observer_list_)
     observer.UserAddedToSession(added_user);
 }
 
 void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   for (auto& observer : session_state_observer_list_)
     observer.ActiveUserHashChanged(hash);
 }
 
 void UserManagerBase::ChangeUserChildStatus(User* user, bool is_child) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!task_runner_ || task_runner_->RunsTasksOnCurrentThread());
   if (user->IsSupervised() == is_child)
     return;
   user->SetIsChild(is_child);
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index 826fe0d8..e669699 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <utility>
+#include <vector>
 
 #include "base/build_time.h"
 #include "base/command_line.h"
@@ -336,9 +337,7 @@
       GetChannelForVariations(client_->GetChannel());
   UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.UserChannel", channel);
 
-  const std::string latest_country =
-      local_state_->GetString(prefs::kVariationsCountry);
-
+  const std::string latest_country = GetLatestCountry();
   std::unique_ptr<const base::FieldTrial::EntropyProvider> low_entropy_provider(
       CreateLowEntropyProvider());
   // Note that passing |&ui_string_overrider_| via base::Unretained below is
@@ -780,8 +779,7 @@
   variations::VariationsSeedSimulator seed_simulator(*default_provider,
                                                      *low_provider);
 
-  const std::string latest_country =
-      local_state_->GetString(prefs::kVariationsCountry);
+  const std::string latest_country = GetLatestCountry();
   const variations::VariationsSeedSimulator::Result result =
       seed_simulator.SimulateSeedStudies(
           *seed, client_->GetApplicationLocale(),
@@ -823,6 +821,12 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(version.IsValid());
 
+  const std::string override_country =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kVariationsOverrideCountry);
+  if (!override_country.empty())
+    return override_country;
+
   const base::ListValue* list_value =
       local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
   std::string stored_version_string;
@@ -930,7 +934,12 @@
 }
 
 std::string VariationsService::GetLatestCountry() const {
-  return local_state_->GetString(prefs::kVariationsCountry);
+  const std::string override_country =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kVariationsOverrideCountry);
+  return !override_country.empty()
+             ? override_country
+             : local_state_->GetString(prefs::kVariationsCountry);
 }
 
 }  // namespace variations
diff --git a/components/variations/variations_switches.cc b/components/variations/variations_switches.cc
index 0bc8e82..5b19c66 100644
--- a/components/variations/variations_switches.cc
+++ b/components/variations/variations_switches.cc
@@ -26,6 +26,13 @@
 // escaped for all non-alphanumeric characters.
 const char kForceFieldTrialParams[] = "force-fieldtrial-params";
 
+// Allows overriding the country used for evaluating variations. This is similar
+// to the "Override Variations Country" entry on chrome://translate-internals,
+// but is exposed as a command-line flag to allow testing First Run scenarios.
+// Additionally, unlike chrome://translate-internals, the value isn't persisted
+// across sessions.
+const char kVariationsOverrideCountry[] = "variations-override-country";
+
 // Specifies a custom URL for the server which reports variation data to the
 // client. Specifying this switch enables the Variations service on
 // unofficial builds. See variations_service.cc.
diff --git a/components/variations/variations_switches.h b/components/variations/variations_switches.h
index adebf9b1..9755dac4 100644
--- a/components/variations/variations_switches.h
+++ b/components/variations/variations_switches.h
@@ -14,6 +14,7 @@
 extern const char kDisableFieldTrialTestingConfig[];
 extern const char kFakeVariationsChannel[];
 extern const char kForceFieldTrialParams[];
+extern const char kVariationsOverrideCountry[];
 extern const char kVariationsServerURL[];
 
 }  // namespace switches
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index d8dfac8..59efca1 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -232,6 +232,11 @@
   // to be a main frame.  This should be cleaned up eventually.
   bool is_guest = BrowserPluginGuest::IsGuest(RenderViewHostImpl::From(host_));
   if (frame_connector_ && !is_guest) {
+    // An auto-resize set by the top-level frame overrides what would be
+    // reported by embedding RenderWidgetHostViews.
+    if (host_->delegate() && !host_->delegate()->GetAutoResizeSize().IsEmpty())
+      return host_->delegate()->GetAutoResizeSize();
+
     RenderWidgetHostView* parent_view =
         frame_connector_->GetParentRenderWidgetHostView();
     // The parent_view can be null in unit tests when using a TestWebContents.
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
index b27892ce..d03ebb4b 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/view_messages.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
@@ -15,6 +17,7 @@
 #include "content/test/test_content_browser_client.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace content {
 
@@ -73,4 +76,42 @@
                  base::Unretained(this)));
 }
 
+// Test that auto-resize sizes in the top frame are propagated to OOPIF
+// RenderWidgetHostViews. See https://crbug.com/726743.
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest,
+                       ChildFrameAutoResizeUpdate) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL(
+                   "a.com", "/cross_site_iframe_factory.html?a(b)")));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  root->current_frame_host()->render_view_host()->EnableAutoResize(
+      gfx::Size(0, 0), gfx::Size(100, 100));
+
+  RenderWidgetHostView* rwhv =
+      root->child_at(0)->current_frame_host()->GetRenderWidgetHost()->GetView();
+
+  // Fake an auto-resize update from the parent renderer.
+  int routing_id =
+      root->current_frame_host()->GetRenderWidgetHost()->GetRoutingID();
+  ViewHostMsg_UpdateRect_Params params;
+  params.view_size = gfx::Size(75, 75);
+  params.flags = 0;
+  root->current_frame_host()->GetRenderWidgetHost()->OnMessageReceived(
+      ViewHostMsg_UpdateRect(routing_id, params));
+
+  // RenderWidgetHostImpl has delayed auto-resize processing. Yield here to
+  // let it complete.
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                run_loop.QuitClosure());
+  run_loop.Run();
+
+  // The child frame's RenderWidgetHostView should now use the auto-resize value
+  // for its visible viewport.
+  EXPECT_EQ(gfx::Size(75, 75), rwhv->GetVisibleViewportSize());
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index 4b72469d..7d08361 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -871,7 +871,10 @@
   if (!transaction)
     return;
 
-  connection_->AbortTransaction(transaction);
+  connection_->AbortTransaction(
+      transaction,
+      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+                             "Transaction aborted by user."));
 }
 
 void DatabaseImpl::IDBThreadHelper::AbortWithError(
diff --git a/content/browser/indexed_db/indexed_db_connection.cc b/content/browser/indexed_db/indexed_db_connection.cc
index a541a70f..c7744d4 100644
--- a/content/browser/indexed_db/indexed_db_connection.cc
+++ b/content/browser/indexed_db/indexed_db_connection.cc
@@ -116,11 +116,6 @@
   return transaction_ptr;
 }
 
-void IndexedDBConnection::AbortTransaction(IndexedDBTransaction* transaction) {
-  IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction->id());
-  transaction->Abort();
-}
-
 void IndexedDBConnection::AbortTransaction(
     IndexedDBTransaction* transaction,
     const IndexedDBDatabaseError& error) {
diff --git a/content/browser/indexed_db/indexed_db_connection.h b/content/browser/indexed_db/indexed_db_connection.h
index e69119557..ea3b8542 100644
--- a/content/browser/indexed_db/indexed_db_connection.h
+++ b/content/browser/indexed_db/indexed_db_connection.h
@@ -62,7 +62,6 @@
       blink::WebIDBTransactionMode mode,
       IndexedDBBackingStore::Transaction* backing_store_transaction);
 
-  void AbortTransaction(IndexedDBTransaction* transaction);
   void AbortTransaction(IndexedDBTransaction* transaction,
                         const IndexedDBDatabaseError& error);
 
diff --git a/content/browser/indexed_db/indexed_db_database_error.h b/content/browser/indexed_db/indexed_db_database_error.h
index 6106f75..0cb060c 100644
--- a/content/browser/indexed_db/indexed_db_database_error.h
+++ b/content/browser/indexed_db/indexed_db_database_error.h
@@ -9,10 +9,11 @@
 
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "content/common/content_export.h"
 
 namespace content {
 
-class IndexedDBDatabaseError {
+class CONTENT_EXPORT IndexedDBDatabaseError {
  public:
   IndexedDBDatabaseError();
   explicit IndexedDBDatabaseError(uint16_t code);
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index e1a3a5b..bc7bfccd 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -177,11 +177,6 @@
                             ptr_factory_.GetWeakPtr()));
 }
 
-void IndexedDBTransaction::Abort() {
-  Abort(IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionUnknownError,
-                               "Internal error (unknown cause)"));
-}
-
 void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
   IDB_TRACE1("IndexedDBTransaction::Abort", "txn.id", id());
   DCHECK(!processing_event_queue_);
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
index ba1e6ce..fdd2cdb 100644
--- a/content/browser/indexed_db/indexed_db_transaction.h
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -49,8 +49,7 @@
 
   leveldb::Status Commit();
 
-  // This object is destroyed by these method calls.
-  virtual void Abort();
+  // This object is destroyed by this method.
   void Abort(const IndexedDBDatabaseError& error);
 
   // Called by the transaction coordinator when this transaction is unblocked.
diff --git a/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index 06fe219..ee58580d 100644
--- a/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -14,12 +14,14 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_observer.h"
 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
 
 namespace content {
 const int kFakeProcessId = 10;
@@ -161,7 +163,9 @@
   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
 
   // Clean up to avoid leaks.
-  transaction->Abort();
+  transaction->Abort(IndexedDBDatabaseError(
+      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+                             "Transaction aborted by user.")));
   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
 }
@@ -411,7 +415,9 @@
 
   RunPostedTasks();
 
-  transaction->Abort();
+  transaction->Abort(
+      IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError,
+                             "Transaction aborted by user."));
   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
   EXPECT_EQ(0, transaction->pending_preemptive_events_);
diff --git a/content/browser/loader/resource_handler.cc b/content/browser/loader/resource_handler.cc
index 961934d..0f87551 100644
--- a/content/browser/loader/resource_handler.cc
+++ b/content/browser/loader/resource_handler.cc
@@ -16,7 +16,9 @@
   delegate_ = delegate;
 }
 
-ResourceHandler::~ResourceHandler() {}
+ResourceHandler::~ResourceHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 ResourceHandler::ResourceHandler(net::URLRequest* request)
     : request_(request) {}
diff --git a/content/browser/loader/resource_handler.h b/content/browser/loader/resource_handler.h
index c292cab2..2b67e0c4 100644
--- a/content/browser/loader/resource_handler.h
+++ b/content/browser/loader/resource_handler.h
@@ -18,7 +18,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
 #include "content/browser/loader/resource_controller.h"
 #include "content/common/content_export.h"
 
@@ -43,8 +43,7 @@
 // No ResourceHandler method other than OnWillRead will ever be called
 // synchronously when it calls into the ResourceController passed in to it,
 // either to resume or cancel the request.
-class CONTENT_EXPORT ResourceHandler
-    : public NON_EXPORTED_BASE(base::NonThreadSafe) {
+class CONTENT_EXPORT ResourceHandler {
  public:
   virtual ~ResourceHandler();
 
@@ -176,6 +175,8 @@
   Delegate* delegate_;
   std::unique_ptr<ResourceController> controller_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(ResourceHandler);
 };
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index df1962ab..ee956813 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -933,6 +933,10 @@
   Send(new ViewMsg_DisableAutoResize(GetRoutingID(), new_size));
   if (!new_size.IsEmpty())
     GetWidget()->GetView()->SetSize(new_size);
+  // This clears the cached value in the WebContents, so that OOPIFs will
+  // stop using it.
+  if (GetWidget()->delegate())
+    GetWidget()->delegate()->ResetAutoResizeSize();
 }
 
 void RenderViewHostImpl::ExecuteMediaPlayerActionAtLocation(
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index cb32ee9..2cad1fb9 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -111,6 +111,10 @@
     ukm::UkmRecorder* service,
     ukm::SourceId ukm_source_id) {}
 
+gfx::Size RenderWidgetHostDelegate::GetAutoResizeSize() {
+  return gfx::Size();
+}
+
 WebContents* RenderWidgetHostDelegate::GetAsWebContents() {
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index ca56a9d..29b3115 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -266,6 +266,12 @@
   // Notifies that a CompositorFrame was received from the renderer.
   virtual void DidReceiveCompositorFrame() {}
 
+  // Gets the size set by a top-level frame with auto-resize enabled.
+  virtual gfx::Size GetAutoResizeSize();
+
+  // Reset the auto-size value, to indicate that auto-size is no longer active.
+  virtual void ResetAutoResizeSize() {}
+
  protected:
   virtual ~RenderWidgetHostDelegate() {}
 };
diff --git a/content/browser/speech/speech_recognition_engine.cc b/content/browser/speech/speech_recognition_engine.cc
index b6bd1d5..bd99e7a 100644
--- a/content/browser/speech/speech_recognition_engine.cc
+++ b/content/browser/speech/speech_recognition_engine.cc
@@ -102,7 +102,9 @@
       use_framed_post_data_(false),
       state_(STATE_IDLE) {}
 
-SpeechRecognitionEngine::~SpeechRecognitionEngine() {}
+SpeechRecognitionEngine::~SpeechRecognitionEngine() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 void SpeechRecognitionEngine::SetConfig(const Config& config) {
   config_ = config;
@@ -145,7 +147,7 @@
 
 void SpeechRecognitionEngine::DispatchHTTPResponse(const URLFetcher* source,
                                                    bool end_of_response) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(source);
   const bool response_is_good = source->GetStatus().is_success() &&
                                 source->GetResponseCode() == 200;
@@ -214,7 +216,7 @@
 }
 
 bool SpeechRecognitionEngine::IsRecognitionPending() const {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return state_ != STATE_IDLE;
 }
 
@@ -226,7 +228,7 @@
 
 void SpeechRecognitionEngine::DispatchEvent(
     const FSMEventArgs& event_args) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_LE(event_args.event, EVENT_MAX_VALUE);
   DCHECK_LE(state_, STATE_MAX_VALUE);
 
diff --git a/content/browser/speech/speech_recognition_engine.h b/content/browser/speech/speech_recognition_engine.h
index e7a2535..9792d3aaa 100644
--- a/content/browser/speech/speech_recognition_engine.h
+++ b/content/browser/speech/speech_recognition_engine.h
@@ -12,7 +12,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
 #include "content/browser/speech/audio_encoder.h"
 #include "content/browser/speech/chunked_byte_buffer.h"
 #include "content/common/content_export.h"
@@ -57,9 +57,7 @@
 // EndRecognition. If a recognition was started, the caller can free the
 // SpeechRecognitionEngine only after calling EndRecognition.
 
-class CONTENT_EXPORT SpeechRecognitionEngine
-    : public net::URLFetcherDelegate,
-      public NON_EXPORTED_BASE(base::NonThreadSafe) {
+class CONTENT_EXPORT SpeechRecognitionEngine : public net::URLFetcherDelegate {
  public:
   class Delegate {
    public:
@@ -216,6 +214,8 @@
   bool use_framed_post_data_;
   FSMState state_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionEngine);
 };
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 626c4c8..cef65a9 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2787,10 +2787,32 @@
   if (render_widget_host != GetRenderViewHost()->GetWidget())
     return;
 
+  auto_resize_size_ = new_size;
+
+  // Out-of-process iframe visible viewport sizes usually come from the
+  // top-level RenderWidgetHostView, but when auto-resize is enabled on the
+  // top frame then that size is used instead.
+  for (FrameTreeNode* node : frame_tree_.Nodes()) {
+    if (node->current_frame_host()->is_local_root()) {
+      RenderWidgetHostImpl* host =
+          node->current_frame_host()->GetRenderWidgetHost();
+      if (host != render_widget_host)
+        host->WasResized();
+    }
+  }
+
   if (delegate_)
     delegate_->ResizeDueToAutoResize(this, new_size);
 }
 
+gfx::Size WebContentsImpl::GetAutoResizeSize() {
+  return auto_resize_size_;
+}
+
+void WebContentsImpl::ResetAutoResizeSize() {
+  auto_resize_size_ = gfx::Size();
+}
+
 WebContents* WebContentsImpl::OpenURL(const OpenURLParams& params) {
   if (!delegate_)
     return NULL;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index e71d078c..3b58932 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -668,6 +668,8 @@
                               bool width_changed) override;
   void ResizeDueToAutoResize(RenderWidgetHostImpl* render_widget_host,
                              const gfx::Size& new_size) override;
+  gfx::Size GetAutoResizeSize() override;
+  void ResetAutoResizeSize() override;
   void ScreenInfoChanged() override;
   void UpdateDeviceScaleFactor(double device_scale_factor) override;
   void GetScreenInfo(ScreenInfo* screen_info) override;
@@ -1446,6 +1448,10 @@
   // this overrides |preferred_size_|.
   gfx::Size preferred_size_for_capture_;
 
+  // Size set by a top-level frame with auto-resize enabled. This is needed by
+  // out-of-process iframes for their visible viewport size.
+  gfx::Size auto_resize_size_;
+
 #if defined(OS_ANDROID)
   // Date time chooser opened by this tab.
   // Only used in Android since all other platforms use a multi field UI.
diff --git a/content/child/fileapi/webfilesystem_impl.cc b/content/child/fileapi/webfilesystem_impl.cc
index 8c55be9..f34ea12 100644
--- a/content/child/fileapi/webfilesystem_impl.cc
+++ b/content/child/fileapi/webfilesystem_impl.cc
@@ -97,7 +97,7 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& main_thread_task_runner,
     Method method, const Params& params,
     WaitableCallbackResults* waitable_results) {
-  if (!main_thread_task_runner->RunsTasksInCurrentSequence()) {
+  if (!main_thread_task_runner->BelongsToCurrentThread()) {
     main_thread_task_runner->PostTask(
         FROM_HERE,
         base::Bind(&CallDispatcherOnMainThread<Method, Params>,
@@ -397,7 +397,7 @@
 }
 
 WebFileSystemImpl::~WebFileSystemImpl() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   g_webfilesystem_tls.Pointer()->Set(NULL);
 }
 
@@ -655,21 +655,21 @@
 
 int WebFileSystemImpl::RegisterCallbacks(
     const WebFileSystemCallbacks& callbacks) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   int id = next_callbacks_id_++;
   callbacks_[id] = callbacks;
   return id;
 }
 
 WebFileSystemCallbacks WebFileSystemImpl::GetCallbacks(int callbacks_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   CallbacksMap::iterator found = callbacks_.find(callbacks_id);
   DCHECK(found != callbacks_.end());
   return found->second;
 }
 
 void WebFileSystemImpl::UnregisterCallbacks(int callbacks_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   CallbacksMap::iterator found = callbacks_.find(callbacks_id);
   DCHECK(found != callbacks_.end());
   callbacks_.erase(found);
diff --git a/content/child/fileapi/webfilesystem_impl.h b/content/child/fileapi/webfilesystem_impl.h
index 4efe2f9..133becd2 100644
--- a/content/child/fileapi/webfilesystem_impl.h
+++ b/content/child/fileapi/webfilesystem_impl.h
@@ -10,7 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
+#include "base/threading/thread_checker.h"
 #include "content/public/child/worker_thread.h"
 #include "third_party/WebKit/public/platform/WebFileSystem.h"
 
@@ -106,7 +106,8 @@
   int next_callbacks_id_;
   WaitableCallbackResultsMap waitable_results_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  // Thread-affine per use of TLS in impl.
+  THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(WebFileSystemImpl);
 };
diff --git a/content/renderer/gpu/compositor_external_begin_frame_source.cc b/content/renderer/gpu/compositor_external_begin_frame_source.cc
index 421415e4..5e14b87f 100644
--- a/content/renderer/gpu/compositor_external_begin_frame_source.cc
+++ b/content/renderer/gpu/compositor_external_begin_frame_source.cc
@@ -20,11 +20,11 @@
       routing_id_(routing_id) {
   DCHECK(begin_frame_source_filter_);
   DCHECK(message_sender_);
-  DetachFromThread();
+  DETACH_FROM_THREAD(thread_checker_);
 }
 
 CompositorExternalBeginFrameSource::~CompositorExternalBeginFrameSource() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (begin_frame_source_proxy_) {
     begin_frame_source_proxy_->ClearBeginFrameSource();
     begin_frame_source_filter_->RemoveHandlerOnCompositorThread(
@@ -35,7 +35,7 @@
 
 void CompositorExternalBeginFrameSource::AddObserver(
     cc::BeginFrameObserver* obs) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (!begin_frame_source_proxy_) {
     begin_frame_source_proxy_ =
@@ -71,7 +71,7 @@
 
 void CompositorExternalBeginFrameSource::OnMessageReceived(
     const IPC::Message& message) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(begin_frame_source_proxy_);
   IPC_BEGIN_MESSAGE_MAP(CompositorExternalBeginFrameSource, message)
     IPC_MESSAGE_HANDLER(ViewMsg_SetBeginFramePaused,
diff --git a/content/renderer/gpu/compositor_external_begin_frame_source.h b/content/renderer/gpu/compositor_external_begin_frame_source.h
index 96520a02..7765a675 100644
--- a/content/renderer/gpu/compositor_external_begin_frame_source.h
+++ b/content/renderer/gpu/compositor_external_begin_frame_source.h
@@ -10,7 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread_checker.h"
 #include "cc/scheduler/begin_frame_source.h"
 #include "content/renderer/gpu/compositor_forwarding_message_filter.h"
 
@@ -31,8 +31,7 @@
 // directly rather than proxied by this class.
 class CompositorExternalBeginFrameSource
     : public cc::BeginFrameSource,
-      public cc::ExternalBeginFrameSourceClient,
-      public NON_EXPORTED_BASE(base::NonThreadSafe) {
+      public cc::ExternalBeginFrameSourceClient {
  public:
   explicit CompositorExternalBeginFrameSource(
       CompositorForwardingMessageFilter* filter,
@@ -88,6 +87,8 @@
   int routing_id_;
   CompositorForwardingMessageFilter::Handler begin_frame_source_filter_handler_;
 
+  THREAD_CHECKER(thread_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(CompositorExternalBeginFrameSource);
 };
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index a7f6ff2..868a9c16 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -2194,7 +2194,8 @@
 TEST_F(RenderViewImplTest, PreferredSizeZoomed) {
   LoadHTML("<body style='margin:0;'><div style='display:inline-block; "
            "width:400px; height:400px;'/></body>");
-  view()->webview()->MainFrame()->SetCanHaveScrollbars(false);
+  view()->webview()->MainFrame()->ToWebLocalFrame()->SetCanHaveScrollbars(
+      false);
   EnablePreferredSizeMode();
 
   gfx::Size size = GetPreferredSize();
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 39f9432e..35718fb 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2189,8 +2189,9 @@
     webview()->HidePopups();
     if (send_preferred_size_changes_ &&
         webview()->MainFrame()->IsWebLocalFrame()) {
-      webview()->MainFrame()->SetCanHaveScrollbars(ShouldDisplayScrollbars(
-          params.new_size.width(), params.new_size.height()));
+      webview()->MainFrame()->ToWebLocalFrame()->SetCanHaveScrollbars(
+          ShouldDisplayScrollbars(params.new_size.width(),
+                                  params.new_size.height()));
     }
     if (display_mode_ != params.display_mode) {
       display_mode_ = params.display_mode;
diff --git a/extensions/browser/api/app_window/app_window_apitest.cc b/extensions/browser/api/app_window/app_window_apitest.cc
index 2ad9886..f3685fb 100644
--- a/extensions/browser/api/app_window/app_window_apitest.cc
+++ b/extensions/browser/api/app_window/app_window_apitest.cc
@@ -30,61 +30,28 @@
 
 namespace extensions {
 
-namespace {
-
-class TestAppWindowRegistryObserver : public AppWindowRegistry::Observer {
- public:
-  explicit TestAppWindowRegistryObserver(Profile* profile)
-      : profile_(profile), icon_updates_(0) {
-    AppWindowRegistry::Get(profile_)->AddObserver(this);
-  }
-  ~TestAppWindowRegistryObserver() override {
-    AppWindowRegistry::Get(profile_)->RemoveObserver(this);
-  }
-
-  // Overridden from AppWindowRegistry::Observer:
-  void OnAppWindowIconChanged(AppWindow* app_window) override {
-    ++icon_updates_;
-  }
-
-  int icon_updates() { return icon_updates_; }
-
- private:
-  Profile* profile_;
-  int icon_updates_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAppWindowRegistryObserver);
-};
-
-}  // namespace
-
 using AppWindowApiTest = PlatformAppBrowserTest;
 using ExperimentalAppWindowApiTest = ExperimentalPlatformAppBrowserTest;
 
 // Tests chrome.app.window.setIcon.
-// Flaky test crbug.com/716726
-IN_PROC_BROWSER_TEST_F(ExperimentalAppWindowApiTest, DISABLED_SetIcon) {
-  std::unique_ptr<TestAppWindowRegistryObserver> test_observer(
-      new TestAppWindowRegistryObserver(browser()->profile()));
+IN_PROC_BROWSER_TEST_F(ExperimentalAppWindowApiTest, SetIcon) {
   ExtensionTestMessageListener listener("ready", true);
 
   // Launch the app and wait for it to be ready.
   LoadAndLaunchPlatformApp("windows_api_set_icon", &listener);
-  EXPECT_EQ(0, test_observer->icon_updates());
   listener.Reply("");
 
+  AppWindow* app_window = GetFirstAppWindow();
+  ASSERT_TRUE(app_window);
+
   // Now wait until the WebContent has decoded the icon and chrome has
   // processed it. This needs to be in a loop since the renderer runs in a
   // different process.
-  while (test_observer->icon_updates() < 1) {
-    base::RunLoop run_loop;
-    run_loop.RunUntilIdle();
-  }
-  AppWindow* app_window = GetFirstAppWindow();
-  ASSERT_TRUE(app_window);
+  while (app_window->custom_app_icon().IsEmpty())
+    base::RunLoop().RunUntilIdle();
+
   EXPECT_NE(std::string::npos,
             app_window->app_icon_url().spec().find("icon.png"));
-  EXPECT_EQ(1, test_observer->icon_updates());
 }
 
 // TODO(asargent) - Figure out what to do about the fact that minimize events
diff --git a/extensions/browser/app_window/app_delegate.h b/extensions/browser/app_window/app_delegate.h
index 356f1ec7..1cdd50f2 100644
--- a/extensions/browser/app_window/app_delegate.h
+++ b/extensions/browser/app_window/app_delegate.h
@@ -70,7 +70,7 @@
                                           const GURL& security_origin,
                                           content::MediaStreamType type,
                                           const Extension* extension) = 0;
-  virtual int PreferredIconSize() = 0;
+  virtual int PreferredIconSize() const = 0;
 
   // Web contents modal dialog support.
   virtual void SetWebContentsBlocked(content::WebContents* web_contents,
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index 86fe183..a0e4b1d 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -49,13 +49,11 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "extensions/common/permissions/permissions_data.h"
-#include "extensions/grit/extensions_browser_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkRegion.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/gfx/image/image_skia_operations.h"
 
 #if !defined(OS_MACOSX)
 #include "components/prefs/pref_service.h"
@@ -293,7 +291,6 @@
   requested_alpha_enabled_ = new_params.alpha_enabled;
   is_ime_window_ = params.is_ime_window;
   show_in_shelf_ = params.show_in_shelf;
-  window_icon_url_ = params.window_icon_url;
 
   AppWindowClient* app_window_client = AppWindowClient::Get();
   native_app_window_.reset(
@@ -302,10 +299,10 @@
   helper_.reset(new AppWebContentsHelper(
       browser_context_, extension_id_, web_contents(), app_delegate_.get()));
 
-  UpdateExtensionAppIcon();
-  // Download showInShelf=true window icon.
-  if (window_icon_url_.is_valid())
-    SetAppIconUrl(window_icon_url_);
+  native_app_window_->UpdateWindowIcon();
+
+  if (params.window_icon_url.is_valid())
+    SetAppIconUrl(params.window_icon_url);
 
   AppWindowRegistry::Get(browser_context_)->AddAppWindow(this);
 
@@ -561,22 +558,10 @@
   return title;
 }
 
-bool AppWindow::HasCustomIcon() const {
-  return window_icon_url_.is_valid() || app_icon_url_.is_valid();
-}
-
 void AppWindow::SetAppIconUrl(const GURL& url) {
   // Avoid using any previous icons that were being downloaded.
   image_loader_ptr_factory_.InvalidateWeakPtrs();
-
-  // Reset |app_icon_image_| to abort pending image load (if any).
-  if (!show_in_shelf_) {
-    app_icon_image_.reset();
-    app_icon_url_ = url;
-  } else {
-    window_icon_url_ = url;
-  }
-
+  app_icon_url_ = url;
   web_contents()->DownloadImage(
       url,
       true,   // is a favicon
@@ -596,38 +581,10 @@
 }
 
 void AppWindow::UpdateAppIcon(const gfx::Image& image) {
-  // Set the showInShelf=true window icon and add the app_icon_image_
-  // as a badge. If the image is empty, set the default app icon placeholder
-  // as the base image.
-  if (window_icon_url_.is_valid() && app_icon_image_ &&
-      !app_icon_image_->image().IsEmpty()) {
-    gfx::Image base_image =
-        !image.IsEmpty()
-            ? image
-            : gfx::Image(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-                  IDR_APP_DEFAULT_ICON));
-    // Scale the icon to EXTENSION_ICON_LARGE.
-    const int large_icon_size = extension_misc::EXTENSION_ICON_LARGE;
-    if (base_image.Width() != large_icon_size ||
-        base_image.Height() != large_icon_size) {
-      gfx::ImageSkia resized_image =
-          gfx::ImageSkiaOperations::CreateResizedImage(
-              base_image.AsImageSkia(), skia::ImageOperations::RESIZE_BEST,
-              gfx::Size(large_icon_size, large_icon_size));
-      app_icon_ = gfx::Image(gfx::ImageSkiaOperations::CreateIconWithBadge(
-          resized_image, app_icon_image_->image_skia()));
-    } else {
-      app_icon_ = gfx::Image(gfx::ImageSkiaOperations::CreateIconWithBadge(
-          base_image.AsImageSkia(), app_icon_image_->image_skia()));
-    }
-  } else {
-    if (image.IsEmpty())
-      return;
-
-    app_icon_ = image;
-  }
+  if (image.IsEmpty())
+    return;
+  custom_app_icon_ = image;
   native_app_window_->UpdateWindowIcon();
-  AppWindowRegistry::Get(browser_context_)->AppWindowIconChanged(this);
 }
 
 void AppWindow::SetFullscreen(FullscreenType type, bool enable) {
@@ -812,10 +769,8 @@
     const GURL& image_url,
     const std::vector<SkBitmap>& bitmaps,
     const std::vector<gfx::Size>& original_bitmap_sizes) {
-  if (((image_url != app_icon_url_) && (image_url != window_icon_url_)) ||
-      bitmaps.empty()) {
+  if (image_url != app_icon_url_ || bitmaps.empty())
     return;
-  }
 
   // Bitmaps are ordered largest to smallest. Choose the smallest bitmap
   // whose height >= the preferred size.
@@ -829,38 +784,6 @@
   UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest));
 }
 
-void AppWindow::OnExtensionIconImageChanged(IconImage* image) {
-  DCHECK_EQ(app_icon_image_.get(), image);
-
-  // Update app_icon if no valid window icon url is set.
-  if (!window_icon_url_.is_valid())
-    UpdateAppIcon(gfx::Image(app_icon_image_->image_skia()));
-}
-
-void AppWindow::UpdateExtensionAppIcon() {
-  // Avoid using any previous app icons were being downloaded.
-  image_loader_ptr_factory_.InvalidateWeakPtrs();
-
-  const Extension* extension = GetExtension();
-  if (!extension)
-    return;
-
-  gfx::ImageSkia app_default_icon =
-      *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-          IDR_APP_DEFAULT_ICON);
-
-  app_icon_image_.reset(new IconImage(browser_context(),
-                                      extension,
-                                      IconsInfo::GetIcons(extension),
-                                      app_delegate_->PreferredIconSize(),
-                                      app_default_icon,
-                                      this));
-
-  // Triggers actual image loading with 1x resources. The 2x resource will
-  // be handled by IconImage class when requested.
-  app_icon_image_->image_skia().GetRepresentation(1.0f);
-}
-
 void AppWindow::SetNativeWindowFullscreen() {
   native_app_window_->SetFullscreen(fullscreen_types_);
 
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index a850d713..ab812c2 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -19,7 +19,6 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "extensions/browser/extension_function_dispatcher.h"
-#include "extensions/browser/extension_icon_image.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "ui/base/ui_base_types.h"  // WindowShowState
 #include "ui/gfx/geometry/rect.h"
@@ -87,7 +86,6 @@
 class AppWindow : public content::WebContentsDelegate,
                   public content::WebContentsObserver,
                   public web_modal::WebContentsModalDialogManagerDelegate,
-                  public IconImage::Observer,
                   public ExtensionFunctionDispatcher::Delegate,
                   public ExtensionRegistryObserver {
  public:
@@ -236,7 +234,7 @@
     return window_type_ == WINDOW_TYPE_PANEL;
   }
   content::BrowserContext* browser_context() const { return browser_context_; }
-  const gfx::Image& app_icon() const { return app_icon_; }
+  const gfx::Image& custom_app_icon() const { return custom_app_icon_; }
   const GURL& app_icon_url() const { return app_icon_url_; }
   const GURL& initial_url() const { return initial_url_; }
   bool is_hidden() const { return is_hidden_; }
@@ -357,10 +355,6 @@
   // unblock resource requests.
   void NotifyRenderViewReady();
 
-  // Returns true if window has custom icon in case either |window_icon_url_| or
-  // |app_icon_url_| is set. Custom icon may be not loaded yet.
-  bool HasCustomIcon() const;
-
   // Whether the app window wants to be alpha enabled.
   bool requested_alpha_enabled() const { return requested_alpha_enabled_; }
 
@@ -372,7 +366,7 @@
 
   bool show_in_shelf() const { return show_in_shelf_; }
 
-  const GURL& window_icon_url() const { return window_icon_url_; }
+  AppDelegate* app_delegate() { return app_delegate_.get(); }
 
   void SetAppWindowContentsForTesting(
       std::unique_ptr<AppWindowContents> contents) {
@@ -473,9 +467,6 @@
   // CreateParams that should be used to create the window.
   CreateParams LoadDefaults(CreateParams params) const;
 
-  // Load the app's image, firing a load state change when loaded.
-  void UpdateExtensionAppIcon();
-
   // Set the fullscreen state in the native app window.
   void SetNativeWindowFullscreen();
 
@@ -501,9 +492,6 @@
                           const std::vector<SkBitmap>& bitmaps,
                           const std::vector<gfx::Size>& original_bitmap_sizes);
 
-  // IconImage::Observer implementation.
-  void OnExtensionIconImageChanged(IconImage* image) override;
-
   // The browser context with which this window is associated. AppWindow does
   // not own this object.
   content::BrowserContext* browser_context_;
@@ -517,16 +505,13 @@
   const SessionID session_id_;
   WindowType window_type_;
 
-  // Icon shown in the task bar.
-  gfx::Image app_icon_;
+  // Custom icon shown in the task bar or in Chrome OS shelf.
+  gfx::Image custom_app_icon_;
 
   // Icon URL to be used for setting the app icon. If not empty, app_icon_ will
   // be fetched and set using this URL.
   GURL app_icon_url_;
 
-  // An object to load the app's icon as an extension resource.
-  std::unique_ptr<IconImage> app_icon_image_;
-
   std::unique_ptr<NativeAppWindow> native_app_window_;
   std::unique_ptr<AppWindowContents> app_window_contents_;
   std::unique_ptr<AppDelegate> app_delegate_;
@@ -565,11 +550,6 @@
   // Whether |show_in_shelf| was set in the CreateParams.
   bool show_in_shelf_;
 
-  // Icon URL to be used for setting the window icon. If not empty,
-  // app_icon_ will be fetched and set using this URL and will have
-  // app_icon_image_ as a badge.
-  GURL window_icon_url_;
-
   // PlzNavigate: this is called when the first navigation is ready to commit.
   base::Closure on_first_commit_callback_;
 
diff --git a/extensions/browser/app_window/app_window_registry.cc b/extensions/browser/app_window/app_window_registry.cc
index 3eb52ca..bd259b0b 100644
--- a/extensions/browser/app_window/app_window_registry.cc
+++ b/extensions/browser/app_window/app_window_registry.cc
@@ -24,10 +24,6 @@
 void AppWindowRegistry::Observer::OnAppWindowAdded(AppWindow* app_window) {
 }
 
-void AppWindowRegistry::Observer::OnAppWindowIconChanged(
-    AppWindow* app_window) {
-}
-
 void AppWindowRegistry::Observer::OnAppWindowRemoved(AppWindow* app_window) {
 }
 
@@ -64,12 +60,6 @@
     observer.OnAppWindowAdded(app_window);
 }
 
-void AppWindowRegistry::AppWindowIconChanged(AppWindow* app_window) {
-  AddAppWindowToList(app_window);
-  for (auto& observer : observers_)
-    observer.OnAppWindowIconChanged(app_window);
-}
-
 void AppWindowRegistry::AppWindowActivated(AppWindow* app_window) {
   BringToFront(app_window);
   for (auto& observer : observers_)
diff --git a/extensions/browser/app_window/app_window_registry.h b/extensions/browser/app_window/app_window_registry.h
index 799c4f6..e63b48b 100644
--- a/extensions/browser/app_window/app_window_registry.h
+++ b/extensions/browser/app_window/app_window_registry.h
@@ -37,8 +37,6 @@
    public:
     // Called just after a app window was added.
     virtual void OnAppWindowAdded(AppWindow* app_window);
-    // Called when the window icon changes.
-    virtual void OnAppWindowIconChanged(AppWindow* app_window);
     // Called just after a app window was removed.
     virtual void OnAppWindowRemoved(AppWindow* app_window);
     // Called just after a app window was hidden. This is different from
@@ -67,7 +65,6 @@
   static AppWindowRegistry* Get(content::BrowserContext* context);
 
   void AddAppWindow(AppWindow* app_window);
-  void AppWindowIconChanged(AppWindow* app_window);
   // Called by |app_window| when it is activated.
   void AppWindowActivated(AppWindow* app_window);
   void AppWindowHidden(AppWindow* app_window);
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 287f3ac..9eb80d4 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1238,6 +1238,7 @@
   MEDIAPERCEPTIONPRIVATE_SETSTATE,
   MEDIAPERCEPTIONPRIVATE_GETDIAGNOSTICS,
   NETWORKINGPRIVATE_GETCERTIFICATELISTS,
+  ACCESSIBILITY_PRIVATE_SETSWITCHACCESSKEYS,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/manifest_handlers/icons_handler.cc b/extensions/common/manifest_handlers/icons_handler.cc
index 2825ae9..6fb5ce5 100644
--- a/extensions/common/manifest_handlers/icons_handler.cc
+++ b/extensions/common/manifest_handlers/icons_handler.cc
@@ -28,6 +28,7 @@
 
 // static
 const ExtensionIconSet& IconsInfo::GetIcons(const Extension* extension) {
+  DCHECK(extension);
   IconsInfo* info = static_cast<IconsInfo*>(
       extension->GetManifestData(keys::kIcons));
   return info ? info->icons : g_empty_icon_set.Get();
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc
index 22e1bcf..824719f 100644
--- a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc
@@ -434,7 +434,9 @@
     CHECK(SetElementaryStreams(stream_infos));
 }
 
-WiFiDisplayTransportStreamPacketizer::~WiFiDisplayTransportStreamPacketizer() {}
+WiFiDisplayTransportStreamPacketizer::~WiFiDisplayTransportStreamPacketizer() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
 
 bool WiFiDisplayTransportStreamPacketizer::EncodeElementaryStreamUnit(
     unsigned stream_index,
@@ -444,7 +446,7 @@
     base::TimeTicks pts,
     base::TimeTicks dts,
     bool flush) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_LT(stream_index, stream_states_.size());
   ElementaryStreamState& stream_state = stream_states_[stream_index];
 
@@ -549,7 +551,7 @@
 }
 
 bool WiFiDisplayTransportStreamPacketizer::EncodeMetaInformation(bool flush) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   return (EncodeProgramAssociationTable(false) &&
           EncodeProgramMapTables(false) && EncodeProgramClockReference(flush));
@@ -557,7 +559,7 @@
 
 bool WiFiDisplayTransportStreamPacketizer::EncodeProgramAssociationTable(
     bool flush) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Fill in a packet header.
   uint8_t header_data[ts::kPacketHeaderSize];
@@ -576,7 +578,7 @@
 
 bool WiFiDisplayTransportStreamPacketizer::EncodeProgramClockReference(
     bool flush) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   program_clock_reference_ = base::TimeTicks::Now();
 
@@ -607,7 +609,7 @@
 }
 
 bool WiFiDisplayTransportStreamPacketizer::EncodeProgramMapTables(bool flush) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!program_map_table_.empty());
 
   // Fill in a packet header.
@@ -633,7 +635,7 @@
 void WiFiDisplayTransportStreamPacketizer::NormalizeUnitTimeStamps(
     base::TimeTicks* pts,
     base::TimeTicks* dts) const {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Normalize a presentation time stamp.
   if (!pts || pts->is_null())
@@ -651,7 +653,7 @@
 
 bool WiFiDisplayTransportStreamPacketizer::SetElementaryStreams(
     const std::vector<WiFiDisplayElementaryStreamInfo>& stream_infos) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   std::vector<ElementaryStreamState> new_stream_states;
   new_stream_states.reserve(stream_infos.size());
@@ -717,7 +719,7 @@
 void WiFiDisplayTransportStreamPacketizer::UpdateDelayForUnitTimeStamps(
     const base::TimeTicks& pts,
     const base::TimeTicks& dts) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (pts.is_null())
     return;
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h
index c7a883c..797785b 100644
--- a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h"
 
@@ -63,7 +63,7 @@
 // Whenever a Transport Stream (TS) packet is fully created and thus ready for
 // further processing, a pure virtual member function
 // |OnPacketizedTransportStreamPacket| is called.
-class WiFiDisplayTransportStreamPacketizer : public base::NonThreadSafe {
+class WiFiDisplayTransportStreamPacketizer {
  public:
   struct ElementaryStreamState;
 
@@ -134,7 +134,7 @@
   bool SetElementaryStreams(
       const std::vector<WiFiDisplayElementaryStreamInfo>& stream_infos);
 
-  void DetachFromThread() { base::NonThreadSafe::DetachFromThread(); }
+  void DetachFromThread() { DETACH_FROM_THREAD(thread_checker_); }
 
  protected:
   bool EncodeProgramAssociationTable(bool flush);
@@ -170,6 +170,8 @@
   std::vector<ElementaryStreamState> stream_states_;
   std::vector<uint8_t> program_association_table_;
   std::vector<uint8_t> program_map_table_;
+
+  THREAD_CHECKER(thread_checker_);
 };
 
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_app_delegate.cc b/extensions/shell/browser/shell_app_delegate.cc
index 5e5669d..245cc2f 100644
--- a/extensions/shell/browser/shell_app_delegate.cc
+++ b/extensions/shell/browser/shell_app_delegate.cc
@@ -81,7 +81,7 @@
   return true;
 }
 
-int ShellAppDelegate::PreferredIconSize() {
+int ShellAppDelegate::PreferredIconSize() const {
   return extension_misc::EXTENSION_ICON_SMALL;
 }
 
diff --git a/extensions/shell/browser/shell_app_delegate.h b/extensions/shell/browser/shell_app_delegate.h
index 599ebe93..ff124c1c 100644
--- a/extensions/shell/browser/shell_app_delegate.h
+++ b/extensions/shell/browser/shell_app_delegate.h
@@ -46,7 +46,7 @@
                                   const GURL& security_origin,
                                   content::MediaStreamType type,
                                   const Extension* extension) override;
-  int PreferredIconSize() override;
+  int PreferredIconSize() const override;
   void SetWebContentsBlocked(content::WebContents* web_contents,
                              bool blocked) override;
   bool IsWebContentsVisible(content::WebContents* web_contents) override;
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h
index 2d577fe..95216a1 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h
@@ -21,6 +21,8 @@
 extern NSString* const kClearSavedPasswordsCellId;
 extern NSString* const kClearAutofillCellId;
 
+// CollectionView for clearing browsing data (including history,
+// cookies, caches, passwords, and autofill).
 @interface ClearBrowsingDataCollectionViewController
     : SettingsRootCollectionViewController
 
diff --git a/ios/chrome/browser/ui/webui/about_ui.h b/ios/chrome/browser/ui/webui/about_ui.h
index 76b07d9..6a5495b 100644
--- a/ios/chrome/browser/ui/webui/about_ui.h
+++ b/ios/chrome/browser/ui/webui/about_ui.h
@@ -14,6 +14,8 @@
 class WebUIIOS;
 }
 
+// The WebUI controller for chrome://chrome-urls, chrome://histograms,
+// and chrome://credits.
 class AboutUI : public web::WebUIIOSController {
  public:
   explicit AboutUI(web::WebUIIOS* web_ui, const std::string& name);
diff --git a/ios/chrome/browser/voice/BUILD.gn b/ios/chrome/browser/voice/BUILD.gn
index c2a0f1d..5d81ae3 100644
--- a/ios/chrome/browser/voice/BUILD.gn
+++ b/ios/chrome/browser/voice/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("voice") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "speech_input_locale.h",
     "speech_input_locale_config.h",
diff --git a/ios/chrome/browser/voice/speech_input_locale_config.mm b/ios/chrome/browser/voice/speech_input_locale_config.mm
index a4941b3..32c063a8 100644
--- a/ios/chrome/browser/voice/speech_input_locale_config.mm
+++ b/ios/chrome/browser/voice/speech_input_locale_config.mm
@@ -6,6 +6,10 @@
 
 #include "ios/chrome/browser/voice/speech_input_locale_config_impl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace voice {
 
 // static
diff --git a/ios/chrome/browser/voice/speech_input_locale_config_impl.mm b/ios/chrome/browser/voice/speech_input_locale_config_impl.mm
index 4cee9e5..6a83c23e 100644
--- a/ios/chrome/browser/voice/speech_input_locale_config_impl.mm
+++ b/ios/chrome/browser/voice/speech_input_locale_config_impl.mm
@@ -16,6 +16,10 @@
 #include "ios/public/provider/chrome/browser/voice/voice_search_language.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // Returns the language portion of |locale_code|.
diff --git a/ios/chrome/browser/voice/speech_input_locale_match_config.mm b/ios/chrome/browser/voice/speech_input_locale_match_config.mm
index e53a8a0..661bce5eb 100644
--- a/ios/chrome/browser/voice/speech_input_locale_match_config.mm
+++ b/ios/chrome/browser/voice/speech_input_locale_match_config.mm
@@ -5,7 +5,10 @@
 #import "ios/chrome/browser/voice/speech_input_locale_match_config.h"
 
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 namespace {
 
@@ -18,10 +21,7 @@
 
 #pragma mark - SpeechInputLocaleMatchConfig
 
-@interface SpeechInputLocaleMatchConfig () {
-  // Backing object for the property of the same name.
-  base::scoped_nsobject<NSArray> _matches;
-}
+@interface SpeechInputLocaleMatchConfig ()
 
 // Loads |_matches| from config file |plistFileName|.
 - (void)loadConfigFile:(NSString*)plistFileName;
@@ -29,6 +29,7 @@
 @end
 
 @implementation SpeechInputLocaleMatchConfig
+@synthesize matches = _matches;
 
 + (instancetype)sharedInstance {
   static SpeechInputLocaleMatchConfig* matchConfig;
@@ -47,11 +48,6 @@
   return self;
 }
 
-#pragma mark Accessors
-
-- (NSArray*)matches {
-  return _matches.get();
-}
 
 #pragma mark - Private
 
@@ -63,49 +59,29 @@
   NSMutableArray* matches = [NSMutableArray array];
   for (id item in configData) {
     NSDictionary* matchDict = base::mac::ObjCCastStrict<NSDictionary>(item);
-    base::scoped_nsobject<SpeechInputLocaleMatch> match(
-        [[SpeechInputLocaleMatch alloc] initWithDictionary:matchDict]);
+    SpeechInputLocaleMatch* match =
+        [[SpeechInputLocaleMatch alloc] initWithDictionary:matchDict];
     [matches addObject:match];
   }
-  _matches.reset([matches copy]);
+  _matches = [matches copy];
 }
 
 @end
 
 #pragma mark - SpeechInputLocaleMatch
 
-@interface SpeechInputLocaleMatch () {
-  // Backing objects for properties of the same name.
-  base::scoped_nsobject<NSString> _matchedLocaleCode;
-  base::scoped_nsobject<NSArray> _matchingLocaleCodes;
-  base::scoped_nsobject<NSArray> _matchingLanguages;
-}
-
-@end
-
 @implementation SpeechInputLocaleMatch
+@synthesize matchedLocaleCode = _matchedLocaleCode;
+@synthesize matchingLocaleCodes = _matchingLocaleCodes;
+@synthesize matchingLanguages = _matchingLanguages;
 
 - (instancetype)initWithDictionary:(NSDictionary*)matchDict {
   if ((self = [super init])) {
-    _matchedLocaleCode.reset([matchDict[kMatchedLocaleKey] copy]);
-    _matchingLocaleCodes.reset([matchDict[kMatchingLocalesKey] copy]);
-    _matchingLanguages.reset([matchDict[kMatchingLanguagesKey] copy]);
+    _matchedLocaleCode = [matchDict[kMatchedLocaleKey] copy];
+    _matchingLocaleCodes = [matchDict[kMatchingLocalesKey] copy];
+    _matchingLanguages = [matchDict[kMatchingLanguagesKey] copy];
   }
   return self;
 }
 
-#pragma mark Accessors
-
-- (NSString*)matchedLocaleCode {
-  return _matchedLocaleCode;
-}
-
-- (NSArray*)matchingLocaleCodes {
-  return _matchingLocaleCodes;
-}
-
-- (NSArray*)matchingLanguages {
-  return _matchingLanguages;
-}
-
 @end
diff --git a/ios/clean/chrome/browser/ui/context_menu/context_menu_view_controller.mm b/ios/clean/chrome/browser/ui/context_menu/context_menu_view_controller.mm
index d1d120a..441ae48 100644
--- a/ios/clean/chrome/browser/ui/context_menu/context_menu_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/context_menu/context_menu_view_controller.mm
@@ -20,11 +20,16 @@
                                  ContextMenuContext* context) {
   DCHECK(dispatcher);
   DCHECK(context);
+// |-performSelector:withObject:| throws a warning in ARC because the compiler
+// doesn't know how to handle the memory management of the returned values.
+// Since all ContextMenuCommands return void, these warning can be ignored
+// here.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
   for (SEL command : commands) {
-    IMP command_imp = [dispatcher methodForSelector:command];
-    DCHECK(command_imp);
-    command_imp(dispatcher, command, context);
+    [dispatcher performSelector:command withObject:context];
   }
+#pragma clang diagnostic pop
 }
 }
 
diff --git a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
index 4f1be95..2bc00224 100644
--- a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
+++ b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
@@ -72,9 +72,11 @@
 
 - (void)stopFinding {
   web::WebState* webState = self.webStateList->GetActiveWebState();
-  FindTabHelper* helper = FindTabHelper::FromWebState(webState);
-  DCHECK(helper);
-  helper->StopFinding(nil);
+  if (webState) {
+    FindTabHelper* helper = FindTabHelper::FromWebState(webState);
+    DCHECK(helper);
+    helper->StopFinding(nil);
+  }
 }
 
 - (void)findResultsAvailable:(FindInPageModel*)model {
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h
index 1ca74ee..c771697 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h
@@ -10,23 +10,32 @@
 // Interface to support insert/delete/updates to a tab collection.
 @protocol TabCollectionConsumer
 
-// Inserts |item| into tab collection at |index|.
-- (void)insertItem:(TabCollectionItem*)item atIndex:(int)index;
+// Inserts |item| into tab collection at |index|. |SelectedIndex| is the index
+// of the selected item in the tab collection.
+- (void)insertItem:(TabCollectionItem*)item
+           atIndex:(int)index
+     selectedIndex:(int)selectedIndex;
 
-// Deletes |index| from tab collection.
-- (void)deleteItemAtIndex:(int)index;
+// Deletes |index| from tab collection. |SelectedIndex| is the index
+// of the selected tab in the tab collection.
+- (void)deleteItemAtIndex:(int)index selectedIndex:(int)selectedIndex;
 
-// Moves item from |fromIndex| to |toIndex|.
-- (void)moveItemFromIndex:(int)fromIndex toIndex:(int)toIndex;
+// Moves item from |fromIndex| to |toIndex|. |SelectedIndex| is the index
+// of the selected tab in the tab collection.
+- (void)moveItemFromIndex:(int)fromIndex
+                  toIndex:(int)toIndex
+            selectedIndex:(int)selectedIndex;
 
 // Replaces item at |index| with |item|.
 - (void)replaceItemAtIndex:(int)index withItem:(TabCollectionItem*)item;
 
-// Selects the item at |index|.
-- (void)selectItemAtIndex:(int)index;
+// Selects the item at |selectedIndex|.
+- (void)setSelectedIndex:(int)selectedIndex;
 
-// Populates tab collection with |items|.
-- (void)populateItems:(NSArray<TabCollectionItem*>*)items;
+// Populates tab collection with |items|. |SelectedIndex| is the index
+// of the selected item in the tab collection.
+- (void)populateItems:(NSArray<TabCollectionItem*>*)items
+        selectedIndex:(int)selectedIndex;
 @end
 
 #endif  // IOS_CLEAN_CHROME_BROWSER_UI_TAB_COLLECTION_TAB_COLLECTION_CONSUMER_H_
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.mm
index 41ac68e..e6a8eebc 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.mm
@@ -11,15 +11,20 @@
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h"
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_item.h"
 #include "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+@interface TabCollectionMediator ()<CRWWebStateObserver>
+@end
+
 @implementation TabCollectionMediator {
   std::unique_ptr<WebStateListObserverBridge> _webStateListObserver;
   std::unique_ptr<ScopedObserver<WebStateList, WebStateListObserverBridge>>
       _scopedWebStateListObserver;
+  std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
 }
 
 @synthesize webStateList = _webStateList;
@@ -43,22 +48,24 @@
 #pragma mark - Public
 
 - (void)disconnect {
-  self.webStateList = nullptr;
+  _webStateList = nullptr;
+  _webStateObserver.reset();
 }
 
 #pragma mark - Properties
 
 - (void)setWebStateList:(WebStateList*)webStateList {
-  // TODO(crbug.com/727427):Add support for DCHECK(webStateList).
+  DCHECK(webStateList);
   _scopedWebStateListObserver->RemoveAll();
   _webStateList = webStateList;
+  _scopedWebStateListObserver->Add(_webStateList);
+  _webStateObserver = base::MakeUnique<web::WebStateObserverBridge>(
+      self.webStateList->GetActiveWebState(), self);
   [self populateConsumerItems];
-  if (_webStateList) {
-    _scopedWebStateListObserver->Add(_webStateList);
-  }
 }
 
 - (void)setConsumer:(id<TabCollectionConsumer>)consumer {
+  DCHECK(consumer);
   _consumer = consumer;
   [self populateConsumerItems];
 }
@@ -70,7 +77,8 @@
               atIndex:(int)index {
   DCHECK(self.consumer);
   [self.consumer insertItem:[self tabCollectionItemFromWebState:webState]
-                    atIndex:index];
+                    atIndex:index
+              selectedIndex:webStateList->active_index()];
 }
 
 - (void)webStateList:(WebStateList*)webStateList
@@ -78,7 +86,9 @@
            fromIndex:(int)fromIndex
              toIndex:(int)toIndex {
   DCHECK(self.consumer);
-  [self.consumer moveItemFromIndex:fromIndex toIndex:toIndex];
+  [self.consumer moveItemFromIndex:fromIndex
+                           toIndex:toIndex
+                     selectedIndex:webStateList->active_index()];
 }
 
 - (void)webStateList:(WebStateList*)webStateList
@@ -95,7 +105,8 @@
     didDetachWebState:(web::WebState*)webState
               atIndex:(int)index {
   DCHECK(self.consumer);
-  [self.consumer deleteItemAtIndex:index];
+  [self.consumer deleteItemAtIndex:index
+                     selectedIndex:webStateList->active_index()];
 }
 
 - (void)webStateList:(WebStateList*)webStateList
@@ -104,11 +115,27 @@
                     atIndex:(int)atIndex
                  userAction:(BOOL)userAction {
   DCHECK(self.consumer);
-  [self.consumer selectItemAtIndex:atIndex];
+  [self.consumer setSelectedIndex:atIndex];
+  _webStateObserver =
+      base::MakeUnique<web::WebStateObserverBridge>(newWebState, self);
+}
+
+#pragma mark - CRWWebStateObserver
+
+// Navigational changes to the web state update the tab collection, such as
+// the title and snapshot.
+- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
+  DCHECK(self.webStateList);
+  DCHECK(self.consumer);
+  int index = self.webStateList->GetIndexOfWebState(webState);
+  [self.consumer
+      replaceItemAtIndex:index
+                withItem:[self tabCollectionItemFromWebState:webState]];
 }
 
 #pragma mark - Private
 
+// Constructs a TabCollectionItem from a |webState|.
 - (TabCollectionItem*)tabCollectionItemFromWebState:
     (const web::WebState*)webState {
   // PLACEHOLDER: Use real webstate title in the future.
@@ -123,24 +150,26 @@
   return item;
 }
 
+// Constructs an array of TabCollectionItems from a |webStateList|.
 - (NSArray<TabCollectionItem*>*)tabCollectionItemsFromWebStateList:
     (const WebStateList*)webStateList {
   DCHECK(webStateList);
   NSMutableArray<TabCollectionItem*>* items = [[NSMutableArray alloc] init];
   for (int i = 0; i < webStateList->count(); i++) {
-    [items
-        addObject:[self
-                      tabCollectionItemFromWebState:webStateList->GetWebStateAt(
-                                                        i)]];
+    web::WebState* webState = webStateList->GetWebStateAt(i);
+    [items addObject:[self tabCollectionItemFromWebState:webState]];
   }
   return [items copy];
 }
 
+// Constructs an array of TabCollectionItems from the current webStateList
+// and pushes them to the consumer.
 - (void)populateConsumerItems {
   if (self.consumer && self.webStateList) {
-    [self.consumer populateItems:[self tabCollectionItemsFromWebStateList:
-                                           self.webStateList]];
-    [self.consumer selectItemAtIndex:self.webStateList->active_index()];
+    [self.consumer
+        populateItems:[self
+                          tabCollectionItemsFromWebStateList:self.webStateList]
+        selectedIndex:self.webStateList->active_index()];
   }
 }
 
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator_unittest.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator_unittest.mm
index 7b266361..827a315 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator_unittest.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator_unittest.mm
@@ -63,14 +63,14 @@
 // Tests that the consumer is notified of an insert into webStateList.
 TEST_F(TabCollectionMediatorTest, TestInsertWebState) {
   InsertWebState(2);
-  [[consumer_ verify] insertItem:[OCMArg any] atIndex:2];
+  [[consumer_ verify] insertItem:[OCMArg any] atIndex:2 selectedIndex:0];
 }
 
 // Tests that the consumer is notified that a web state has been moved in
 // webStateList.
 TEST_F(TabCollectionMediatorTest, TestMoveWebState) {
   web_state_list_->MoveWebStateAt(0, 2);
-  [[consumer_ verify] moveItemFromIndex:0 toIndex:2];
+  [[consumer_ verify] moveItemFromIndex:0 toIndex:2 selectedIndex:2];
 }
 
 // Tests that the consumer is notified that a web state has been replaced in
@@ -85,12 +85,12 @@
 // webStateList.
 TEST_F(TabCollectionMediatorTest, TestDetachWebState) {
   web_state_list_->CloseWebStateAt(1);
-  [[consumer_ verify] deleteItemAtIndex:1];
+  [[consumer_ verify] deleteItemAtIndex:1 selectedIndex:0];
 }
 
 // Tests that the consumer is notified that the active web state has changed in
 // webStateList.
 TEST_F(TabCollectionMediatorTest, TestChangeActiveWebState) {
   web_state_list_->ActivateWebStateAt(2);
-  [[consumer_ verify] selectItemAtIndex:2];
+  [[consumer_ verify] setSelectedIndex:2];
 }
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
index f904ac5..ff06540a 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
@@ -9,6 +9,7 @@
 #endif
 
 namespace {
+const CGFloat kBorderMargin = 6.0f;
 const CGFloat kSelectedBorderCornerRadius = 8.0f;
 const CGFloat kSelectedBorderWidth = 4.0f;
 }
@@ -17,16 +18,36 @@
 
 - (instancetype)initWithFrame:(CGRect)frame {
   if ((self = [super initWithFrame:frame])) {
-    self.selectedBackgroundView = [[UIView alloc] init];
-    self.selectedBackgroundView.backgroundColor = [UIColor blackColor];
-    self.selectedBackgroundView.layer.cornerRadius =
-        kSelectedBorderCornerRadius;
-    self.selectedBackgroundView.layer.borderWidth = kSelectedBorderWidth;
-    self.selectedBackgroundView.layer.borderColor = self.tintColor.CGColor;
-    self.selectedBackgroundView.transform = CGAffineTransformScale(
-        self.selectedBackgroundView.transform, 1.08, 1.08);
+    [self setupSelectedBackgroundView];
   }
   return self;
 }
 
+- (void)setupSelectedBackgroundView {
+  self.selectedBackgroundView = [[UIView alloc] init];
+  self.selectedBackgroundView.backgroundColor = [UIColor blackColor];
+
+  UIView* border = [[UIView alloc] init];
+  border.translatesAutoresizingMaskIntoConstraints = NO;
+  border.backgroundColor = [UIColor blackColor];
+  border.layer.cornerRadius = kSelectedBorderCornerRadius;
+  border.layer.borderWidth = kSelectedBorderWidth;
+  border.layer.borderColor = self.tintColor.CGColor;
+  [self.selectedBackgroundView addSubview:border];
+  [NSLayoutConstraint activateConstraints:@[
+    [border.topAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.topAnchor
+                       constant:-kBorderMargin],
+    [border.leadingAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.leadingAnchor
+                       constant:-kBorderMargin],
+    [border.trailingAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.trailingAnchor
+                       constant:kBorderMargin],
+    [border.bottomAnchor
+        constraintEqualToAnchor:self.selectedBackgroundView.bottomAnchor
+                       constant:kBorderMargin]
+  ]];
+}
+
 @end
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.h b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.h
index d7dada96..907f4b99 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.h
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.h
@@ -9,8 +9,6 @@
 
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h"
 
-@protocol TabCollectionDataSource;
-
 // Controller for a scrolling view displaying square cells that represent
 // the user's open tabs.
 @interface TabCollectionViewController
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
index 5f64f9d..4fa101e 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
@@ -16,8 +16,11 @@
 
 @interface TabCollectionViewController ()<UICollectionViewDelegate,
                                           SessionCellDelegate>
+// Collection view of tabs.
 @property(nonatomic, readwrite) UICollectionView* tabs;
+// The model backing the collection view.
 @property(nonatomic, readwrite) NSMutableArray<TabCollectionItem*>* items;
+// Selected index of tab collection.
 @property(nonatomic, assign) int selectedIndex;
 @end
 
@@ -50,13 +53,27 @@
     [self.tabs.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
   ]];
 
-  [self selectItemAtIndex:self.selectedIndex];
+  [self.tabs
+      selectItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex
+                                                inSection:0]
+                   animated:NO
+             scrollPosition:UICollectionViewScrollPositionNone];
 }
 
 - (UIStatusBarStyle)preferredStatusBarStyle {
   return UIStatusBarStyleLightContent;
 }
 
+#pragma mark - Setters
+
+- (void)setSelectedIndex:(int)selectedIndex {
+  [self.tabs selectItemAtIndexPath:[NSIndexPath indexPathForItem:selectedIndex
+                                                       inSection:0]
+                          animated:YES
+                    scrollPosition:UICollectionViewScrollPositionNone];
+  _selectedIndex = selectedIndex;
+}
+
 #pragma mark - Required subclass override
 
 - (UICollectionViewLayout*)collectionViewLayout {
@@ -138,47 +155,55 @@
 
 #pragma mark - TabCollectionConsumer methods
 
-- (void)insertItem:(TabCollectionItem*)item atIndex:(int)index {
+- (void)insertItem:(TabCollectionItem*)item
+           atIndex:(int)index
+     selectedIndex:(int)selectedIndex {
+  DCHECK_GE(index, 0);
   DCHECK_LE(static_cast<NSUInteger>(index), self.items.count);
   [self.items insertObject:item atIndex:index];
-  [self.tabs insertItemsAtIndexPaths:@[ [self indexPathForIndex:index] ]];
+  [self.tabs insertItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:index
+                                                            inSection:0] ]];
+  self.selectedIndex = selectedIndex;
 }
 
-- (void)deleteItemAtIndex:(int)index {
+- (void)deleteItemAtIndex:(int)index selectedIndex:(int)selectedIndex {
+  DCHECK_GE(index, 0);
   DCHECK_LT(static_cast<NSUInteger>(index), self.items.count);
   [self.items removeObjectAtIndex:index];
-  [self.tabs deleteItemsAtIndexPaths:@[ [self indexPathForIndex:index] ]];
+  [self.tabs deleteItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:index
+                                                            inSection:0] ]];
+  self.selectedIndex = selectedIndex;
 }
 
-- (void)moveItemFromIndex:(int)fromIndex toIndex:(int)toIndex {
+- (void)moveItemFromIndex:(int)fromIndex
+                  toIndex:(int)toIndex
+            selectedIndex:(int)selectedIndex {
   TabCollectionItem* item = self.items[fromIndex];
   [self.items removeObjectAtIndex:fromIndex];
   [self.items insertObject:item atIndex:toIndex];
-  [self.tabs moveItemAtIndexPath:[self indexPathForIndex:fromIndex]
-                     toIndexPath:[self indexPathForIndex:toIndex]];
+  [self.tabs
+      moveItemAtIndexPath:[NSIndexPath indexPathForItem:fromIndex inSection:0]
+              toIndexPath:[NSIndexPath indexPathForItem:toIndex inSection:0]];
+  self.selectedIndex = selectedIndex;
 }
 
 - (void)replaceItemAtIndex:(int)index withItem:(TabCollectionItem*)item {
-  [self.items removeObjectAtIndex:index];
-  [self.items insertObject:item atIndex:index];
+  DCHECK_GE(index, 0);
+  DCHECK_LT(static_cast<NSUInteger>(index), self.items.count);
+  self.items[index] = item;
+  TabCollectionTabCell* cell = base::mac::ObjCCastStrict<TabCollectionTabCell>(
+      [self.tabs cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index
+                                                            inSection:0]]);
+  [cell setAppearanceForTabTitle:self.items[index].title
+                         favicon:nil
+                        cellSize:CGSizeZero];
 }
 
-- (void)selectItemAtIndex:(int)index {
-  self.selectedIndex = index;
-  [self.tabs selectItemAtIndexPath:[self indexPathForIndex:index]
-                          animated:YES
-                    scrollPosition:UITableViewScrollPositionNone];
-}
-
-- (void)populateItems:(NSArray<TabCollectionItem*>*)items {
+- (void)populateItems:(NSArray<TabCollectionItem*>*)items
+        selectedIndex:(int)selectedIndex {
   self.items = [items mutableCopy];
-  [self.tabs reloadData];
-}
-
-#pragma mark - Private
-
-- (NSIndexPath*)indexPathForIndex:(int)index {
-  return [NSIndexPath indexPathForItem:index inSection:0];
+  [self.tabs reloadItemsAtIndexPaths:[self.tabs indexPathsForVisibleItems]];
+  self.selectedIndex = selectedIndex;
 }
 
 @end
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller_unittest.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller_unittest.mm
index 7d61f0a..f6b5cf8 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller_unittest.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller_unittest.mm
@@ -40,19 +40,21 @@
 
 // Tests that an item is inserted.
 TEST_F(TabCollectionViewControllerTest, TestInsertItem) {
-  [view_controller_ insertItem:[[TabCollectionItem alloc] init] atIndex:0];
+  [view_controller_ insertItem:[[TabCollectionItem alloc] init]
+                       atIndex:0
+                 selectedIndex:0];
   EXPECT_EQ(3, static_cast<int>(view_controller_.items.count));
 }
 
 // Tests that an item is deleted.
 TEST_F(TabCollectionViewControllerTest, TestDeleteItem) {
-  [view_controller_ deleteItemAtIndex:0];
+  [view_controller_ deleteItemAtIndex:0 selectedIndex:0];
   EXPECT_EQ(1, static_cast<int>(view_controller_.items.count));
 }
 
 // Tests that an item is moved.
 TEST_F(TabCollectionViewControllerTest, TestMoveItem) {
-  [view_controller_ moveItemFromIndex:0 toIndex:1];
+  [view_controller_ moveItemFromIndex:0 toIndex:1 selectedIndex:0];
   EXPECT_NSEQ(@"Item1", view_controller_.items[0].title);
 }
 
@@ -68,6 +70,6 @@
 TEST_F(TabCollectionViewControllerTest, TestInitializeItems) {
   TabCollectionItem* item = [[TabCollectionItem alloc] init];
   item.title = @"NewItem";
-  [view_controller_ populateItems:@[ item ]];
+  [view_controller_ populateItems:@[ item ] selectedIndex:0];
   EXPECT_NSEQ(@"NewItem", view_controller_.items[0].title);
 }
diff --git a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
index 1688f3e2..8478ce5 100644
--- a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
@@ -5,6 +5,7 @@
 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.h"
 
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/clean/chrome/browser/ui/commands/tab_grid_commands.h"
 #import "ios/clean/chrome/browser/ui/commands/tab_strip_commands.h"
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.h"
 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_view_controller.h"
@@ -64,6 +65,9 @@
 
 - (void)closeTabStripTabAtIndex:(int)index {
   self.webStateList.CloseWebStateAt(index);
+  if (self.webStateList.empty()) {
+    [static_cast<id<TabGridCommands>>(self.browser->dispatcher()) showTabGrid];
+  }
 }
 
 @end
diff --git a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
index 846f101c..830c61e 100644
--- a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
+++ b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
@@ -42,7 +42,7 @@
 
   TabCollectionItem* item0 = [[TabCollectionItem alloc] init];
   item0.title = @"Tab 0";
-  [self.viewController populateItems:@[ item0 ]];
+  [self.viewController populateItems:@[ item0 ] selectedIndex:0];
 
   [self.baseViewController setHidesBarsOnSwipe:YES];
   [self.baseViewController pushViewController:self.viewController animated:YES];
diff --git a/media/audio/audio_output_proxy.cc b/media/audio/audio_output_proxy.cc
index b215b47d..c3551b1 100644
--- a/media/audio/audio_output_proxy.cc
+++ b/media/audio/audio_output_proxy.cc
@@ -18,12 +18,12 @@
 }
 
 AudioOutputProxy::~AudioOutputProxy() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_ == kCreated || state_ == kClosed) << "State is: " << state_;
 }
 
 bool AudioOutputProxy::Open() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, kCreated);
 
   if (!dispatcher_ || !dispatcher_->OpenStream()) {
@@ -36,7 +36,7 @@
 }
 
 void AudioOutputProxy::Start(AudioSourceCallback* callback) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // We need to support both states since the callback may not handle OnError()
   // immediately (or at all).  It's also possible for subsequent StartStream()
@@ -52,7 +52,7 @@
 }
 
 void AudioOutputProxy::Stop() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (state_ != kPlaying)
     return;
 
@@ -62,7 +62,7 @@
 }
 
 void AudioOutputProxy::SetVolume(double volume) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   volume_ = volume;
 
   if (dispatcher_)
@@ -70,12 +70,12 @@
 }
 
 void AudioOutputProxy::GetVolume(double* volume) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   *volume = volume_;
 }
 
 void AudioOutputProxy::Close() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_ == kCreated || state_ == kOpenError || state_ == kOpened ||
          state_ == kStartError);
 
diff --git a/media/audio/audio_output_proxy.h b/media/audio/audio_output_proxy.h
index 16a0628..d68622f84b 100644
--- a/media/audio/audio_output_proxy.h
+++ b/media/audio/audio_output_proxy.h
@@ -8,7 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
 #include "media/audio/audio_io.h"
 #include "media/base/audio_parameters.h"
 
@@ -24,9 +24,7 @@
 //
 // AudioOutputProxy uses AudioOutputDispatcher to open and close
 // physical output streams.
-class MEDIA_EXPORT AudioOutputProxy
-  : public AudioOutputStream,
-    public NON_EXPORTED_BASE(base::NonThreadSafe) {
+class MEDIA_EXPORT AudioOutputProxy : public AudioOutputStream {
  public:
   // Caller keeps ownership of |dispatcher|.
   explicit AudioOutputProxy(base::WeakPtr<AudioOutputDispatcher> dispatcher);
@@ -62,6 +60,8 @@
   // is stopped, and then started again.
   double volume_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(AudioOutputProxy);
 };
 
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 4cae7313..99b47b5 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -420,7 +420,7 @@
                 return MediaCodecStatus.ERROR;
             }
             boolean usesCbcs = cipherMode == MediaCodec.CRYPTO_MODE_AES_CBC;
-            if (usesCbcs && !MediaCodecUtil.platformSupportsCbcsEncryption()) {
+            if (usesCbcs && !MediaCodecUtil.platformSupportsCbcsEncryption(Build.VERSION.SDK_INT)) {
                 Log.e(TAG, "Encryption scheme 'cbcs' not supported on this platform.");
                 return MediaCodecStatus.ERROR;
             }
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 8f67ebba..f8be531f 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -627,14 +627,14 @@
     }
 
     /**
-     * Returns true if and only if the platform we are running on supports the 'cbcs'
-     * encryption scheme, specifically AES CBC encryption with possibility of pattern
-     * encryption.
+     * Returns true if and only if a platform with the given SDK API level supports the 'cbcs'
+     * encryption scheme, specifically AES CBC encryption with possibility of pattern encryption.
      * While 'cbcs' scheme was originally implemented in N, there was a bug (in the
      * DRM code) which means that it didn't really work properly until N-MR1).
      */
-    static boolean platformSupportsCbcsEncryption() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
+    @CalledByNative
+    static boolean platformSupportsCbcsEncryption(int sdk) {
+        return sdk >= Build.VERSION_CODES.N_MR1;
     }
 
     /**
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
index c6dacedf..58f669a4 100644
--- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
@@ -59,10 +59,7 @@
 
     @CalledByNative
     protected void destroy() {
-        if (mLoadDataUriTask != null) {
-            mLoadDataUriTask.cancel(true);
-            mLoadDataUriTask = null;
-        }
+        cancelLoadDataUriTask();
         mNativeMediaPlayerBridge = 0;
     }
 
@@ -152,6 +149,7 @@
 
     @CalledByNative
     protected void release() {
+        cancelLoadDataUriTask();
         getLocalPlayer().release();
     }
 
@@ -212,10 +210,7 @@
 
     @CalledByNative
     protected boolean setDataUriDataSource(final String url) {
-        if (mLoadDataUriTask != null) {
-            mLoadDataUriTask.cancel(true);
-            mLoadDataUriTask = null;
-        }
+        cancelLoadDataUriTask();
 
         if (!url.startsWith("data:")) return false;
         int headerStop = url.indexOf(',');
@@ -394,4 +389,11 @@
 
     private native void nativeOnDidSetDataUriDataSource(long nativeMediaPlayerBridge,
                                                         boolean success);
+
+    private void cancelLoadDataUriTask() {
+        if (mLoadDataUriTask != null) {
+            mLoadDataUriTask.cancel(true);
+            mLoadDataUriTask = null;
+        }
+    }
 }
diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc
index 101d1696..62c5e8fc 100644
--- a/media/base/android/media_codec_util.cc
+++ b/media/base/android/media_codec_util.cc
@@ -198,6 +198,12 @@
 }
 
 // static
+bool MediaCodecUtil::PlatformSupportsCbcsEncryption(int sdk) {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_MediaCodecUtil_platformSupportsCbcsEncryption(env, sdk);
+}
+
+// static
 std::set<int> MediaCodecUtil::GetEncoderColorFormats(
     const std::string& mime_type) {
   std::set<int> color_formats;
diff --git a/media/base/android/media_codec_util.h b/media/base/android/media_codec_util.h
index 4b122e3..c1b8c4be 100644
--- a/media/base/android/media_codec_util.h
+++ b/media/base/android/media_codec_util.h
@@ -43,6 +43,9 @@
   // Returns true if MediaCodec.setParameters() is available on the device.
   static bool SupportsSetParameters();
 
+  // Returns true if MediaCodec supports CBCS Encryption.
+  static bool PlatformSupportsCbcsEncryption(int sdk);
+
   // Returns whether it's possible to create a MediaCodec for the given codec
   // and secureness.
   static bool CanDecode(VideoCodec codec, bool is_secure);
diff --git a/media/base/android/media_codec_util_unittest.cc b/media/base/android/media_codec_util_unittest.cc
index 879180df..7c935e91 100644
--- a/media/base/android/media_codec_util_unittest.cc
+++ b/media/base/android/media_codec_util_unittest.cc
@@ -18,6 +18,7 @@
 using base::android::SDK_VERSION_LOLLIPOP_MR1;
 using base::android::SDK_VERSION_MARSHMALLOW;
 using base::android::SDK_VERSION_NOUGAT;
+using base::android::SDK_VERSION_NOUGAT_MR1;
 
 class MediaCodecUtilTest : public testing::Test {
  public:
@@ -73,4 +74,13 @@
   }
 }
 
+TEST_F(MediaCodecUtilTest, TestCbcsAvailableIfNewerVersion) {
+  EXPECT_FALSE(
+      MediaCodecUtil::PlatformSupportsCbcsEncryption(SDK_VERSION_MARSHMALLOW));
+  EXPECT_FALSE(
+      MediaCodecUtil::PlatformSupportsCbcsEncryption(SDK_VERSION_NOUGAT));
+  EXPECT_TRUE(
+      MediaCodecUtil::PlatformSupportsCbcsEncryption(SDK_VERSION_NOUGAT_MR1));
+}
+
 }  // namespace media
diff --git a/net/cert/internal/nist_pkits_unittest.cc b/net/cert/internal/nist_pkits_unittest.cc
index 8980709..6697ded9 100644
--- a/net/cert/internal/nist_pkits_unittest.cc
+++ b/net/cert/internal/nist_pkits_unittest.cc
@@ -57,15 +57,18 @@
 }
 
 void PkitsTestInfo::SetInitialExplicitPolicy(bool b) {
-  initial_explicit_policy = b;
+  initial_explicit_policy =
+      b ? InitialExplicitPolicy::kTrue : InitialExplicitPolicy::kFalse;
 }
 
 void PkitsTestInfo::SetInitialPolicyMappingInhibit(bool b) {
-  initial_policy_mapping_inhibit = b;
+  initial_policy_mapping_inhibit = b ? InitialPolicyMappingInhibit::kTrue
+                                     : InitialPolicyMappingInhibit::kFalse;
 }
 
 void PkitsTestInfo::SetInitialInhibitAnyPolicy(bool b) {
-  initial_inhibit_any_policy = b;
+  initial_inhibit_any_policy =
+      b ? InitialAnyPolicyInhibit::kTrue : InitialAnyPolicyInhibit::kFalse;
 }
 
 PkitsTestInfo::~PkitsTestInfo() = default;
diff --git a/net/cert/internal/nist_pkits_unittest.h b/net/cert/internal/nist_pkits_unittest.h
index adc064b6..8f9ef42 100644
--- a/net/cert/internal/nist_pkits_unittest.h
+++ b/net/cert/internal/nist_pkits_unittest.h
@@ -46,13 +46,15 @@
   std::set<der::Input> initial_policy_set;
 
   // The value of "initial-explicit-policy".
-  bool initial_explicit_policy = false;
+  InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
 
   // The value of "initial-policy-mapping-inhibit".
-  bool initial_policy_mapping_inhibit = false;
+  InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
+      InitialPolicyMappingInhibit::kFalse;
 
   // The value of "initial-inhibit-any-policy".
-  bool initial_inhibit_any_policy = false;
+  InitialAnyPolicyInhibit initial_inhibit_any_policy =
+      InitialAnyPolicyInhibit::kFalse;
 
   // This is the time when PKITS was published.
   der::GeneralizedTime time = {2011, 4, 15, 0, 0, 0};
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index d1ce0bdb..05b66d6 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -604,9 +604,12 @@
 
   // Verify the entire certificate chain.
   auto result_path = base::MakeUnique<ResultPath>();
-  VerifyCertificateChain(next_path_.certs, next_path_.last_cert_trust,
-                         signature_policy_, time_, key_purpose_,
-                         &result_path->errors);
+  // TODO(eroman): don't pass placeholder for policy.
+  VerifyCertificateChain(
+      next_path_.certs, next_path_.last_cert_trust, signature_policy_, time_,
+      key_purpose_, InitialExplicitPolicy::kFalse, {AnyPolicy()},
+      InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse,
+      nullptr /*user_constrained_policy_set*/, &result_path->errors);
   bool verify_result = !result_path->errors.ContainsHighSeverityErrors();
 
   DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
diff --git a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
index e6dfa424e..ca3d81b 100644
--- a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
@@ -30,7 +30,7 @@
         trust_store.AddTrustAnchorWithConstraints(test.chain.back());
         break;
       case CertificateTrustType::UNSPECIFIED:
-        LOG(ERROR) << "Unexpected CertificateTrustType";
+        trust_store.AddCertificateWithUnspecifiedTrust(test.chain.back());
         break;
       case CertificateTrustType::DISTRUSTED:
         trust_store.AddDistrustedCertificateForTest(test.chain.back());
diff --git a/net/cert/internal/test_helpers.cc b/net/cert/internal/test_helpers.cc
index 16c39cf1..70bd6447 100644
--- a/net/cert/internal/test_helpers.cc
+++ b/net/cert/internal/test_helpers.cc
@@ -125,7 +125,8 @@
   return ::testing::AssertionSuccess();
 }
 
-VerifyCertChainTest::VerifyCertChainTest() = default;
+VerifyCertChainTest::VerifyCertChainTest()
+    : user_initial_policy_set{AnyPolicy()} {}
 VerifyCertChainTest::~VerifyCertChainTest() = default;
 
 bool VerifyCertChainTest::HasHighSeverityErrors() const {
diff --git a/net/cert/internal/test_helpers.h b/net/cert/internal/test_helpers.h
index dceb068..d8b1c69 100644
--- a/net/cert/internal/test_helpers.h
+++ b/net/cert/internal/test_helpers.h
@@ -95,6 +95,16 @@
   // The Key Purpose to use when verifying the chain.
   KeyPurpose key_purpose = KeyPurpose::ANY_EKU;
 
+  InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
+
+  std::set<der::Input> user_initial_policy_set;
+
+  InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
+      InitialPolicyMappingInhibit::kFalse;
+
+  InitialAnyPolicyInhibit initial_any_policy_inhibit =
+      InitialAnyPolicyInhibit::kFalse;
+
   // The expected errors/warnings from verification (as a string).
   std::string expected_errors;
 
diff --git a/net/cert/internal/trust_store_in_memory.cc b/net/cert/internal/trust_store_in_memory.cc
index 34f53b57..09efd721 100644
--- a/net/cert/internal/trust_store_in_memory.cc
+++ b/net/cert/internal/trust_store_in_memory.cc
@@ -28,6 +28,11 @@
   AddCertificate(std::move(cert), CertificateTrust::ForDistrusted());
 }
 
+void TrustStoreInMemory::AddCertificateWithUnspecifiedTrust(
+    scoped_refptr<ParsedCertificate> cert) {
+  AddCertificate(std::move(cert), CertificateTrust::ForUnspecified());
+}
+
 void TrustStoreInMemory::SyncGetIssuersOf(const ParsedCertificate* cert,
                                           ParsedCertificateList* issuers) {
   auto range = entries_.equal_range(cert->normalized_issuer().AsStringPiece());
diff --git a/net/cert/internal/trust_store_in_memory.h b/net/cert/internal/trust_store_in_memory.h
index 19d542f..6c705b7 100644
--- a/net/cert/internal/trust_store_in_memory.h
+++ b/net/cert/internal/trust_store_in_memory.h
@@ -37,6 +37,10 @@
   // issuer/serial is probably what we would want for a real implementation).
   void AddDistrustedCertificateForTest(scoped_refptr<ParsedCertificate> cert);
 
+  // Adds a certificate to the store, that is neither trusted nor untrusted.
+  void AddCertificateWithUnspecifiedTrust(
+      scoped_refptr<ParsedCertificate> cert);
+
   // TrustStore implementation:
   void SyncGetIssuersOf(const ParsedCertificate* cert,
                         ParsedCertificateList* issuers) override;
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index 2560b942..d4f6bc8 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -4,6 +4,7 @@
 
 #include "net/cert/internal/verify_certificate_chain.h"
 
+#include <algorithm>
 #include <memory>
 
 #include "base/logging.h"
@@ -63,6 +64,9 @@
                      "The extended key usage does not include client auth");
 DEFINE_CERT_ERROR_ID(kCertIsNotTrustAnchor,
                      "Certificate is not a trust anchor");
+DEFINE_CERT_ERROR_ID(kNoValidPolicy, "No valid policy");
+DEFINE_CERT_ERROR_ID(kPolicyMappingAnyPolicy,
+                     "PolicyMappings must not map anyPolicy");
 
 bool IsHandledCriticalExtensionOid(const der::Input& oid) {
   if (oid == BasicConstraintsOid())
@@ -78,8 +82,19 @@
     return true;
   if (oid == SubjectAltNameOid())
     return true;
+  // TODO(eroman): The policy qualifiers are not processed (or in some cases
+  // even parsed). This is fine when the policies extension is non-critical,
+  // however if it is critical the code should also ensure that the policy
+  // qualifiers are only recognized ones (CPS and User Notice).
+  if (oid == CertificatePoliciesOid())
+    return true;
+  if (oid == PolicyMappingsOid())
+    return true;
+  if (oid == PolicyConstraintsOid())
+    return true;
+  if (oid == InhibitAnyPolicyOid())
+    return true;
 
-  // TODO(eroman): Make this more complete.
   return false;
 }
 
@@ -120,7 +135,7 @@
 //    The validity period for a certificate is the period of time from
 //    notBefore through notAfter, inclusive.
 void VerifyTimeValidity(const ParsedCertificate& cert,
-                        const der::GeneralizedTime time,
+                        const der::GeneralizedTime& time,
                         CertErrors* errors) {
   if (time < cert.tbs().validity_not_before)
     errors->AddError(kValidityFailedNotBefore);
@@ -216,16 +231,546 @@
   }
 }
 
-// This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
-// Processing" procedure.
-void BasicCertificateProcessing(
+// Returns |true| if |policies| contains the OID |search_oid|.
+bool SetContains(const std::set<der::Input>& policies,
+                 const der::Input& search_oid) {
+  return policies.count(search_oid) > 0;
+}
+
+// Representation of RFC 5280's "valid_policy_tree", used to keep track of the
+// valid policies and policy re-mappings.
+//
+// ValidPolicyTree differs slightly from RFC 5280's description in that:
+//
+//  (1) It does not track "qualifier_set". This is not needed as it is not
+//      output by this implementation.
+//
+//  (2) It only stores the most recent level of the policy tree rather than
+//      the full tree of nodes.
+class ValidPolicyTree {
+ public:
+  ValidPolicyTree() {}
+
+  struct Node {
+    // |root_policy| is equivalent to |valid_policy|, but in the domain of the
+    // caller.
+    //
+    // The reason for this distinction is the Policy Mappings extension.
+    //
+    // So whereas |valid_policy| is in the remapped domain defined by the
+    // issuing certificate, |root_policy| is in the fixed domain of the caller.
+    //
+    // OIDs in "user_initial_policy_set" and "user_constrained_policy_set" are
+    // directly comparable to |root_policy| values, but not necessarily to
+    // |valid_policy|.
+    //
+    // In terms of the valid policy tree, |root_policy| can be found by
+    // starting at the node's root ancestor, and finding the first node with a
+    // valid_policy other than anyPolicy. This is effectively the same process
+    // as used during policy tree intersection in RFC 5280 6.1.5.g.iii.1
+    der::Input root_policy;
+
+    // The same as RFC 5280's "valid_policy" variable.
+    der::Input valid_policy;
+
+    // The same as RFC 5280s "expected_policy_set" variable.
+    std::set<der::Input> expected_policy_set;
+
+    // Note that RFC 5280's "qualifier_set" is omitted.
+  };
+
+  // Level represents all the nodes at depth "i" in the valid_policy_tree.
+  using Level = std::vector<Node>;
+
+  // Initializes the ValidPolicyTree for the given "user_initial_policy_set".
+  //
+  // In RFC 5280, the valid_policy_tree is initialized to a root node at depth
+  // 0 of "anyPolicy"; the intersection with the "user_initial_policy_set" is
+  // done at the end (Wrap Up) as described in section 6.1.5 step g.
+  //
+  // Whereas in this implementation, the restriction on policies is added here,
+  // and intersecting the valid policy tree during Wrap Up is no longer needed.
+  //
+  // The final "user_constrained_policy_set" obtained will be the same. The
+  // advantages of this approach is simpler code.
+  void Init(const std::set<der::Input>& user_initial_policy_set) {
+    Clear();
+    for (const der::Input& policy_oid : user_initial_policy_set)
+      AddRootNode(policy_oid);
+  }
+
+  // Returns the current level (i.e. all nodes at depth i in the valid
+  // policy tree).
+  const Level& current_level() const { return current_level_; }
+  Level& current_level() { return current_level_; }
+
+  // In RFC 5280 valid_policy_tree may be set to null. That is represented here
+  // by emptiness.
+  bool IsNull() const { return current_level_.empty(); }
+  void SetNull() { Clear(); }
+
+  // This implementation keeps only the last level of the valid policy
+  // tree. Calling StartLevel() returns the nodes for the previous
+  // level, and starts a new level.
+  Level StartLevel() {
+    Level prev_level;
+    std::swap(prev_level, current_level_);
+    return prev_level;
+  }
+
+  // Gets the set of policies (in terms of root authority's policy domain) that
+  // are valid at the curent level of the policy tree.
+  //
+  // For example:
+  //
+  //  * If the valid policy tree was initialized with anyPolicy, then this
+  //    function returns what X.509 calls "authorities-constrained-policy-set".
+  //
+  //  * If the valid policy tree was instead initialized with the
+  //    "user-initial-policy_set", then this function returns what X.509
+  //    calls "user-constrained-policy-set"
+  //    ("authorities-constrained-policy-set" intersected with the
+  //    "user-initial-policy-set").
+  void GetValidRootPolicySet(std::set<der::Input>* policy_set) {
+    policy_set->clear();
+    for (const Node& node : current_level_)
+      policy_set->insert(node.root_policy);
+
+    // If the result includes anyPolicy, simplify it to a set of size 1.
+    if (policy_set->size() > 1 && SetContains(*policy_set, AnyPolicy()))
+      *policy_set = {AnyPolicy()};
+  }
+
+  // Adds a node |n| to the current level which is a child of |parent|
+  // such that:
+  //   * n.valid_policy = policy_oid
+  //   * n.expected_policy_set = {policy_oid}
+  void AddNode(const Node& parent, const der::Input& policy_oid) {
+    AddNodeWithExpectedPolicySet(parent, policy_oid, {policy_oid});
+  }
+
+  // Adds a node |n| to the current level which is a child of |parent|
+  // such that:
+  //   * n.valid_policy = policy_oid
+  //   * n.expected_policy_set = expected_policy_set
+  void AddNodeWithExpectedPolicySet(
+      const Node& parent,
+      const der::Input& policy_oid,
+      const std::set<der::Input>& expected_policy_set) {
+    Node new_node;
+    new_node.valid_policy = policy_oid;
+    new_node.expected_policy_set = expected_policy_set;
+
+    // Consider the root policy as the first policy other than anyPolicy (or
+    // anyPolicy if it hasn't been restricted yet).
+    new_node.root_policy =
+        (parent.root_policy == AnyPolicy()) ? policy_oid : parent.root_policy;
+
+    current_level_.push_back(std::move(new_node));
+  }
+
+  // Returns the first node having valid_policy == anyPolicy in |level|, or
+  // nullptr if there is none.
+  static const Node* FindAnyPolicyNode(const Level& level) {
+    for (const Node& node : level) {
+      if (node.valid_policy == AnyPolicy())
+        return &node;
+    }
+    return nullptr;
+  }
+
+  // Deletes all nodes |n| in |level| where |n.valid_policy| matches the given
+  // |valid_policy|. This may re-order the nodes in |level|.
+  static void DeleteNodesMatchingValidPolicy(const der::Input& valid_policy,
+                                             Level* level) {
+    // This works by swapping nodes to the end of the vector, and then doing a
+    // single resize to delete them all.
+    auto cur = level->begin();
+    auto end = level->end();
+    while (cur != end) {
+      bool should_delete_node = cur->valid_policy == valid_policy;
+      if (should_delete_node) {
+        end = std::prev(end);
+        if (cur != end)
+          std::iter_swap(cur, end);
+      } else {
+        ++cur;
+      }
+    }
+    level->erase(end, level->end());
+  }
+
+ private:
+  // Deletes all nodes in the valid policy tree.
+  void Clear() { current_level_.clear(); }
+
+  // Adds a node to the current level for OID |policy_oid|. The current level
+  // is assumed to be the root level.
+  void AddRootNode(const der::Input& policy_oid) {
+    Node new_node;
+    new_node.root_policy = policy_oid;
+    new_node.valid_policy = policy_oid;
+    new_node.expected_policy_set = {policy_oid};
+    current_level_.push_back(std::move(new_node));
+  }
+
+  Level current_level_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValidPolicyTree);
+};
+
+// Class that encapsulates the state variables used by certificate path
+// validation.
+class PathVerifier {
+ public:
+  // Same parameters and meaning as VerifyCertificateChain().
+  void Run(const ParsedCertificateList& certs,
+           const CertificateTrust& last_cert_trust,
+           const SignaturePolicy* signature_policy,
+           const der::GeneralizedTime& time,
+           KeyPurpose required_key_purpose,
+           InitialExplicitPolicy initial_explicit_policy,
+           const std::set<der::Input>& user_initial_policy_set,
+           InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+           InitialAnyPolicyInhibit initial_any_policy_inhibit,
+           std::set<der::Input>* user_constrained_policy_set,
+           CertPathErrors* errors);
+
+ private:
+  // Verifies and updates the valid policies. This corresponds with RFC 5280
+  // section 6.1.3 steps d-f.
+  void VerifyPolicies(const ParsedCertificate& cert,
+                      bool is_target_cert,
+                      CertErrors* errors);
+
+  // Applies the policy mappings. This corresponds with RFC 5280 section 6.1.4
+  // steps a-b.
+  void VerifyPolicyMappings(const ParsedCertificate& cert, CertErrors* errors);
+
+  // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
+  // Processing" procedure.
+  void BasicCertificateProcessing(const ParsedCertificate& cert,
+                                  bool is_target_cert,
+                                  const SignaturePolicy* signature_policy,
+                                  const der::GeneralizedTime& time,
+                                  KeyPurpose required_key_purpose,
+                                  CertErrors* errors);
+
+  // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
+  // Certificate i+1" procedure. |cert| is expected to be an intermediate.
+  void PrepareForNextCertificate(const ParsedCertificate& cert,
+                                 CertErrors* errors);
+
+  // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up
+  // Procedure". It does processing for the final certificate (the target cert).
+  void WrapUp(const ParsedCertificate& cert, CertErrors* errors);
+
+  // Enforces trust anchor constraints compatibile with RFC 5937.
+  //
+  // Note that the anchor constraints are encoded via the attached certificate
+  // itself.
+  void ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
+                                   KeyPurpose required_key_purpose,
+                                   CertErrors* errors);
+
+  // Initializes the path validation algorithm given anchor constraints. This
+  // follows the description in RFC 5937
+  void ProcessRootCertificate(const ParsedCertificate& cert,
+                              const CertificateTrust& trust,
+                              KeyPurpose required_key_purpose,
+                              CertErrors* errors);
+
+  ValidPolicyTree valid_policy_tree_;
+
+  // Will contain a NameConstraints for each previous cert in the chain which
+  // had nameConstraints. This corresponds to the permitted_subtrees and
+  // excluded_subtrees state variables from RFC 5280.
+  std::vector<const NameConstraints*> name_constraints_list_;
+
+  // |explicit_policy_| corresponds with the same named variable from RFC 5280
+  // section 6.1.2:
+  //
+  //   explicit_policy:  an integer that indicates if a non-NULL
+  //   valid_policy_tree is required.  The integer indicates the
+  //   number of non-self-issued certificates to be processed before
+  //   this requirement is imposed.  Once set, this variable may be
+  //   decreased, but may not be increased.  That is, if a certificate in the
+  //   path requires a non-NULL valid_policy_tree, a later certificate cannot
+  //   remove this requirement.  If initial-explicit-policy is set, then the
+  //   initial value is 0, otherwise the initial value is n+1.
+  size_t explicit_policy_;
+
+  // |inhibit_any_policy_| corresponds with the same named variable from RFC
+  // 5280 section 6.1.2:
+  //
+  //   inhibit_anyPolicy:  an integer that indicates whether the
+  //   anyPolicy policy identifier is considered a match.  The
+  //   integer indicates the number of non-self-issued certificates
+  //   to be processed before the anyPolicy OID, if asserted in a
+  //   certificate other than an intermediate self-issued
+  //   certificate, is ignored.  Once set, this variable may be
+  //   decreased, but may not be increased.  That is, if a
+  //   certificate in the path inhibits processing of anyPolicy, a
+  //   later certificate cannot permit it.  If initial-any-policy-
+  //   inhibit is set, then the initial value is 0, otherwise the
+  //   initial value is n+1.
+  size_t inhibit_any_policy_;
+
+  // |policy_mapping_| corresponds with the same named variable from RFC 5280
+  // section 6.1.2:
+  //
+  //   policy_mapping:  an integer that indicates if policy mapping
+  //   is permitted.  The integer indicates the number of non-self-
+  //   issued certificates to be processed before policy mapping is
+  //   inhibited.  Once set, this variable may be decreased, but may
+  //   not be increased.  That is, if a certificate in the path
+  //   specifies that policy mapping is not permitted, it cannot be
+  //   overridden by a later certificate.  If initial-policy-
+  //   mapping-inhibit is set, then the initial value is 0,
+  //   otherwise the initial value is n+1.
+  size_t policy_mapping_;
+
+  // |working_spki_| is an amalgamation of 3 separate variables from RFC 5280:
+  //    * working_public_key
+  //    * working_public_key_algorithm
+  //    * working_public_key_parameters
+  //
+  // They are combined for simplicity since the signature verification takes an
+  // SPKI, and the parameter inheritence is not applicable for the supported
+  // key types.
+  //
+  // An approximate explanation of |working_spki| is this description from RFC
+  // 5280 section 6.1.2:
+  //
+  //    working_public_key:  the public key used to verify the
+  //    signature of a certificate.
+  der::Input working_spki_;
+
+  // |working_normalized_issuer_name_| is the normalized value of the
+  // working_issuer_name variable in RFC 5280 section 6.1.2:
+  //
+  //    working_issuer_name:  the issuer distinguished name expected
+  //    in the next certificate in the chain.
+  der::Input working_normalized_issuer_name_;
+
+  // |max_path_length_| corresponds with the same named variable in RFC 5280
+  // section 6.1.2.
+  //
+  //    max_path_length:  this integer is initialized to n, is
+  //    decremented for each non-self-issued certificate in the path,
+  //    and may be reduced to the value in the path length constraint
+  //    field within the basic constraints extension of a CA
+  //    certificate.
+  size_t max_path_length_;
+};
+
+void PathVerifier::VerifyPolicies(const ParsedCertificate& cert,
+                                  bool is_target_cert,
+                                  CertErrors* errors) {
+  // From RFC 5280 section 6.1.3:
+  //
+  //  (d)  If the certificate policies extension is present in the
+  //       certificate and the valid_policy_tree is not NULL, process
+  //       the policy information by performing the following steps in
+  //       order:
+  if (cert.has_policy_oids() && !valid_policy_tree_.IsNull()) {
+    ValidPolicyTree::Level previous_level = valid_policy_tree_.StartLevel();
+
+    // Identify if there was a node with valid_policy == anyPolicy at depth i-1.
+    const ValidPolicyTree::Node* any_policy_node_prev_level =
+        ValidPolicyTree::FindAnyPolicyNode(previous_level);
+
+    //     (1)  For each policy P not equal to anyPolicy in the
+    //          certificate policies extension, let P-OID denote the OID
+    //          for policy P and P-Q denote the qualifier set for policy
+    //          P.  Perform the following steps in order:
+    bool cert_has_any_policy = false;
+    for (const der::Input& p_oid : cert.policy_oids()) {
+      if (p_oid == AnyPolicy()) {
+        cert_has_any_policy = true;
+        continue;
+      }
+
+      //        (i)   For each node of depth i-1 in the valid_policy_tree
+      //              where P-OID is in the expected_policy_set, create a
+      //              child node as follows: set the valid_policy to P-OID,
+      //              set the qualifier_set to P-Q, and set the
+      //              expected_policy_set to {P-OID}.
+      bool found_match = false;
+      for (const ValidPolicyTree::Node& prev_node : previous_level) {
+        if (SetContains(prev_node.expected_policy_set, p_oid)) {
+          valid_policy_tree_.AddNode(prev_node, p_oid);
+          found_match = true;
+        }
+      }
+
+      //        (ii)  If there was no match in step (i) and the
+      //              valid_policy_tree includes a node of depth i-1 with
+      //              the valid_policy anyPolicy, generate a child node with
+      //              the following values: set the valid_policy to P-OID,
+      //              set the qualifier_set to P-Q, and set the
+      //              expected_policy_set to  {P-OID}.
+      if (!found_match && any_policy_node_prev_level)
+        valid_policy_tree_.AddNode(*any_policy_node_prev_level, p_oid);
+    }
+
+    //     (2)  If the certificate policies extension includes the policy
+    //          anyPolicy with the qualifier set AP-Q and either (a)
+    //          inhibit_anyPolicy is greater than 0 or (b) i<n and the
+    //          certificate is self-issued, then:
+    //
+    //          For each node in the valid_policy_tree of depth i-1, for
+    //          each value in the expected_policy_set (including
+    //          anyPolicy) that does not appear in a child node, create a
+    //          child node with the following values: set the valid_policy
+    //          to the value from the expected_policy_set in the parent
+    //          node, set the qualifier_set to AP-Q, and set the
+    //          expected_policy_set to the value in the valid_policy from
+    //          this node.
+    if (cert_has_any_policy && ((inhibit_any_policy_ > 0) ||
+                                (!is_target_cert && IsSelfIssued(cert)))) {
+      // Keep track of the existing policies at depth i.
+      std::set<der::Input> child_node_policies;
+      for (const ValidPolicyTree::Node& node :
+           valid_policy_tree_.current_level())
+        child_node_policies.insert(node.valid_policy);
+
+      for (const ValidPolicyTree::Node& prev_node : previous_level) {
+        for (const der::Input& expected_policy :
+             prev_node.expected_policy_set) {
+          if (!SetContains(child_node_policies, expected_policy)) {
+            child_node_policies.insert(expected_policy);
+            valid_policy_tree_.AddNode(prev_node, expected_policy);
+          }
+        }
+      }
+    }
+
+    //     (3)  If there is a node in the valid_policy_tree of depth i-1
+    //          or less without any child nodes, delete that node.  Repeat
+    //          this step until there are no nodes of depth i-1 or less
+    //          without children.
+    //
+    // Nothing needs to be done for this step, since this implementation only
+    // stores the nodes at depth i, and the entire level has already been
+    // calculated.
+  }
+
+  //  (e)  If the certificate policies extension is not present, set the
+  //       valid_policy_tree to NULL.
+  if (!cert.has_policy_oids())
+    valid_policy_tree_.SetNull();
+
+  //  (f)  Verify that either explicit_policy is greater than 0 or the
+  //       valid_policy_tree is not equal to NULL;
+  if (!((explicit_policy_ > 0) || !valid_policy_tree_.IsNull()))
+    errors->AddError(kNoValidPolicy);
+}
+
+void PathVerifier::VerifyPolicyMappings(const ParsedCertificate& cert,
+                                        CertErrors* errors) {
+  if (!cert.has_policy_mappings())
+    return;
+
+  // From RFC 5280 section 6.1.4:
+  //
+  //  (a)  If a policy mappings extension is present, verify that the
+  //       special value anyPolicy does not appear as an
+  //       issuerDomainPolicy or a subjectDomainPolicy.
+  for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+    if (mapping.issuer_domain_policy == AnyPolicy() ||
+        mapping.subject_domain_policy == AnyPolicy()) {
+      // Because this implementation continues processing certificates after
+      // this error, clear the valid policy tree to ensure the
+      // "user_constrained_policy_set" output upon failure is empty.
+      valid_policy_tree_.SetNull();
+      errors->AddError(kPolicyMappingAnyPolicy);
+    }
+  }
+
+  //  (b)  If a policy mappings extension is present, then for each
+  //       issuerDomainPolicy ID-P in the policy mappings extension:
+  //
+  //     (1)  If the policy_mapping variable is greater than 0, for each
+  //          node in the valid_policy_tree of depth i where ID-P is the
+  //          valid_policy, set expected_policy_set to the set of
+  //          subjectDomainPolicy values that are specified as
+  //          equivalent to ID-P by the policy mappings extension.
+  //
+  //          If no node of depth i in the valid_policy_tree has a
+  //          valid_policy of ID-P but there is a node of depth i with a
+  //          valid_policy of anyPolicy, then generate a child node of
+  //          the node of depth i-1 that has a valid_policy of anyPolicy
+  //          as follows:
+  //
+  //        (i)    set the valid_policy to ID-P;
+  //
+  //        (ii)   set the qualifier_set to the qualifier set of the
+  //               policy anyPolicy in the certificate policies
+  //               extension of certificate i; and
+  //
+  //        (iii)  set the expected_policy_set to the set of
+  //               subjectDomainPolicy values that are specified as
+  //               equivalent to ID-P by the policy mappings extension.
+  //
+  if (policy_mapping_ > 0) {
+    const ValidPolicyTree::Node* any_policy_node =
+        ValidPolicyTree::FindAnyPolicyNode(valid_policy_tree_.current_level());
+
+    // Group mappings by issuer domain policy.
+    std::map<der::Input, std::set<der::Input>> mappings;
+    for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+      mappings[mapping.issuer_domain_policy].insert(
+          mapping.subject_domain_policy);
+    }
+
+    for (const auto& it : mappings) {
+      const der::Input& issuer_domain_policy = it.first;
+      const std::set<der::Input>& subject_domain_policies = it.second;
+      bool found_node = false;
+
+      for (ValidPolicyTree::Node& node : valid_policy_tree_.current_level()) {
+        if (node.valid_policy == issuer_domain_policy) {
+          node.expected_policy_set = subject_domain_policies;
+          found_node = true;
+        }
+      }
+
+      if (!found_node && any_policy_node) {
+        valid_policy_tree_.AddNodeWithExpectedPolicySet(
+            *any_policy_node, issuer_domain_policy, subject_domain_policies);
+      }
+    }
+  }
+
+  //  (b)  If a policy mappings extension is present, then for each
+  //       issuerDomainPolicy ID-P in the policy mappings extension:
+  //
+  //  ...
+  //
+  //     (2)  If the policy_mapping variable is equal to 0:
+  //
+  //        (i)    delete each node of depth i in the valid_policy_tree
+  //               where ID-P is the valid_policy.
+  //
+  //        (ii)   If there is a node in the valid_policy_tree of depth
+  //               i-1 or less without any child nodes, delete that
+  //               node.  Repeat this step until there are no nodes of
+  //               depth i-1 or less without children.
+  if (policy_mapping_ == 0) {
+    for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
+      ValidPolicyTree::DeleteNodesMatchingValidPolicy(
+          mapping.issuer_domain_policy, &valid_policy_tree_.current_level());
+    }
+  }
+}
+
+void PathVerifier::BasicCertificateProcessing(
     const ParsedCertificate& cert,
     bool is_target_cert,
     const SignaturePolicy* signature_policy,
     const der::GeneralizedTime& time,
-    const der::Input& working_spki,
-    const der::Input& working_normalized_issuer_name,
-    const std::vector<const NameConstraints*>& name_constraints_list,
+    KeyPurpose required_key_purpose,
     CertErrors* errors) {
   // Check that the signature algorithms in Certificate vs TBSCertificate
   // match. This isn't part of RFC 5280 section 6.1.3, but is mandated by
@@ -235,7 +780,7 @@
   // Verify the digital signature using the previous certificate's key (RFC
   // 5280 section 6.1.3 step a.1).
   if (!VerifySignedData(cert.signature_algorithm(), cert.tbs_certificate_tlv(),
-                        cert.signature_value(), working_spki, signature_policy,
+                        cert.signature_value(), working_spki_, signature_policy,
                         errors)) {
     errors->AddError(kVerifySignedDataFailed);
   }
@@ -249,15 +794,15 @@
 
   // Verify the certificate's issuer name matches the issuing certificate's
   // subject name. (RFC 5280 section 6.1.3 step a.4)
-  if (cert.normalized_issuer() != working_normalized_issuer_name)
+  if (cert.normalized_issuer() != working_normalized_issuer_name_)
     errors->AddError(kSubjectDoesNotMatchIssuer);
 
   // Name constraints (RFC 5280 section 6.1.3 step b & c)
   // If certificate i is self-issued and it is not the final certificate in the
   // path, skip this step for certificate i.
-  if (!name_constraints_list.empty() &&
+  if (!name_constraints_list_.empty() &&
       (!IsSelfIssued(cert) || is_target_cert)) {
-    for (const NameConstraints* nc : name_constraints_list) {
+    for (const NameConstraints* nc : name_constraints_list_) {
       if (!nc->IsPermittedCert(cert.normalized_subject(),
                                cert.subject_alt_names())) {
         errors->AddError(kNotPermittedByNameConstraints);
@@ -265,31 +810,30 @@
     }
   }
 
-  // TODO(eroman): Steps d-f are omitted, as policy constraints are not yet
-  // implemented.
+  // RFC 5280 section 6.1.3 step d - f.
+  VerifyPolicies(cert, is_target_cert, errors);
+
+  // The key purpose is checked not just for the end-entity certificate, but
+  // also interpreted as a constraint when it appears in intermediates. This
+  // goes beyond what RFC 5280 describes, but is the de-facto standard. See
+  // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
+  VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
 }
 
-// This function corresponds to RFC 5280 section 6.1.4's "Preparation for
-// Certificate i+1" procedure. |cert| is expected to be an intermediate.
-void PrepareForNextCertificate(
-    const ParsedCertificate& cert,
-    size_t* max_path_length_ptr,
-    der::Input* working_spki,
-    der::Input* working_normalized_issuer_name,
-    std::vector<const NameConstraints*>* name_constraints_list,
-    CertErrors* errors) {
-  // TODO(crbug.com/634456): Steps a-b are omitted, as policy mappings are not
-  // yet implemented.
+void PathVerifier::PrepareForNextCertificate(const ParsedCertificate& cert,
+                                             CertErrors* errors) {
+  // RFC 5280 section 6.1.4 step a-b
+  VerifyPolicyMappings(cert, errors);
 
   // From RFC 5280 section 6.1.4 step c:
   //
   //    Assign the certificate subject name to working_normalized_issuer_name.
-  *working_normalized_issuer_name = cert.normalized_subject();
+  working_normalized_issuer_name_ = cert.normalized_subject();
 
   // From RFC 5280 section 6.1.4 step d:
   //
   //    Assign the certificate subjectPublicKey to working_public_key.
-  *working_spki = cert.tbs().spki_tlv;
+  working_spki_ = cert.tbs().spki_tlv;
 
   // Note that steps e and f are omitted as they are handled by
   // the assignment to |working_spki| above. See the definition
@@ -297,10 +841,53 @@
 
   // From RFC 5280 section 6.1.4 step g:
   if (cert.has_name_constraints())
-    name_constraints_list->push_back(&cert.name_constraints());
+    name_constraints_list_.push_back(&cert.name_constraints());
 
-  // TODO(eroman): Steps h-j are omitted as policy
-  // constraints/mappings/inhibitAnyPolicy are not yet implemented.
+  //     (h)  If certificate i is not self-issued:
+  if (!IsSelfIssued(cert)) {
+    //         (1)  If explicit_policy is not 0, decrement explicit_policy by
+    //              1.
+    if (explicit_policy_ > 0)
+      explicit_policy_ -= 1;
+
+    //         (2)  If policy_mapping is not 0, decrement policy_mapping by 1.
+    if (policy_mapping_ > 0)
+      policy_mapping_ -= 1;
+
+    //         (3)  If inhibit_anyPolicy is not 0, decrement inhibit_anyPolicy
+    //              by 1.
+    if (inhibit_any_policy_ > 0)
+      inhibit_any_policy_ -= 1;
+  }
+
+  //      (i)  If a policy constraints extension is included in the
+  //           certificate, modify the explicit_policy and policy_mapping
+  //           state variables as follows:
+  if (cert.has_policy_constraints()) {
+    //         (1)  If requireExplicitPolicy is present and is less than
+    //              explicit_policy, set explicit_policy to the value of
+    //              requireExplicitPolicy.
+    if (cert.policy_constraints().has_require_explicit_policy &&
+        cert.policy_constraints().require_explicit_policy < explicit_policy_) {
+      explicit_policy_ = cert.policy_constraints().require_explicit_policy;
+    }
+
+    //         (2)  If inhibitPolicyMapping is present and is less than
+    //              policy_mapping, set policy_mapping to the value of
+    //              inhibitPolicyMapping.
+    if (cert.policy_constraints().has_inhibit_policy_mapping &&
+        cert.policy_constraints().inhibit_policy_mapping < policy_mapping_) {
+      policy_mapping_ = cert.policy_constraints().inhibit_policy_mapping;
+    }
+  }
+
+  //      (j)  If the inhibitAnyPolicy extension is included in the
+  //           certificate and is less than inhibit_anyPolicy, set
+  //           inhibit_anyPolicy to the value of inhibitAnyPolicy.
+  if (cert.has_inhibit_any_policy() &&
+      cert.inhibit_any_policy() < inhibit_any_policy_) {
+    inhibit_any_policy_ = cert.inhibit_any_policy();
+  }
 
   // From RFC 5280 section 6.1.4 step k:
   //
@@ -327,10 +914,10 @@
   //    max_path_length is greater than zero and decrement
   //    max_path_length by 1.
   if (!IsSelfIssued(cert)) {
-    if (*max_path_length_ptr == 0) {
+    if (max_path_length_ == 0) {
       errors->AddError(kMaxPathLengthViolated);
     } else {
-      --(*max_path_length_ptr);
+      --max_path_length_;
     }
   }
 
@@ -340,8 +927,8 @@
   //    less than max_path_length, set max_path_length to the value
   //    of pathLenConstraint.
   if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len &&
-      cert.basic_constraints().path_len < *max_path_length_ptr) {
-    *max_path_length_ptr = cert.basic_constraints().path_len;
+      cert.basic_constraints().path_len < max_path_length_) {
+    max_path_length_ = cert.basic_constraints().path_len;
   }
 
   // From RFC 5280 section 6.1.4 step n:
@@ -408,13 +995,22 @@
   }
 }
 
-// This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
-// It does processing for the final certificate (the target cert).
-void WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
-  // TODO(crbug.com/634452): Steps a-b are omitted as policy constraints are not
-  // yet implemented.
+void PathVerifier::WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
+  // From RFC 5280 section 6.1.5:
+  //      (a)  If explicit_policy is not 0, decrement explicit_policy by 1.
+  if (explicit_policy_ > 0)
+    explicit_policy_ -= 1;
 
-  // Note step c-e are omitted the verification function does
+  //      (b)  If a policy constraints extension is included in the
+  //           certificate and requireExplicitPolicy is present and has a
+  //           value of 0, set the explicit_policy state variable to 0.
+  if (cert.has_policy_constraints() &&
+      cert.policy_constraints().has_require_explicit_policy &&
+      cert.policy_constraints().require_explicit_policy == 0) {
+    explicit_policy_ = 0;
+  }
+
+  // Note step c-e are omitted as the verification function does
   // not output the working public key.
 
   // From RFC 5280 section 6.1.5 step f:
@@ -428,24 +1024,24 @@
   // directly match the procedures in RFC 5280's section 6.1.
   VerifyNoUnconsumedCriticalExtensions(cert, errors);
 
-  // TODO(eroman): Step g is omitted, as policy constraints are not yet
-  // implemented.
+  // RFC 5280 section 6.1.5 step g is skipped, as the intersection of valid
+  // policies was computed during previous steps.
+  //
+  //    If either (1) the value of explicit_policy variable is greater than
+  //    zero or (2) the valid_policy_tree is not NULL, then path processing
+  //   has succeeded.
+  if (!(explicit_policy_ > 0 || !valid_policy_tree_.IsNull())) {
+    errors->AddError(kNoValidPolicy);
+  }
 
   // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
   // however is implied by RFC 5280 section 4.2.1.9.
   VerifyTargetCertHasConsistentCaBits(cert, errors);
 }
 
-// Enforces trust anchor constraints compatibile with RFC 5937.
-//
-// Note that the anchor constraints are encoded via the attached certificate
-// itself.
-void ApplyTrustAnchorConstraints(
-    const ParsedCertificate& cert,
-    KeyPurpose required_key_purpose,
-    size_t* max_path_length_ptr,
-    std::vector<const NameConstraints*>* name_constraints_list,
-    CertErrors* errors) {
+void PathVerifier::ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
+                                               KeyPurpose required_key_purpose,
+                                               CertErrors* errors) {
   // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling
   // done for intermediates (described in Web PKI's Baseline Requirements).
   VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
@@ -454,7 +1050,7 @@
 
   // Initialize name constraints initial-permitted/excluded-subtrees.
   if (cert.has_name_constraints())
-    name_constraints_list->push_back(&cert.name_constraints());
+    name_constraints_list_.push_back(&cert.name_constraints());
 
   // TODO(eroman): Initialize user-initial-policy-set based on anchor
   // constraints.
@@ -477,7 +1073,7 @@
   // NOTE: RFC 5937 does not say to enforce the CA=true part of basic
   // constraints.
   if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len)
-    *max_path_length_ptr = cert.basic_constraints().path_len;
+    max_path_length_ = cert.basic_constraints().path_len;
 
   // From RFC 5937 section 2:
   //
@@ -487,22 +1083,15 @@
   VerifyNoUnconsumedCriticalExtensions(cert, errors);
 }
 
-// Initializes the path validation algorithm given anchor constraints. This
-// follows the description in RFC 5937
-void ProcessRootCertificate(
-    const ParsedCertificate& cert,
-    const CertificateTrust& trust,
-    KeyPurpose required_key_purpose,
-    size_t* max_path_length_ptr,
-    std::vector<const NameConstraints*>* name_constraints_list,
-    der::Input* working_spki,
-    der::Input* working_normalized_issuer_name,
-    CertErrors* errors) {
+void PathVerifier::ProcessRootCertificate(const ParsedCertificate& cert,
+                                          const CertificateTrust& trust,
+                                          KeyPurpose required_key_purpose,
+                                          CertErrors* errors) {
   // Use the certificate's SPKI and subject when verifying the next certificate.
   // Note this is initialized even in the case of untrusted roots (they already
   // emit an error for the distrust).
-  *working_spki = cert.tbs().spki_tlv;
-  *working_normalized_issuer_name = cert.normalized_subject();
+  working_spki_ = cert.tbs().spki_tlv;
+  working_normalized_issuer_name_ = cert.normalized_subject();
 
   switch (trust.type) {
     case CertificateTrustType::UNSPECIFIED:
@@ -517,24 +1106,26 @@
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       // If the trust anchor has constraints, enforce them.
       if (trust.type == CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS) {
-        ApplyTrustAnchorConstraints(cert, required_key_purpose,
-                                    max_path_length_ptr, name_constraints_list,
-                                    errors);
+        ApplyTrustAnchorConstraints(cert, required_key_purpose, errors);
       }
       break;
   }
 }
 
-}  // namespace
-
-// This implementation is structured to mimic the description of certificate
-// path verification given by RFC 5280 section 6.1.
-void VerifyCertificateChain(const ParsedCertificateList& certs,
-                            const CertificateTrust& last_cert_trust,
-                            const SignaturePolicy* signature_policy,
-                            const der::GeneralizedTime& time,
-                            KeyPurpose required_key_purpose,
-                            CertPathErrors* errors) {
+void PathVerifier::Run(
+    const ParsedCertificateList& certs,
+    const CertificateTrust& last_cert_trust,
+    const SignaturePolicy* signature_policy,
+    const der::GeneralizedTime& time,
+    KeyPurpose required_key_purpose,
+    InitialExplicitPolicy initial_explicit_policy,
+    const std::set<der::Input>& user_initial_policy_set,
+    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+    InitialAnyPolicyInhibit initial_any_policy_inhibit,
+    std::set<der::Input>* user_constrained_policy_set,
+    CertPathErrors* errors) {
+  // This implementation is structured to mimic the description of certificate
+  // path verification given by RFC 5280 section 6.1.
   DCHECK(signature_policy);
   DCHECK(errors);
 
@@ -551,50 +1142,48 @@
     return;
   }
 
-  // Will contain a NameConstraints for each previous cert in the chain which
-  // had nameConstraints. This corresponds to the permitted_subtrees and
-  // excluded_subtrees state variables from RFC 5280.
-  std::vector<const NameConstraints*> name_constraints_list;
+  // RFC 5280's "n" variable is the length of the path, which does not count
+  // the trust anchor. (Although in practice it doesn't really change behaviors
+  // if n is used in place of n+1).
+  const size_t n = certs.size() - 1;
 
-  // |working_spki| is an amalgamation of 3 separate variables from RFC 5280:
-  //    * working_public_key
-  //    * working_public_key_algorithm
-  //    * working_public_key_parameters
-  //
-  // They are combined for simplicity since the signature verification takes an
-  // SPKI, and the parameter inheritence is not applicable for the supported
-  // key types.
-  //
-  // An approximate explanation of |working_spki| is this description from RFC
-  // 5280 section 6.1.2:
-  //
-  //    working_public_key:  the public key used to verify the
-  //    signature of a certificate.
-  der::Input working_spki;
+  valid_policy_tree_.Init(user_initial_policy_set);
 
-  // |working_normalized_issuer_name| is the normalized value of the
-  // working_issuer_name variable in RFC 5280 section 6.1.2:
+  // RFC 5280 section section 6.1.2:
   //
-  //    working_issuer_name:  the issuer distinguished name expected
-  //    in the next certificate in the chain.
-  der::Input working_normalized_issuer_name;
+  // If initial-explicit-policy is set, then the initial value
+  // [of explicit_policy] is 0, otherwise the initial value is n+1.
+  explicit_policy_ =
+      initial_explicit_policy == InitialExplicitPolicy::kTrue ? 0 : n + 1;
 
-  // |max_path_length| corresponds with the same named variable in RFC 5280
-  // section 6.1.2:
+  // RFC 5280 section section 6.1.2:
   //
-  //    max_path_length:  this integer is initialized to n, is
-  //    decremented for each non-self-issued certificate in the path,
-  //    and may be reduced to the value in the path length constraint
-  //    field within the basic constraints extension of a CA
-  //    certificate.
-  size_t max_path_length = certs.size();
+  // If initial-any-policy-inhibit is set, then the initial value
+  // [of inhibit_anyPolicy] is 0, otherwise the initial value is n+1.
+  inhibit_any_policy_ =
+      initial_any_policy_inhibit == InitialAnyPolicyInhibit::kTrue ? 0 : n + 1;
+
+  // RFC 5280 section section 6.1.2:
+  //
+  // If initial-policy-mapping-inhibit is set, then the initial value
+  // [of policy_mapping] is 0, otherwise the initial value is n+1.
+  policy_mapping_ =
+      initial_policy_mapping_inhibit == InitialPolicyMappingInhibit::kTrue
+          ? 0
+          : n + 1;
+
+  // RFC 5280 section section 6.1.2:
+  //
+  // max_path_length:  this integer is initialized to n, ...
+  max_path_length_ = n;
 
   // Iterate over all the certificates in the reverse direction: starting from
   // the root certificate and progressing towards the target certificate.
   //
-  //   * i=0               :  Root certificate (i.e. trust anchor)
-  //   * i=1               :  Certificated signed by the root certificate
-  //   * i=certs.size()-1  :  Target certificate.
+  //   * i=0  :  Root certificate (i.e. trust anchor)
+  //   * i=1  :  Certificate issued by root
+  //   * i=x  :  Certificate i=x is issued by certificate i=x-1
+  //   * i=n  :  Target certificate.
   for (size_t i = 0; i < certs.size(); ++i) {
     const size_t index_into_certs = certs.size() - i - 1;
 
@@ -612,8 +1201,6 @@
 
     if (is_root_cert) {
       ProcessRootCertificate(cert, last_cert_trust, required_key_purpose,
-                             &max_path_length, &name_constraints_list,
-                             &working_spki, &working_normalized_issuer_name,
                              cert_errors);
 
       // Don't do any other checks for root certificates.
@@ -626,28 +1213,45 @@
     //     - Then run "Wrap up"
     //     - Otherwise run "Prepare for Next cert"
     BasicCertificateProcessing(cert, is_target_cert, signature_policy, time,
-                               working_spki, working_normalized_issuer_name,
-                               name_constraints_list, cert_errors);
-
-    // The key purpose is checked not just for the end-entity certificate, but
-    // also interpreted as a constraint when it appears in intermediates. This
-    // goes beyond what RFC 5280 describes, but is the de-facto standard. See
-    // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
-    VerifyExtendedKeyUsage(cert, required_key_purpose, cert_errors);
-
+                               required_key_purpose, cert_errors);
     if (!is_target_cert) {
-      PrepareForNextCertificate(cert, &max_path_length, &working_spki,
-                                &working_normalized_issuer_name,
-                                &name_constraints_list, cert_errors);
+      PrepareForNextCertificate(cert, cert_errors);
     } else {
       WrapUp(cert, cert_errors);
     }
   }
 
+  if (user_constrained_policy_set) {
+    // valid_policy_tree_ already contains the intersection of valid policies
+    // with user_initial_policy_set.
+    valid_policy_tree_.GetValidRootPolicySet(user_constrained_policy_set);
+  }
+
   // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
   //
   //    A certificate MUST NOT appear more than once in a prospective
   //    certification path.
 }
 
+}  // namespace
+
+void VerifyCertificateChain(
+    const ParsedCertificateList& certs,
+    const CertificateTrust& last_cert_trust,
+    const SignaturePolicy* signature_policy,
+    const der::GeneralizedTime& time,
+    KeyPurpose required_key_purpose,
+    InitialExplicitPolicy initial_explicit_policy,
+    const std::set<der::Input>& user_initial_policy_set,
+    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+    InitialAnyPolicyInhibit initial_any_policy_inhibit,
+    std::set<der::Input>* user_constrained_policy_set,
+    CertPathErrors* errors) {
+  PathVerifier verifier;
+  verifier.Run(certs, last_cert_trust, signature_policy, time,
+               required_key_purpose, initial_explicit_policy,
+               user_initial_policy_set, initial_policy_mapping_inhibit,
+               initial_any_policy_inhibit, user_constrained_policy_set, errors);
+}
+
 }  // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index 8d1f044..a8b2917 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -5,7 +5,7 @@
 #ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
 #define NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
 
-#include <vector>
+#include <set>
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
@@ -30,72 +30,160 @@
   CLIENT_AUTH,
 };
 
+enum class InitialExplicitPolicy {
+  kFalse,
+  kTrue,
+};
+
+enum class InitialPolicyMappingInhibit {
+  kFalse,
+  kTrue,
+};
+
+enum class InitialAnyPolicyInhibit {
+  kFalse,
+  kTrue,
+};
+
 // VerifyCertificateChain() verifies an ordered certificate path in accordance
-// with RFC 5280 (with some modifications [1]).
+// with RFC 5280's "Certification Path Validation" algorithm (section 6).
 //
-// [1] Deviations from RFC 5280:
+// -----------------------------------------
+// Deviations from RFC 5280
+// -----------------------------------------
 //
-//   * If Extended Key Usage appears on intermediates it is treated as a
-//     restriction on subordinate certificates.
+//   * If Extended Key Usage appears on intermediates, it is treated as
+//     a restriction on subordinate certificates.
 //
-// The caller is responsible for additionally checking:
+// -----------------------------------------
+// Additional responsibilities of the caller
+// -----------------------------------------
+//
+// After successful path verification, the caller is responsible for
+// subsequently checking:
 //
 //  * The end-entity's KeyUsage before using its SPKI.
-//  * The end-entity's name/subjectAltName (note that name constraints from
-//    intermediates will have already been applied, so just need to check
-//    the end-entity for a match).
-//  * Policies
-//
-// WARNING: This implementation is in progress, and is currently incomplete.
-// Consult an OWNER before using it.
-//
-// TODO(eroman): Take a CertPath instead of ParsedCertificateList +
-//               TrustAnchor.
+//  * The end-entity's name/subjectAltName. Name constraints from intermediates
+//    will have already been applied, so it is sufficient to check the
+//    end-entity for a match.
 //
 // ---------
 // Inputs
 // ---------
 //
-//   cert_chain:
-//     A non-empty chain of N DER-encoded certificates, listed in the
-//     "forward" direction. The first certificate is the target certificate to
-//     verify, and the last certificate has trustedness given by
-//     |last_cert_trust|.
+//   certs:
+//     A non-empty chain of DER-encoded certificates, listed in the
+//     "forward" direction. The first certificate is the target
+//     certificate to verify, and the last certificate has trustedness
+//     given by |last_cert_trust| (generally a trust anchor).
 //
-//      * cert_chain[0] is the target certificate to verify.
-//      * cert_chain[i+1] holds the certificate that issued cert_chain[i].
-//      * cert_chain[N-1] the root certificate
+//      * certs[0] is the target certificate to verify.
+//      * certs[i+1] holds the certificate that issued cert_chain[i].
+//      * certs[N-1] the root certificate
+//
+//     Note that THIS IS NOT identical in meaning to the same named
+//     "certs" input defined in RFC 5280 section 6.1.1.a. The differences
+//     are:
+//
+//      * The order of certificates is reversed
+//      * In RFC 5280 "certs" DOES NOT include the trust anchor
 //
 //   last_cert_trust:
-//     Trustedness of certs.back(). The trustedness of certs.back() MUST BE
-//     decided by the caller -- this function takes it purely as an input.
-//     Moreover, the CertificateTrust can be used to specify trust anchor
-//     constraints [1]
+//     Trustedness of |certs.back()|. The trustedness of |certs.back()|
+//     MUST BE decided by the caller -- this function takes it purely as
+//     an input. Moreover, the CertificateTrust can be used to specify
+//     trust anchor constraints.
+//
+//     This combined with |certs.back()| (the root certificate) fills a
+//     similar role to "trust anchor information" defined in RFC 5280
+//     section 6.1.1.d.
 //
 //   signature_policy:
 //     The policy to use when verifying signatures (what hash algorithms are
 //     allowed, what length keys, what named curves, etc).
 //
 //   time:
-//     The UTC time to use for expiration checks.
+//     The UTC time to use for expiration checks. This is equivalent to
+//     the input from RFC 5280 section 6.1.1:
 //
-//   key_purpose:
+//       (b)  the current date/time.
+//
+//   required_key_purpose:
 //     The key purpose that the target certificate needs to be valid for.
 //
+//   user_initial_policy_set:
+//     This is equivalent to the same named input in RFC 5280 section
+//     6.1.1:
+//
+//       (c)  user-initial-policy-set: A set of certificate policy
+//            identifiers naming the policies that are acceptable to the
+//            certificate user. The user-initial-policy-set contains the
+//            special value any-policy if the user is not concerned about
+//            certificate policy.
+//
+//   initial_policy_mapping_inhibit:
+//     This is equivalent to the same named input in RFC 5280 section
+//     6.1.1:
+//
+//       (e)  initial-policy-mapping-inhibit, which indicates if policy
+//            mapping is allowed in the certification path.
+//
+//   initial_explicit_policy:
+//     This is equivalent to the same named input in RFC 5280 section
+//     6.1.1:
+//
+//       (f)  initial-explicit-policy, which indicates if the path must be
+//            valid for at least one of the certificate policies in the
+//            user-initial-policy-set.
+//
+//   initial_any_policy_inhibit:
+//     This is equivalent to the same named input in RFC 5280 section
+//     6.1.1:
+//
+//       (g)  initial-any-policy-inhibit, which indicates whether the
+//            anyPolicy OID should be processed if it is included in a
+//            certificate.
+//
 // ---------
 // Outputs
 // ---------
+//
+//   user_constrained_policy_set:
+//     Can be null. If non-null, |user_constrained_policy_set| will be filled
+//     with the matching policies (intersected with user_initial_policy_set).
+//     This is equivalent to the same named output in X.509 section 10.2.
+//     Note that it is OK for this to point to input user_initial_policy_set.
+//
 //   errors:
 //     Must be non-null. The set of errors/warnings encountered while
 //     validating the path are appended to this structure. If verification
 //     failed, then there is guaranteed to be at least 1 high severity error
 //     written to |errors|.
 //
-// [1] Conceptually VerifyCertificateChain() sets RFC 5937's
-// "enforceTrustAnchorConstraints" to true. And one specifies whether to
-// interpret a root certificate as having trust anchor constraints through the
-// |last_cert_trust| parameter. The constraints are just a subset of the
-// extensions present in the certificate:
+// -------------------------
+// Trust Anchor constraints
+// -------------------------
+//
+// Conceptually, VerifyCertificateChain() sets RFC 5937's
+// "enforceTrustAnchorConstraints" to true.
+//
+// One specifies trust anchor constraints using the |last_cert_trust|
+// parameter in conjunction with extensions appearing in |certs.back()|.
+//
+// The trust anchor |certs.back()| is always passed as a certificate to
+// this function, however the manner in which that certificate is
+// interpreted depends on |last_cert_trust|:
+//
+// TRUSTED_ANCHOR:
+//
+// No properties from the root certificate, other than its Subject and
+// SPKI, are checked during verification. This is the usual
+// interpretation for a "trust anchor".
+//
+// TRUSTED_ANCHOR_WITH_CONSTRAINTS:
+//
+// Only a subset of extensions and properties from the certificate are checked,
+// as described by RFC 5937.
 //
 //  * Signature:             No
 //  * Validity (expiration): No
@@ -104,17 +192,24 @@
 //  * Basic constraints:     Yes, but only the pathlen (CA=false is accepted)
 //  * Name constraints:      Yes
 //  * Certificate policies:  Not currently, TODO(crbug.com/634453)
+//  * Policy Mappings:       No
 //  * inhibitAnyPolicy:      Not currently, TODO(crbug.com/634453)
 //  * PolicyConstraints:     Not currently, TODO(crbug.com/634452)
 //
 // The presence of any other unrecognized extension marked as critical fails
 // validation.
-NET_EXPORT void VerifyCertificateChain(const ParsedCertificateList& certs,
-                                       const CertificateTrust& last_cert_trust,
-                                       const SignaturePolicy* signature_policy,
-                                       const der::GeneralizedTime& time,
-                                       KeyPurpose required_key_purpose,
-                                       CertPathErrors* errors);
+NET_EXPORT void VerifyCertificateChain(
+    const ParsedCertificateList& certs,
+    const CertificateTrust& last_cert_trust,
+    const SignaturePolicy* signature_policy,
+    const der::GeneralizedTime& time,
+    KeyPurpose required_key_purpose,
+    InitialExplicitPolicy initial_explicit_policy,
+    const std::set<der::Input>& user_initial_policy_set,
+    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
+    InitialAnyPolicyInhibit initial_any_policy_inhibit,
+    std::set<der::Input>* user_constrained_policy_set,
+    CertPathErrors* errors);
 
 // TODO(crbug.com/634443): Move exported errors to a central location?
 extern CertErrorId kValidityFailedNotAfter;
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index 8138dae9..f86e1e9e 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -67,12 +67,19 @@
 
     SimpleSignaturePolicy signature_policy(1024);
 
+    std::set<der::Input> user_constrained_policy_set;
+
     CertPathErrors path_errors;
-    VerifyCertificateChain(input_chain, CertificateTrust::ForTrustAnchor(),
-                           &signature_policy, info.time, KeyPurpose::ANY_EKU,
-                           &path_errors);
+    VerifyCertificateChain(
+        input_chain, CertificateTrust::ForTrustAnchor(), &signature_policy,
+        info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy,
+        info.initial_policy_set, info.initial_policy_mapping_inhibit,
+        info.initial_inhibit_any_policy, &user_constrained_policy_set,
+        &path_errors);
     bool did_succeed = !path_errors.ContainsHighSeverityErrors();
 
+    EXPECT_EQ(info.user_constrained_policy_set, user_constrained_policy_set);
+
     //  TODO(crbug.com/634443): Test errors on failure?
     if (info.should_validate != did_succeed) {
       ASSERT_EQ(info.should_validate, did_succeed)
@@ -222,6 +229,21 @@
                               PkitsTest07KeyUsage,
                               VerifyCertificateChainPkitsTestDelegate);
 INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
+                              PkitsTest08CertificatePolicies,
+                              VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
+                              PkitsTest09RequireExplicitPolicy,
+                              VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
+                              PkitsTest10PolicyMappings,
+                              VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
+                              PkitsTest11InhibitPolicyMapping,
+                              VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
+                              PkitsTest12InhibitAnyPolicy,
+                              VerifyCertificateChainPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
                               PkitsTest13NameConstraints,
                               VerifyCertificateChainPkitsTestDelegate);
 INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
@@ -232,8 +254,4 @@
 // PkitsTest05VerifyingPathswithSelfIssuedCertificates,
 // PkitsTest14DistributionPoints, PkitsTest15DeltaCRLs
 
-// TODO(mattm): Certificate Policies support: PkitsTest08CertificatePolicies,
-// PkitsTest09RequireExplicitPolicy PkitsTest10PolicyMappings,
-// PkitsTest11InhibitPolicyMapping, PkitsTest12InhibitAnyPolicy
-
 }  // namespace net
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc
index 13c689f..82851d0 100644
--- a/net/cert/internal/verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -19,8 +19,13 @@
     SimpleSignaturePolicy signature_policy(1024);
 
     CertPathErrors errors;
-    VerifyCertificateChain(test.chain, test.last_cert_trust, &signature_policy,
-                           test.time, test.key_purpose, &errors);
+    // TODO(eroman): Check user_constrained_policy_set.
+    VerifyCertificateChain(
+        test.chain, test.last_cert_trust, &signature_policy, test.time,
+        test.key_purpose, test.initial_explicit_policy,
+        test.user_initial_policy_set, test.initial_policy_mapping_inhibit,
+        test.initial_any_policy_inhibit,
+        nullptr /*user_constrained_policy_set*/, &errors);
     EXPECT_EQ(test.expected_errors, errors.ToDebugString(test.chain))
         << "Test file: " << test_file_path;
   }
diff --git a/net/third_party/nist-pkits/generate_tests.py b/net/third_party/nist-pkits/generate_tests.py
index 04afc6f8..8083d62a 100644
--- a/net/third_party/nist-pkits/generate_tests.py
+++ b/net/third_party/nist-pkits/generate_tests.py
@@ -688,6 +688,17 @@
              user_constrained_policy_set=[]),
   ],
 
+  '4.10.7': [ # Invalid Mapping From anyPolicy Test7 
+    # Procedure: Validate Invalid Mapping From anyPolicy Test7 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.100 using
+    # the default settings.
+    #
+    # Expected Result: The path should not validate successfully since the
+    # intermediate certificate includes a policy mapping extension in which
+    # anyPolicy appears as an issuerDomainPolicy. 
+    TestInfo(False, user_constrained_policy_set=[]),
+  ],
+
   '4.10.8': [ # Invalid Mapping To anyPolicy Test8
     # Procedure: Validate Invalid Mapping To anyPolicy Test8 EE using the
     # default settings or open and verify Signed Test Message 6.2.2.101 using
@@ -696,8 +707,6 @@
     # Expected Result: The path should not validate successfully since the
     # intermediate certificate includes a policy mapping extension in which
     # anyPolicy appears as an subjectDomainPolicy.
-    #
-    # TODO(eroman): What should user_constrained_policy_set be?
     TestInfo(False, user_constrained_policy_set=[]),
   ],
 
diff --git a/net/third_party/nist-pkits/pkits_testcases-inl.h b/net/third_party/nist-pkits/pkits_testcases-inl.h
index d4b709d..8ddf7c6 100644
--- a/net/third_party/nist-pkits/pkits_testcases-inl.h
+++ b/net/third_party/nist-pkits/pkits_testcases-inl.h
@@ -2113,6 +2113,7 @@
                               "MappingFromanyPolicyCACRL"};
   PkitsTestInfo info;
   info.should_validate = false;
+  info.SetUserConstrainedPolicySet("");
 
   this->RunTest(certs, crls, info);
 }
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index d5d38a5..3c54ea5 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -48,10 +48,16 @@
 const char kSetIntervalTag[] = "set-interval";
 const char kExpectedSequenceIdTag[] = "expected-sequence-id";
 
-const int64_t kDefaultHeartbeatIntervalMs = 5 * 60 * 1000;  // 5 minutes.
-const int64_t kResendDelayMs = 10 * 1000;                   // 10 seconds.
-const int64_t kResendDelayOnHostNotFoundMs = 10 * 1000;     // 10 seconds.
+constexpr base::TimeDelta kDefaultHeartbeatInterval =
+    base::TimeDelta::FromMinutes(5);
+constexpr base::TimeDelta kHeartbeatResponseTimeout =
+    base::TimeDelta::FromSeconds(30);
+constexpr base::TimeDelta kResendDelay = base::TimeDelta::FromSeconds(10);
+constexpr base::TimeDelta kResendDelayOnHostNotFound =
+    base::TimeDelta::FromSeconds(10);
+
 const int kMaxResendOnHostNotFoundCount = 12;  // 2 minutes (12 x 10 seconds).
+const int kMaxHeartbeatTimeouts = 2;
 
 }  // namespace
 
@@ -68,12 +74,7 @@
       signal_strategy_(signal_strategy),
       host_key_pair_(host_key_pair),
       directory_bot_jid_(directory_bot_jid),
-      interval_ms_(kDefaultHeartbeatIntervalMs),
-      sequence_id_(0),
-      sequence_id_was_set_(false),
-      sequence_id_recent_set_num_(0),
-      heartbeat_succeeded_(false),
-      failed_startup_heartbeat_count_(0) {
+      interval_(kDefaultHeartbeatInterval) {
   DCHECK(signal_strategy_);
   DCHECK(host_key_pair_.get());
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -92,10 +93,9 @@
 void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (state == SignalStrategy::CONNECTED) {
-    iq_sender_.reset(new IqSender(signal_strategy_));
+    iq_sender_ = base::MakeUnique<IqSender>(signal_strategy_);
     SendStanza();
-    timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_),
-                 this, &HeartbeatSender::SendStanza);
+    timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza);
   } else if (state == SignalStrategy::DISCONNECTED) {
     request_.reset();
     iq_sender_.reset();
@@ -169,18 +169,32 @@
 
   request_ = iq_sender_->SendIq(
       buzz::STR_SET, directory_bot_jid_, CreateHeartbeatMessage(),
-      base::Bind(&HeartbeatSender::ProcessResponse,
-                 base::Unretained(this),
+      base::Bind(&HeartbeatSender::ProcessResponse, base::Unretained(this),
                  !host_offline_reason_.empty()));
+  request_->SetTimeout(kHeartbeatResponseTimeout);
   ++sequence_id_;
 }
 
-void HeartbeatSender::ProcessResponse(
-    bool is_offline_heartbeat_response,
-    IqRequest* request,
-    const XmlElement* response) {
+void HeartbeatSender::ProcessResponse(bool is_offline_heartbeat_response,
+                                      IqRequest* request,
+                                      const XmlElement* response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  if (!response) {
+    timed_out_heartbeats_count_++;
+    if (timed_out_heartbeats_count_ >= kMaxHeartbeatTimeouts) {
+      LOG(ERROR) << "Heartbeat timed out. Reconnecting XMPP.";
+      timed_out_heartbeats_count_ = 0;
+      // SignalingConnector will reconnect SignalStrategy.
+      signal_strategy_->Disconnect();
+    } else {
+      LOG(ERROR) << "Heartbeat timed out.";
+    }
+    return;
+  } else {
+    timed_out_heartbeats_count_ = 0;
+  }
+
   std::string type = response->Attr(buzz::QN_TYPE);
   if (type == buzz::STR_ERROR) {
     const XmlElement* error_element =
@@ -195,11 +209,8 @@
         // exit.
         failed_startup_heartbeat_count_++;
         if (!heartbeat_succeeded_ && (failed_startup_heartbeat_count_ <=
-                kMaxResendOnHostNotFoundCount)) {
-          timer_resend_.Start(FROM_HERE,
-                              base::TimeDelta::FromMilliseconds(
-                                  kResendDelayOnHostNotFoundMs),
-                              this,
+                                      kMaxResendOnHostNotFoundCount)) {
+          timer_resend_.Start(FROM_HERE, kResendDelayOnHostNotFound, this,
                               &HeartbeatSender::ResendStanza);
           return;
         }
@@ -224,12 +235,13 @@
                                          kSetIntervalTag));
     if (set_interval_element) {
       const std::string& interval_str = set_interval_element->BodyText();
-      int interval;
-      if (!base::StringToInt(interval_str, &interval) || interval <= 0) {
+      int interval_seconds;
+      if (!base::StringToInt(interval_str, &interval_seconds) ||
+          interval_seconds <= 0) {
         LOG(ERROR) << "Received invalid set-interval: "
                    << set_interval_element->Str();
       } else {
-        SetInterval(interval * base::Time::kMillisecondsPerSecond);
+        SetInterval(base::TimeDelta::FromSeconds(interval_seconds));
       }
     }
 
@@ -270,15 +282,14 @@
   }
 }
 
-void HeartbeatSender::SetInterval(int interval) {
-  if (interval != interval_ms_) {
-    interval_ms_ = interval;
+void HeartbeatSender::SetInterval(base::TimeDelta interval) {
+  if (interval != interval_) {
+    interval_ = interval;
 
     // Restart the timer with the new interval.
     if (timer_.IsRunning()) {
       timer_.Stop();
-      timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(interval_ms_),
-                   this, &HeartbeatSender::SendStanza);
+      timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza);
     }
   }
 }
@@ -296,11 +307,11 @@
   } else {
     HOST_LOG << "The heartbeat sequence ID has been set more than once: "
               << "the new value is " << sequence_id;
-    double delay = pow(2.0, sequence_id_recent_set_num_) *
-        (1 + base::RandDouble()) * kResendDelayMs;
-    if (delay <= interval_ms_) {
-      timer_resend_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay),
-                          this, &HeartbeatSender::ResendStanza);
+    base::TimeDelta delay = pow(2.0, sequence_id_recent_set_num_) *
+                            (1 + base::RandDouble()) * kResendDelay;
+    if (delay <= interval_) {
+      timer_resend_.Start(FROM_HERE, delay, this,
+                          &HeartbeatSender::ResendStanza);
     }
   }
   sequence_id_was_set_ = true;
diff --git a/remoting/host/heartbeat_sender.h b/remoting/host/heartbeat_sender.h
index da5ef091..3553a86f 100644
--- a/remoting/host/heartbeat_sender.h
+++ b/remoting/host/heartbeat_sender.h
@@ -131,6 +131,7 @@
   FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponseSetInterval);
   FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest,
                            ProcessResponseExpectedSequenceId);
+  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ResponseTimeout);
   friend class HeartbeatSenderTest;
 
   void SendStanza();
@@ -139,7 +140,7 @@
   void ProcessResponse(bool is_offline_heartbeat_response,
                        IqRequest* request,
                        const buzz::XmlElement* response);
-  void SetInterval(int interval);
+  void SetInterval(base::TimeDelta interval);
   void SetSequenceId(int sequence_id);
 
   // Handlers for host-offline-reason completion and timeout.
@@ -158,14 +159,15 @@
   std::string directory_bot_jid_;
   std::unique_ptr<IqSender> iq_sender_;
   std::unique_ptr<IqRequest> request_;
-  int interval_ms_;
+  base::TimeDelta interval_;
   base::RepeatingTimer timer_;
   base::OneShotTimer timer_resend_;
-  int sequence_id_;
-  bool sequence_id_was_set_;
-  int sequence_id_recent_set_num_;
-  bool heartbeat_succeeded_;
-  int failed_startup_heartbeat_count_;
+  int sequence_id_ = 0;
+  bool sequence_id_was_set_ = false;
+  int sequence_id_recent_set_num_ = 0;
+  bool heartbeat_succeeded_ = false;
+  int failed_startup_heartbeat_count_ = 0;
+  int timed_out_heartbeats_count_ = 0;
 
   // Fields to send and indicate completion of sending host-offline-reason.
   std::string host_offline_reason_;
diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc
index 18f9126..55de05f 100644
--- a/remoting/host/heartbeat_sender_unittest.cc
+++ b/remoting/host/heartbeat_sender_unittest.cc
@@ -41,8 +41,9 @@
 const char kTestJid[] = "User@gmail.com/chromotingABC123";
 const char kTestJidNormalized[] = "user@gmail.com/chromotingABC123";
 const char kStanzaId[] = "123";
-const int kTestInterval = 123;
-const base::TimeDelta kTestTimeout = base::TimeDelta::FromSeconds(123);
+constexpr base::TimeDelta kTestInterval = base::TimeDelta::FromSeconds(123);
+constexpr base::TimeDelta kOfflineReasonTimeout =
+    base::TimeDelta::FromSeconds(123);
 
 }  // namespace
 
@@ -86,9 +87,8 @@
                                const char* expected_sequence_id,
                                const char* expected_host_offline_reason);
 
-  void ProcessResponseWithInterval(
-    bool is_offline_heartbeat_response,
-    int interval);
+  void ProcessResponseWithInterval(bool is_offline_heartbeat_response,
+                                   base::TimeDelta interval);
 
   base::MessageLoop message_loop_;
   MockSignalStrategy signal_strategy_;
@@ -206,7 +206,7 @@
 
 void HeartbeatSenderTest::ProcessResponseWithInterval(
     bool is_offline_heartbeat_response,
-    int interval) {
+    base::TimeDelta interval) {
   std::unique_ptr<XmlElement> response(new XmlElement(buzz::QN_IQ));
   response->AddAttr(QName(std::string(), "type"), "result");
 
@@ -218,7 +218,7 @@
       QName(kChromotingXmlNamespace, "set-interval"));
   result->AddElement(set_interval);
 
-  set_interval->AddText(base::IntToString(interval));
+  set_interval->AddText(base::IntToString(interval.InSeconds()));
 
   heartbeat_sender_->ProcessResponse(
       is_offline_heartbeat_response, nullptr, response.get());
@@ -230,7 +230,7 @@
 
   ProcessResponseWithInterval(false, kTestInterval);
 
-  EXPECT_EQ(kTestInterval * 1000, heartbeat_sender_->interval_ms_);
+  EXPECT_EQ(kTestInterval, heartbeat_sender_->interval_);
 }
 
 // Make sure SetHostOfflineReason sends a correct stanza.
@@ -247,7 +247,7 @@
       .WillRepeatedly(Return(SignalStrategy::CONNECTED));
   EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
 
-  heartbeat_sender_->SetHostOfflineReason("test_error", kTestTimeout,
+  heartbeat_sender_->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
                                           mock_ack_callback.Get());
   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
   base::RunLoop().RunUntilIdle();
@@ -277,7 +277,7 @@
   // Callback should not run, until response to offline-reason.
   EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
 
-  heartbeat_sender_->SetHostOfflineReason("test_error", kTestTimeout,
+  heartbeat_sender_->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
                                           mock_ack_callback.Get());
   heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
   base::RunLoop().RunUntilIdle();
@@ -372,4 +372,34 @@
   EXPECT_EQ(expected_signature, signature->BodyText());
 }
 
+TEST_F(HeartbeatSenderTest, ResponseTimeout) {
+  XmlElement* sent_iq = nullptr;
+  EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
+  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
+      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
+  EXPECT_CALL(signal_strategy_, GetState())
+      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
+
+  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
+  base::RunLoop().RunUntilIdle();
+
+  std::unique_ptr<XmlElement> stanza(sent_iq);
+  ASSERT_TRUE(stanza);
+
+  XmlElement* sent_iq2 = nullptr;
+  EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId + 1));
+  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
+      .WillOnce(DoAll(SaveArg<0>(&sent_iq2), Return(true)));
+
+  heartbeat_sender_->ProcessResponse(false, nullptr, nullptr /* timeout */);
+  heartbeat_sender_->DoSendStanza();
+  base::RunLoop().RunUntilIdle();
+
+  std::unique_ptr<XmlElement> stanza2(sent_iq2);
+  ASSERT_TRUE(stanza2);
+
+  EXPECT_CALL(signal_strategy_, Disconnect());
+  heartbeat_sender_->ProcessResponse(false, nullptr, nullptr /* timeout */);
+}
+
 }  // namespace remoting
diff --git a/services/resource_coordinator/public/interfaces/BUILD.gn b/services/resource_coordinator/public/interfaces/BUILD.gn
index d68debb..35c4ca5 100644
--- a/services/resource_coordinator/public/interfaces/BUILD.gn
+++ b/services/resource_coordinator/public/interfaces/BUILD.gn
@@ -27,6 +27,13 @@
   export_header =
       "services/resource_coordinator/public/cpp/resource_coordinator_export.h"
 
+  export_class_attribute_blink =
+      "SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT"
+  export_define_blink =
+      "SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_IMPLEMENTATION=1"
+  export_header_blink =
+      "services/resource_coordinator/public/cpp/resource_coordinator_export.h"
+
   # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
   use_once_callback = false
 }
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index 3ddbe2c..e3896e35 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -323,6 +323,10 @@
   // clear.
   WmSetGlobalOverrideCursor(CursorData? cursor);
 
+  // Moves the cursor to the given location on the given display.
+  WmMoveCursorToDisplayLocation(gfx.mojom.Point display_pixels,
+                                int64 display_id);
+
   // Response from WmCreateTopLevelWindow() informing the client of the id for
   // the new window.
   OnWmCreatedTopLevelWindow(uint32 change_id, uint32 window_id);
diff --git a/services/ui/ws/platform_display.h b/services/ui/ws/platform_display.h
index bdfb5b4..a33f638 100644
--- a/services/ui/ws/platform_display.h
+++ b/services/ui/ws/platform_display.h
@@ -48,6 +48,8 @@
 
   virtual void SetCursor(const ui::CursorData& cursor) = 0;
 
+  virtual void MoveCursorTo(const gfx::Point& window_pixel_location) = 0;
+
   virtual void UpdateTextInputState(const ui::TextInputState& state) = 0;
   virtual void SetImeVisibility(bool visible) = 0;
 
diff --git a/services/ui/ws/platform_display_default.cc b/services/ui/ws/platform_display_default.cc
index b474dcf..a807396a 100644
--- a/services/ui/ws/platform_display_default.cc
+++ b/services/ui/ws/platform_display_default.cc
@@ -139,6 +139,11 @@
   platform_window_->SetCursor(native_cursor.platform());
 }
 
+void PlatformDisplayDefault::MoveCursorTo(
+    const gfx::Point& window_pixel_location) {
+  platform_window_->MoveCursorTo(window_pixel_location);
+}
+
 void PlatformDisplayDefault::UpdateTextInputState(
     const ui::TextInputState& state) {
   ui::PlatformImeController* ime = platform_window_->GetPlatformImeController();
diff --git a/services/ui/ws/platform_display_default.h b/services/ui/ws/platform_display_default.h
index 3e28a60f..5f942b1 100644
--- a/services/ui/ws/platform_display_default.h
+++ b/services/ui/ws/platform_display_default.h
@@ -44,6 +44,7 @@
   void SetCapture() override;
   void ReleaseCapture() override;
   void SetCursor(const ui::CursorData& cursor) override;
+  void MoveCursorTo(const gfx::Point& window_pixel_location) override;
   void UpdateTextInputState(const ui::TextInputState& state) override;
   void SetImeVisibility(bool visible) override;
   void UpdateViewportMetrics(const display::ViewportMetrics& metrics) override;
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index 73d969b..3f903fb10 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -46,6 +46,7 @@
   void SetCursor(const ui::CursorData& cursor) override {
     *cursor_storage_ = cursor;
   }
+  void MoveCursorTo(const gfx::Point& window_pixel_location) override {}
   void UpdateTextInputState(const ui::TextInputState& state) override {}
   void SetImeVisibility(bool visible) override {}
   void UpdateViewportMetrics(const display::ViewportMetrics& metrics) override {
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index a36a9b1..eec94676 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -192,6 +192,20 @@
   event_dispatcher_.ReleaseCaptureBlockedByAnyModalWindow();
 }
 
+void WindowManagerState::SetCursorLocation(const gfx::Point& display_pixels,
+                                           int64_t display_id) {
+  Display* display = display_manager()->GetDisplayById(display_id);
+  if (!display) {
+    NOTIMPLEMENTED() << "Window manager sent invalid display_id!";
+    return;
+  }
+
+  event_dispatcher()->SetMousePointerDisplayLocation(display_pixels,
+                                                     display_id);
+  UpdateNativeCursorFromDispatcher();
+  display->platform_display()->MoveCursorTo(display_pixels);
+}
+
 void WindowManagerState::SetDragDropSourceWindow(
     DragSource* drag_source,
     ServerWindow* window,
diff --git a/services/ui/ws/window_manager_state.h b/services/ui/ws/window_manager_state.h
index 68e808b..818c179 100644
--- a/services/ui/ws/window_manager_state.h
+++ b/services/ui/ws/window_manager_state.h
@@ -68,6 +68,9 @@
   void ReleaseCaptureBlockedByModalWindow(const ServerWindow* modal_window);
   void ReleaseCaptureBlockedByAnyModalWindow();
 
+  // Sets the location of the cursor to a location on display |display_id|.
+  void SetCursorLocation(const gfx::Point& display_pixels, int64_t display_id);
+
   void SetDragDropSourceWindow(
       DragSource* drag_source,
       ServerWindow* window,
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index f6b0756..5b7166a 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -2367,6 +2367,12 @@
   window_manager_state_->cursor_state().SetGlobalOverrideCursor(cursor);
 }
 
+void WindowTree::WmMoveCursorToDisplayLocation(const gfx::Point& display_pixels,
+                                               int64_t display_id) {
+  DCHECK(window_manager_state_);
+  window_manager_state_->SetCursorLocation(display_pixels, display_id);
+}
+
 void WindowTree::OnWmCreatedTopLevelWindow(uint32_t change_id,
                                            Id transport_window_id) {
   ServerWindow* window =
diff --git a/services/ui/ws/window_tree.h b/services/ui/ws/window_tree.h
index 42b596c7..3bbcd09b 100644
--- a/services/ui/ws/window_tree.h
+++ b/services/ui/ws/window_tree.h
@@ -539,6 +539,8 @@
   void WmSetCursorVisible(bool visible) override;
   void WmSetGlobalOverrideCursor(
       base::Optional<ui::CursorData> cursor) override;
+  void WmMoveCursorToDisplayLocation(const gfx::Point& display_pixels,
+                                     int64_t display_id) override;
   void OnWmCreatedTopLevelWindow(uint32_t change_id,
                                  Id transport_window_id) override;
   void OnAcceleratorAck(
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index 83b9de0..8350e1f 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -1493,6 +1493,45 @@
   EXPECT_TRUE(wm_internal.on_set_modal_type_called());
 }
 
+TEST_F(WindowTreeTest, TestWindowManagerSettingCursorLocation) {
+  const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1);
+  EXPECT_TRUE(
+      wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties()));
+  ServerWindow* embed_window = wm_tree()->GetWindowByClientId(embed_window_id);
+  ASSERT_TRUE(embed_window);
+  EXPECT_TRUE(wm_tree()->SetWindowVisibility(embed_window_id, true));
+  ASSERT_TRUE(FirstRoot(wm_tree()));
+  const ClientWindowId wm_root_id = FirstRootId(wm_tree());
+  EXPECT_TRUE(wm_tree()->AddWindow(wm_root_id, embed_window_id));
+  ServerWindow* wm_root = FirstRoot(wm_tree());
+  ASSERT_TRUE(wm_root);
+  wm_root->SetBounds(gfx::Rect(0, 0, 100, 100));
+  // This tests expects |wm_root| to be a possible target.
+  wm_root->set_event_targeting_policy(
+      mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS);
+  display()->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+  mojom::WindowTreeClientPtr client;
+  wm_client()->Bind(mojo::MakeRequest(&client));
+  const uint32_t embed_flags = 0;
+  wm_tree()->Embed(embed_window_id, std::move(client), embed_flags);
+  WindowTree* tree1 = window_server()->GetTreeWithRoot(embed_window);
+  ASSERT_TRUE(tree1 != nullptr);
+  ASSERT_NE(tree1, wm_tree());
+
+  embed_window->SetBounds(gfx::Rect(20, 20, 20, 20));
+  embed_window->SetCursor(ui::CursorData(ui::CursorType::kIBeam));
+
+  // Because the cursor is still at the origin, changing the cursor shouldn't
+  // have switched to ibeam.
+  EXPECT_EQ(ui::CursorType::kPointer, cursor_type());
+
+  // Have the window manager mvoe the cursor within the embed window.
+  static_cast<mojom::WindowManagerClient*>(wm_tree())
+      ->WmMoveCursorToDisplayLocation(gfx::Point(21, 21), -1);
+
+  EXPECT_EQ(ui::CursorType::kIBeam, cursor_type());
+}
+
 using WindowTreeShutdownTest = testing::Test;
 
 // Makes sure WindowTreeClient doesn't get any messages during shutdown.
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index d7bd592..589224b 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -47,7 +47,7 @@
           "--mash",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mash.filter"
         ],
-        "name": "ash_unittests --mash",
+        "name": "ash_unittests-mash",
         "override_isolate_target": "ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
@@ -59,7 +59,7 @@
           "--mus",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mus.filter"
         ],
-        "name": "ash_unittests --mus",
+        "name": "ash_unittests-mus",
         "override_isolate_target": "ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 47ce9b82..e8d493e 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -11655,7 +11655,7 @@
           "--mash",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mash.filter"
         ],
-        "name": "ash_unittests --mash",
+        "name": "ash_unittests-mash",
         "override_isolate_target": "ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
@@ -11667,7 +11667,7 @@
           "--mus",
           "--test-launcher-filter-file=../../testing/buildbot/filters/ash_unittests_mus.filter"
         ],
-        "name": "ash_unittests --mus",
+        "name": "ash_unittests-mus",
         "override_isolate_target": "ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index cebbb78..5be5f25 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -475,6 +475,22 @@
             ]
         }
     ],
+    "ClientSidePreviews": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_2G_2",
+                    "params": {
+                        "max_allowed_effective_connection_type": "2G",
+                        "show_offline_pages": "true"
+                    }
+                }
+            ]
+        }
+    ],
     "ContextualSearch": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/MSANExpectations b/third_party/WebKit/LayoutTests/MSANExpectations
index 2c2d910..b85495d 100644
--- a/third_party/WebKit/LayoutTests/MSANExpectations
+++ b/third_party/WebKit/LayoutTests/MSANExpectations
@@ -52,3 +52,12 @@
 
 # These tests use OpenGl, which crashes on MSAN builds due to missing instrumentation
 crbug.com/555703 [ Linux ] virtual/media-gpu-accelerated [ Skip ]
+
+# These tests are just a tad bit too slow on MSAN bots, give them more time.
+crbug.com/729136 [ Linux ] external/wpt/encoding/textdecoder-fatal-single-byte.html [ Slow ]
+crbug.com/729136 [ Linux ] fast/css/css-selector-deeply-nested.html [ Slow ]
+crbug.com/729136 [ Linux ] http/tests/inspector/forced-layout-in-microtask.html [ Slow ]
+crbug.com/729136 [ Linux ] http/tests/inspector/tracing/timeline-xhr-response-type-blob-event.html [ Slow ]
+crbug.com/729136 [ Linux ] inspector/components/file-path-scoring.html [ Slow ]
+crbug.com/729136 [ Linux ] inspector/elements/styles-4/styles-should-not-force-sync-style-recalc.html [ Slow ]
+crbug.com/729136 [ Linux ] webaudio/mixing.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 3b09686d..3333bad1 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2984,7 +2984,6 @@
 crbug.com/725455 [ Linux ] nfc/nfc-block-iframe.html [ Failure Pass Timeout ]
 crbug.com/725455 [ Linux ] nfc/push.html [ Failure Pass Timeout ]
 crbug.com/725455 [ Linux ] nfc/watch.html [ Failure Pass Timeout ]
-crbug.com/725585 payments/payment-request-interface.html [ Failure Pass ]
 
 # Sheriff failures 2017-05-29
 crbug.com/727014 [ Linux ] external/wpt/IndexedDB/large-nested-cloning.html [ Pass Timeout ]
@@ -2996,3 +2995,5 @@
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/basic-plumbing-main-to-worker.html [ Pass Failure Timeout Crash ]
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/basic-plumbing-worker-to-main.html [ Pass Failure Timeout Crash ]
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/request-animation-frame.html [ Pass Failure Timeout Crash ]
+
+crbug.com/729075 [ Mac10.11 Retina ] css3/blending/background-blend-mode-gif-color-2.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/css3/fonts/resources/style-matching-test.js b/third_party/WebKit/LayoutTests/css3/fonts/resources/style-matching-test.js
index c5f964ba..58a883fd 100644
--- a/third_party/WebKit/LayoutTests/css3/fonts/resources/style-matching-test.js
+++ b/third_party/WebKit/LayoutTests/css3/fonts/resources/style-matching-test.js
@@ -183,22 +183,14 @@
         }
     }
 
-    function platformFontsForElementWithSelector(selector)
+    async function platformFontsForElementWithSelector(selector)
     {
-        InspectorTest.requestNodeId(documentNodeId, selector, onNodeId);
-
-        function onNodeId(nodeId)
-        {
-            InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode",
-                                           { nodeId: nodeId },
-                                           onGotComputedFonts);
-        }
-
-        function onGotComputedFonts(response)
-        {
-            logResults(response);
-            nextTest();
-        }
+        var nodeId = await InspectorTest.requestNodeId(documentNodeId, selector);
+        await InspectorTest.sendCommandOrDie("CSS.enable", {});
+        var response = await InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode",
+                                                            { nodeId: nodeId });
+        logResults(response);
+        nextTest();
     }
 
     function logResults(response)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/header-values-normalize-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/header-values-normalize-expected.txt
deleted file mode 100644
index 9f0bc93..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/header-values-normalize-expected.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-This is a testharness.js-based test.
-Found 62 tests; 58 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS XMLHttpRequest with value %00 
-PASS fetch() with value %00 
-PASS XMLHttpRequest with value %01 
-PASS fetch() with value %01 
-PASS XMLHttpRequest with value %02 
-PASS fetch() with value %02 
-PASS XMLHttpRequest with value %03 
-PASS fetch() with value %03 
-PASS XMLHttpRequest with value %04 
-PASS fetch() with value %04 
-PASS XMLHttpRequest with value %05 
-PASS fetch() with value %05 
-PASS XMLHttpRequest with value %06 
-PASS fetch() with value %06 
-PASS XMLHttpRequest with value %07 
-PASS fetch() with value %07 
-PASS XMLHttpRequest with value %08 
-PASS fetch() with value %08 
-PASS XMLHttpRequest with value %09 
-PASS fetch() with value %09 
-FAIL XMLHttpRequest with value %0A Failed to execute 'setRequestHeader' on 'XMLHttpRequest': '
-' is not a valid HTTP header field value.
-FAIL fetch() with value %0A promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid value"
-FAIL XMLHttpRequest with value %0D Failed to execute 'setRequestHeader' on 'XMLHttpRequest': '\r' is not a valid HTTP header field value.
-FAIL fetch() with value %0D promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid value"
-PASS XMLHttpRequest with value %0E 
-PASS fetch() with value %0E 
-PASS XMLHttpRequest with value %0F 
-PASS fetch() with value %0F 
-PASS XMLHttpRequest with value %10 
-PASS fetch() with value %10 
-PASS XMLHttpRequest with value %11 
-PASS fetch() with value %11 
-PASS XMLHttpRequest with value %12 
-PASS fetch() with value %12 
-PASS XMLHttpRequest with value %13 
-PASS fetch() with value %13 
-PASS XMLHttpRequest with value %14 
-PASS fetch() with value %14 
-PASS XMLHttpRequest with value %15 
-PASS fetch() with value %15 
-PASS XMLHttpRequest with value %16 
-PASS fetch() with value %16 
-PASS XMLHttpRequest with value %17 
-PASS fetch() with value %17 
-PASS XMLHttpRequest with value %18 
-PASS fetch() with value %18 
-PASS XMLHttpRequest with value %19 
-PASS fetch() with value %19 
-PASS XMLHttpRequest with value %1A 
-PASS fetch() with value %1A 
-PASS XMLHttpRequest with value %1B 
-PASS fetch() with value %1B 
-PASS XMLHttpRequest with value %1C 
-PASS fetch() with value %1C 
-PASS XMLHttpRequest with value %1D 
-PASS fetch() with value %1D 
-PASS XMLHttpRequest with value %1E 
-PASS fetch() with value %1E 
-PASS XMLHttpRequest with value %1F 
-PASS fetch() with value %1F 
-PASS XMLHttpRequest with value %20 
-PASS fetch() with value %20 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-normalize-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-normalize-expected.txt
deleted file mode 100644
index 5e8dd61..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/headers/headers-normalize-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL Create headers with not normalized values Failed to construct 'Headers': Invalid value
-FAIL Check append method with not normalized values assert_equals: name: name1 has value: space expected "space" but got " space "
-FAIL Check set method with not normalized values assert_equals: name: name1 has value: space expected "space" but got " space "
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/fetch/resources/fetch-test-helpers.js b/third_party/WebKit/LayoutTests/http/tests/fetch/resources/fetch-test-helpers.js
index a25c558..a1dc234 100644
--- a/third_party/WebKit/LayoutTests/http/tests/fetch/resources/fetch-test-helpers.js
+++ b/third_party/WebKit/LayoutTests/http/tests/fetch/resources/fetch-test-helpers.js
@@ -76,11 +76,18 @@
 // A header name must match token in RFC 2616.
 // Fetch API Spec: https://fetch.spec.whatwg.org/#concept-header-name
 var INVALID_HEADER_NAMES = INVALID_TOKENS;
+
+// A header value is a byte sequence that matches the following conditions:
+// * Has no leading or trailing HTTP whitespace bytes (0x09, 0x0A, 0x0D and
+//   0x20).
+// * Contains no 0x00, 0x0A or 0x0D bytes.
+// Note that header values are normalized (ie. stripped of leading and trailing
+// HTTP whitespace bytes) when a new header/value pair is appended or set.
 var INVALID_HEADER_VALUES = [
   'test \r data', 'test \n data', 'test \0 data',
   'test\r\n data',
-  'test\r', 'test\n', 'test\r\n', 'test\0',
-  '\0'.repeat(100000), '\r\n'.repeat(50000), 'x'.repeat(100000) + '\0'];
+  'test\0',
+  '\0'.repeat(100000), 'x'.repeat(100000) + '\0'];
 
 var FORBIDDEN_HEADER_NAMES =
   ['Accept-Charset', 'Accept-Encoding', 'Access-Control-Request-Headers',
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/dom-protocol-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/dom-protocol-test.js
index e8ee26a..1be873c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/dom-protocol-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/dom-protocol-test.js
@@ -3,48 +3,44 @@
 
 InspectorTest.trackGetChildNodesEvents = function(nodeInfo, callback)
 {
-    InspectorTest.eventHandler["DOM.setChildNodes"] = setChildNodes;
+  InspectorTest.eventHandler["DOM.setChildNodes"] = setChildNodes;
 
-    function setChildNodes(message)
-    {
-        var nodes = message.params.nodes;
-        for (var i = 0; i < nodes.length; ++i)
-            InspectorTest.addNode(nodeInfo, nodes[i]);
-        if (callback)
-            callback();
-    }
+  function setChildNodes(message)
+  {
+      var nodes = message.params.nodes;
+      for (var i = 0; i < nodes.length; ++i)
+        InspectorTest.addNode(nodeInfo, nodes[i]);
+      if (callback)
+        callback();
+  }
 }
 
 InspectorTest.addNode = function(nodeInfo, node)
 {
-    nodeInfo[node.nodeId] = node;
-    delete node.nodeId;
-    var children = node.children || [];
-    for (var i = 0; i < children.length; ++i)
-        InspectorTest.addNode(nodeInfo, children[i]);
-    var shadowRoots = node.shadowRoots || [];
-    for (var i = 0; i < shadowRoots.length; ++i)
-        InspectorTest.addNode(nodeInfo, shadowRoots[i]);
+  nodeInfo[node.nodeId] = node;
+  delete node.nodeId;
+  var children = node.children || [];
+  for (var i = 0; i < children.length; ++i)
+    InspectorTest.addNode(nodeInfo, children[i]);
+  var shadowRoots = node.shadowRoots || [];
+  for (var i = 0; i < shadowRoots.length; ++i)
+    InspectorTest.addNode(nodeInfo, shadowRoots[i]);
 }
 
-InspectorTest.requestDocumentNodeId = function(callback)
+InspectorTest.requestDocumentNodeId = async function(callback)
 {
-    InspectorTest.sendCommandOrDie("DOM.getDocument", {}, onGotDocument);
-
-    function onGotDocument(result)
-    {
-        callback(result.root.nodeId);
-    }
+    var result = await InspectorTest.sendCommandOrDie("DOM.getDocument", {});
+    if (callback)
+      callback(result.root.nodeId);
+    return result.root.nodeId;
 };
 
-InspectorTest.requestNodeId = function(documentNodeId, selector, callback)
+InspectorTest.requestNodeId = async function(documentNodeId, selector, callback)
 {
-    InspectorTest.sendCommandOrDie("DOM.querySelector", { "nodeId": documentNodeId , "selector": selector }, onGotNode);
-
-    function onGotNode(result)
-    {
-        callback(result.nodeId);
-    }
+    var result = await InspectorTest.sendCommandOrDie("DOM.querySelector", { "nodeId": documentNodeId , "selector": selector });
+    if (callback)
+      callback(result.nodeId);
+    return result.nodeId;
 };
 
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/protocol-test.html b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/protocol-test.html
index 510b441..1f3ce3c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/protocol-test.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/protocol-test.html
@@ -62,6 +62,9 @@
 
 InspectorTest.sendCommandOrDie = function(command, properties, callback)
 {
+    var fulfill;
+    var result = new Promise(f => fulfill = f);
+
     InspectorTest.sendCommand(command, properties || {}, commandCallback);
     function commandCallback(msg)
     {
@@ -72,7 +75,9 @@
         }
         if (callback)
             callback(msg.result);
+        fulfill(msg.result);
     }
+    return result;
 }
 
 InspectorTest.sendCommandPromise = function(method, params)
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header-expected.txt b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header-expected.txt
index 6e131698..391ad3c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header-expected.txt
@@ -1,10 +1,12 @@
 CONSOLE WARNING: line 10: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
-Test that setRequestHeader properly checks for line feeds in header values.
+Test that setRequestHeader properly checks for invalid characters in header values.
 
-%0AEvil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': ' Evil: on' is not a valid HTTP header field value.
+%0AE%0Avil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'E vil: on' is not a valid HTTP header field value.
 
-%0DEvil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': ' Evil: on' is not a valid HTTP header field value.
+%0DE%0Dvil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'E vil: on' is not a valid HTTP header field value.
 
-%0D%0AEvil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': ' Evil: on' is not a valid HTTP header field value.
+%0D%0AEvil%0D%0A%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'Evil : on' is not a valid HTTP header field value.
 
-%0A%0DEvil%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': ' Evil: on' is not a valid HTTP header field value.
+%0A%0DEvil%0D%0A%3A%20on -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'Evil : on' is not a valid HTTP header field value.
+
+%0D%0AEvil%3A%20o%00n%0A%0D -> SUCCESS, setRequestHeader() raised an exception SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': 'Evil: on' is not a valid HTTP header field value.
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header.html b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header.html
index a3e3a6aef..f1fe5234 100644
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header.html
+++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/inject-header.html
@@ -1,6 +1,6 @@
 <html>
 <body>
-<p>Test that setRequestHeader properly checks for line feeds in header values.</p>
+<p>Test that setRequestHeader properly checks for invalid characters in header values.</p>
 <script>
 if (window.testRunner)
     testRunner.dumpAsText();
@@ -28,10 +28,11 @@
     }
 }
 
-test("\nEvil: on");
-test("\rEvil: on");
-test("\r\nEvil: on");
-test("\n\rEvil: on");
+test("\nE\nvil: on");
+test("\rE\rvil: on");
+test("\r\nEvil\r\n: on");
+test("\n\rEvil\r\n: on");
+test("\r\nEvil: o\0n\n\r");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-get-platform-fonts.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-get-platform-fonts.html
index 616c5941..bc8d880 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-get-platform-fonts.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-get-platform-fonts.html
@@ -28,20 +28,13 @@
     }
 
 
-    function platformFontsForElementWithSelector(selector, callback)
+    async function platformFontsForElementWithSelector(selector, callback)
     {
-        InspectorTest.requestNodeId(documentNodeId, selector, onNodeId);
-
-        function onNodeId(nodeId)
-        {
-            InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode", { nodeId: nodeId }, onGotComputedFonts);
-        }
-
-        function onGotComputedFonts(response)
-        {
-            dumpComputedFonts(response);
-            callback();
-        }
+        var nodeId = await InspectorTest.requestNodeId(documentNodeId, selector);
+        await InspectorTest.sendCommandOrDie("CSS.enable", {  });
+        var response = await InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode", { nodeId: nodeId });
+        dumpComputedFonts(response);
+        callback();
     }
 
     function dumpComputedFonts(response)
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-styleSheetText.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-styleSheetText.html
index 44e8920..9667efb 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-styleSheetText.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-styleSheetText.html
@@ -9,47 +9,29 @@
 
     InspectorTest.sendCommand("CSS.enable", {}, cssWasEnabled);
 
-    function cssWasEnabled()
+    async function cssWasEnabled()
     {
-        InspectorTest.sendCommandOrDie("DOM.getDocument", {}, onGotDocument);
-    }
-
-    function onGotDocument(result)
-    {
+        var result = await InspectorTest.sendCommandOrDie("DOM.getDocument", {});
         var root = result.root;
-        InspectorTest.sendCommandOrDie("DOM.querySelector", {
+        var node = await InspectorTest.sendCommandOrDie("DOM.querySelector", {
             nodeId: root.nodeId,
             selector: "#inliner"
-        }, onGotNode);
-    }
+        });
 
-    function onGotNode(node)
-    {
-        InspectorTest.sendCommandOrDie("CSS.getInlineStylesForNode", { nodeId: node.nodeId }, onGotInlineStyles);
-    }
+        await InspectorTest.sendCommandOrDie("CSS.enable", {  });
 
-    function onGotInlineStyles(result)
-    {
+        result = await InspectorTest.sendCommandOrDie("CSS.getInlineStylesForNode", { nodeId: node.nodeId });
         inlineStyleSheetId = result.inlineStyle.styleSheetId;
-        InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: inlineStyleSheetId }, onReceiveStyleSheetText);
-    }
 
-    function onReceiveStyleSheetText(result)
-    {
+        result = await InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: inlineStyleSheetId });
         InspectorTest.log(result.text);
-        InspectorTest.sendCommandOrDie("CSS.setStyleSheetText", {
+
+        result = await InspectorTest.sendCommandOrDie("CSS.setStyleSheetText", {
             styleSheetId: inlineStyleSheetId,
             text: "border: 1px solid black;"
-        }, onSetStyleSheetBody);
-    }
+        });
 
-    function onSetStyleSheetBody(result)
-    {
-        InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: inlineStyleSheetId }, onCheckStyleSheetBody);
-    }
-
-    function onCheckStyleSheetBody(result)
-    {
+        result = await InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: inlineStyleSheetId });
         InspectorTest.log(result.text);
         InspectorTest.completeTest();
     }
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/pseudo-element-matching-selectors.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/pseudo-element-matching-selectors.html
index 980b0285..b658c98 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/css/pseudo-element-matching-selectors.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/pseudo-element-matching-selectors.html
@@ -8,61 +8,37 @@
     document.getElementById("style").textContent = "#for-pseudo:before { content: \"BEFORE\" }";
 }
 
-function test()
+async function test()
 {
     var nodeInfo = {};
     var childrenCallback;
 
     InspectorTest.eventHandler["DOM.setChildNodes"] = setChildNodes;
-    getDocument();
+    await InspectorTest.sendCommandOrDie("DOM.enable", {});
+    await InspectorTest.sendCommandOrDie("CSS.enable", {});
 
-    function getDocument()
-    {
-        step({
-            name: "Get the Document",
-            command: "DOM.getDocument",
-            parameters: {},
-            callback: getImmediateChildren
-        });
-    };
+    InspectorTest.log("\n=== Get the Document ===\n");
+    var result = await InspectorTest.sendCommandOrDie("DOM.getDocument", {});
+    var bodyId = result.root.children[0].children[1].nodeId;
 
-    function getImmediateChildren(result)
-    {
-        var bodyId = result.root.children[0].children[1].nodeId;
-        childrenCallback = onChildrenRequested;
-        step({
-            name: "Get immediate children of the body",
-            command: "DOM.requestChildNodes",
-            parameters: {"nodeId": bodyId}
-        });
-    };
+    InspectorTest.log("\n=== Get immediate children of the body ===\n");
+    result = await InspectorTest.sendCommandOrDie("DOM.requestChildNodes", {"nodeId": bodyId});
+    var node = findNodeById("for-pseudo");
+    var beforeNode = node.pseudoElements[0];
 
-    function onChildrenRequested()
-    {
-        var node = findNodeById("for-pseudo");
-        var beforeNode = node.pseudoElements[0];
-        step({
-            name: "Request matching styles for #for-pseudo::before",
-            command: "CSS.getMatchedStylesForNode",
-            parameters: {nodeId: beforeNode.nodeId},
-            callback: stylesReceived
-        });
-    }
-
-    function stylesReceived(result)
-    {
-        var matchedRules = result.matchedCSSRules;
-        for (var i = 0; i < matchedRules.length; ++i) {
-            var match = matchedRules[i];
-            if (match.rule.selectorList.text === "#for-pseudo::before") {
-                InspectorTest.log("#for-pseudo::before matching the :before element: " + (match.matchingSelectors[0] === 0));
-                InspectorTest.completeTest();
-                return;
-            }
+    InspectorTest.log("\n=== Request matching styles for #for-pseudo::before ===\n");
+    result = await InspectorTest.sendCommandOrDie("CSS.getMatchedStylesForNode", {nodeId: beforeNode.nodeId});
+    var matchedRules = result.matchedCSSRules;
+    for (var i = 0; i < matchedRules.length; ++i) {
+        var match = matchedRules[i];
+        if (match.rule.selectorList.text === "#for-pseudo::before") {
+            InspectorTest.log("#for-pseudo::before matching the :before element: " + (match.matchingSelectors[0] === 0));
+            InspectorTest.completeTest();
+            return;
         }
-        InspectorTest.log("#for-pseudo::before rule not received");
-        InspectorTest.completeTest();
     }
+    InspectorTest.log("#for-pseudo::before rule not received");
+    InspectorTest.completeTest();
 
     function setChildNodes(message)
     {
@@ -75,18 +51,6 @@
             callback();
     }
 
-    function step(test)
-    {
-        InspectorTest.log("\n=== " + test.name + " ===\n");
-        InspectorTest.sendCommand(test.command, test.parameters, function(messageObject) {
-            if (messageObject.hasOwnProperty("error"))
-                InspectorTest.log("Backend error: " + messageObject.error.message + " (" + messageObject.error.code + ")\n");
-
-            if (test.callback)
-                test.callback(messageObject.result);
-        });
-    }
-
     function findNodeById(id)
     {
         for (var nodeId in nodeInfo) {
@@ -105,18 +69,10 @@
         return null;
     }
 
-    function addNodesRecursive(root)
-    {
-        addNode(root);
-        if (!root.children)
-            return;
-        for (var i = 0; i < root.children.length; ++i)
-            addNodesRecursive(root.children[i]);
-    }
-
-    function addNode(node)
+    function addNodesRecursive(node)
     {
         nodeInfo[node.nodeId] = node;
+        (node.children || []).forEach(addNodesRecursive);
     }
 }
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/resources/layout-font-test.js b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/resources/layout-font-test.js
index f3efc86..996ca6b 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/resources/layout-font-test.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/layout-fonts/resources/layout-font-test.js
@@ -57,20 +57,13 @@
 
     }
 
-    function platformFontsForElementWithSelector(selector)
+    async function platformFontsForElementWithSelector(selector)
     {
-        InspectorTest.requestNodeId(documentNodeId, selector, onNodeId);
-
-        function onNodeId(nodeId)
-        {
-            InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode", { nodeId: nodeId }, onGotComputedFonts);
-        }
-
-        function onGotComputedFonts(response)
-        {
-            collectResults(response);
-            testNextPageElement();
-        }
+        var nodeId = await InspectorTest.requestNodeId(documentNodeId, selector);
+        await InspectorTest.sendCommandOrDie("CSS.enable", {});
+        var response = await InspectorTest.sendCommandOrDie("CSS.getPlatformFontsForNode", { nodeId: nodeId });
+        collectResults(response);
+        testNextPageElement();
     }
 
     function collectResults(response)
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/breakpoint-manager.js b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/breakpoint-manager.js
index c33bcd4..704c107 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/breakpoint-manager.js
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/breakpoint-manager.js
@@ -126,6 +126,12 @@
         return new SDK.DebuggerModel.Location(this, script.scriptId, line, column);
     }
 
+    createRawLocationByURL(url, line, column)
+    {
+        var script = this._scriptForURL(url);
+        return new SDK.DebuggerModel.Location(this, script.scriptId, line, column);
+    }
+
     setBreakpointByURL(url, lineNumber, columnNumber, condition, callback)
     {
         InspectorTest.addResult("    debuggerModel.setBreakpoint(" + [url, lineNumber, condition].join(":") + ")");
diff --git a/third_party/WebKit/LayoutTests/payments/payment-request-interface-expected.txt b/third_party/WebKit/LayoutTests/payments/payment-request-interface-expected.txt
deleted file mode 100644
index 12e2625..0000000
--- a/third_party/WebKit/LayoutTests/payments/payment-request-interface-expected.txt
+++ /dev/null
@@ -1,153 +0,0 @@
-CONSOLE WARNING: line 160: Duplicate shipping option identifier 'express' is treated as an invalid address indicator.
-CONSOLE WARNING: line 248: Cannot yet distinguish credit, debit, and prepaid cards.
-CONSOLE WARNING: line 252: Cannot yet distinguish credit, debit, and prepaid cards.
-This is a testharness.js-based test.
-PASS Creating a PaymentRequest with empty parameters should not throw or crash. 
-PASS Creating a PaymentRequest with extra parameters should not throw or crash. 
-PASS Creating a PaymentRequest with omitted optional parameters should not throw or crash. 
-PASS Creating a PaymentRequest with undefined optional parameters should not throw or crash. 
-PASS Creating a PaymentRequest with null optional parameters should not throw or crash. 
-PASS PaymentRequest should have readonly shippingAddress and shippingOption properties. 
-PASS PaymentRequest should have onShippingAddressChange and onShippingOptionChange events. 
-PASS PaymentRequest should have methods abort() and show(). 
-PASS PaymentRequest.abort() and PaymentRequest.show() should take no parameters. 
-PASS Valid data causes no errors. 
-PASS Shipping option identifier should be null if shipping request is omitted. 
-PASS Shipping option identifier should be null if shipping is explicitly not requested. 
-PASS Shipping option identifier should be null if no shipping options are provided. 
-PASS Shipping option identifier should be null if the single provided option is not selected. 
-PASS Shipping option identifier should default to the single provided option if it is selected. 
-PASS Shipping option identifier should be null if multiple unselected shipping options are provided. 
-PASS Shipping option identifier should default to the selected shipping option. 
-PASS Shipping option identifier should default to the last selected shipping option, if multiple are selected. 
-PASS No shipping option selected for duplicate shipping option identifiers. 
-PASS Shipping type should be null if shipping is explicitly not requested. 
-PASS Shipping type should be 'shipping' by default if shipping type isn't specified. 
-PASS Shipping type should be null if shipping type is specified but requestShipping is false. 
-PASS Shipping type should be 'shipping' if shipping type is specified as 'shipping'. 
-PASS Shipping type should be 'delivery' if shipping type is specified as 'delivery'. 
-PASS Shipping type should be 'pickup' if shipping type is specified as 'pickup'. 
-PASS Shipping type should be 'shipping' if shipping type is specified as undefined. 
-PASS Undefined display items should not throw. 
-PASS Empty display items should not throw. 
-PASS Non-negative total value should not throw. 
-PASS Negative line item value should not throw. 
-PASS Undefined modifiers should not throw. 
-PASS Non-negative total value in PaymentDetailsModifier should not throw. 
-PASS Duplicate supported payment method identifiers should not throw. 
-PASS Duplicate supported payment method identifiers in separate methodData objects should not throw. 
-PASS Duplicate supported payment method identifiers in modifiers should not throw. 
-PASS Duplicate supported payment method identifiers in separate methoData objects of modifiers should not throw. 
-PASS Android Pay parameters for test environment with gateway token should not throw. 
-PASS Android Pay parameters for produciton environment with network token should not throw. 
-PASS Basic card parameters should not throw. 
-PASS Empty basic card parameters should not throw. 
-PASS Invalid basic card parameters should not throw when method name is not "basic-card". 
-PASS Invalid basic card parameters should not throw even when method name is "basic-card". 
-PASS Android Pay parameters for network token without environment key should not throw. 
-PASS Invalid Android Pay parameters should not throw when method name is not "https://android.com/pay". 
-PASS Invalid Android Pay parameters should not throw even when method name is "https://android.com/pay". 
-PASS Array value for payment method specific data parameter should not throw 
-FAIL abort() without show() should reject with error Test bug: need to pass exception to assert_throws()
-FAIL PaymentRequest constructor should throw for incorrect parameter types. Test bug: need to pass exception to assert_throws()
-FAIL PaymentRequest constructor should throw for undefined required parameters. Test bug: need to pass exception to assert_throws()
-FAIL PaymentRequest constructor should throw for null required parameter. Test bug: need to pass exception to assert_throws()
-PASS Empty list of supported payment method identifiers should throw TypeError. 
-FAIL Empty supported payment method identifiers should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Absence of total should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Negative total value should throw a TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Negative total value in PaymentDetailsModifier should throw a TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Null supportedMethods in modifiers should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Undefined supportedMethods in modifiers should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Empty supportedMethods in modifiers should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Absence of supportedMethods in modifiers should throw TypeError. Test bug: need to pass exception to assert_throws()
-FAIL Empty details should throw Test bug: need to pass exception to assert_throws()
-PASS Null items should throw 
-PASS Null shipping options should throw 
-PASS Undefined PaymentShippingType value for shppingType should throw a TypeError 
-PASS Null for shppingType should throw a TypeError 
-PASS Array value for shppingType should throw a TypeError 
-PASS Object value for shppingType should throw a TypeError 
-PASS Numeric value for shppingType should throw a TypeError 
-FAIL String value for payment method specific data parameter should throw Test bug: need to pass exception to assert_throws()
-FAIL Numeric value for payment method specific data parameter should throw Test bug: need to pass exception to assert_throws()
-FAIL Infinite JSON value for one of the payment method specific data pieces should throw Test bug: need to pass exception to assert_throws()
-FAIL Null for payment method specific data parameter should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty string for payment method specific data parameter should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined currency code in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "notdigits" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "ALSONOTDIGITS" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10." in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount ".99" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-10." in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-.99" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10-" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1-0" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1.0.0" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1/3" in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty amount in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Null amount in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined amount in total should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined currency code in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "notdigits" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "ALSONOTDIGITS" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10." in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount ".99" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-10." in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-.99" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10-" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1-0" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1.0.0" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1/3" in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty amount in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Null amount in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined amount in displayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined currency code in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "notdigits" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "ALSONOTDIGITS" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10." in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount ".99" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-10." in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-.99" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10-" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1-0" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1.0.0" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1/3" in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty amount in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Null amount in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined amount in shippingOptions.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined currency code in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "notdigits" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "ALSONOTDIGITS" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10." in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount ".99" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-10." in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-.99" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10-" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1-0" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1.0.0" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1/3" in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty amount in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Null amount in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined amount in modifiers.0.total should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined currency code in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "notdigits" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "ALSONOTDIGITS" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10." in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount ".99" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-10." in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "-.99" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "10-" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1-0" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1.0.0" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Invalid amount "1/3" in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Empty amount in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Null amount in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-FAIL Undefined amount in modifiers.0.additionalDisplayItems.0 should throw Test bug: need to pass exception to assert_throws()
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/payments/payment-request-interface.html b/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
index 8ebb2c2..b57aa85 100644
--- a/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
+++ b/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
@@ -277,47 +277,47 @@
 }, 'Array value for payment method specific data parameter should not throw');
 
 promise_test(function(t) {
-    return promise_rejects(t, null, new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails()).abort());
+    return promise_rejects(t, 'InvalidStateError', new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails()).abort());
 }, 'abort() without show() should reject with error');
 
 generate_tests(assert_throws, [
-    ['PaymentRequest constructor should throw for incorrect parameter types.', null, function() {
+    ['PaymentRequest constructor should throw for incorrect parameter types.', new TypeError(), function() {
         new PaymentRequest('', '', '')
     }],
-    ['PaymentRequest constructor should throw for undefined required parameters.', null, function() {
+    ['PaymentRequest constructor should throw for undefined required parameters.', new TypeError(), function() {
         new PaymentRequest(undefined, undefined)
     }],
-    ['PaymentRequest constructor should throw for null required parameter.', null, function() {
+    ['PaymentRequest constructor should throw for null required parameter.', new TypeError(), function() {
         new PaymentRequest(null, null)
     }],
     ['Empty list of supported payment method identifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([], buildDetails())
     }],
-    ['Empty supported payment method identifiers should throw TypeError.', null, function() {
+    ['Empty supported payment method identifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': []}], buildDetails())
     }],
-    ['Absence of total should throw TypeError.', null, function() {
+    ['Absence of total should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'displayItems': [buildItem()]})
     }],
-    ['Negative total value should throw a TypeError.', null, function() {
+    ['Negative total value should throw a TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails('total', {'value': '-0.01'}))
     }],
-    ['Negative total value in PaymentDetailsModifier should throw a TypeError.', null, function() {
+    ['Negative total value in PaymentDetailsModifier should throw a TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'total': buildItem(), 'modifiers': [{'supportedMethods': ['foo'], 'total': buildItem({'value': '-0.01'})}]})
     }],
-    ['Null supportedMethods in modifiers should throw TypeError.', null, function() {
+    ['Null supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'total': buildItem(), 'modifiers': [{'supportedMethods': null}]})
     }],
-    ['Undefined supportedMethods in modifiers should throw TypeError.', null, function() {
+    ['Undefined supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'total': buildItem(), 'modifiers': [{'supportedMethods': undefined}]})
     }],
-    ['Empty supportedMethods in modifiers should throw TypeError.', null, function() {
+    ['Empty supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'total': buildItem(), 'modifiers': [{'supportedMethods': []}]})
     }],
-    ['Absence of supportedMethods in modifiers should throw TypeError.', null, function() {
+    ['Absence of supportedMethods in modifiers should throw TypeError.', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {'total': buildItem(), 'modifiers': [{'total': buildItem()}]})
     }],
-    ['Empty details should throw', null, function() {
+    ['Empty details should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo']}], {})
     }],
     ['Null items should throw', new TypeError(), function() {
@@ -343,21 +343,21 @@
     }],
 
     // Payment method specific data should be a JSON-serializable object.
-    ['String value for payment method specific data parameter should throw', null, function() {
+    ['String value for payment method specific data parameter should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo'], 'data': 'foo'}], buildDetails(), {})
     }],
-    ['Numeric value for payment method specific data parameter should throw', null, function() {
+    ['Numeric value for payment method specific data parameter should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo'], 'data': 42}], buildDetails(), {})
     }],
-    ['Infinite JSON value for one of the payment method specific data pieces should throw', null, function() {
+    ['Infinite JSON value for one of the payment method specific data pieces should throw', new TypeError(), function() {
         var infiniteData = {'foo': {}};
         infiniteData.foo = infiniteData;
         new PaymentRequest([{'supportedMethods': ['foo'], 'data': infiniteData}], buildDetails())
     }],
-    ['Null for payment method specific data parameter should throw', null, function() {
+    ['Null for payment method specific data parameter should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo'], 'data': null}], buildDetails())
     }],
-    ['Empty string for payment method specific data parameter should throw', null, function() {
+    ['Empty string for payment method specific data parameter should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': ['foo'], 'data': ''}], buildDetails())
     }]
 ]);
@@ -366,51 +366,51 @@
 for (var i in detailNames) {
     generate_tests(assert_throws, [
         // Invalid currency code formats.
-        ['Undefined currency code in ' + detailNames[i] + ' should throw', null, function() {
+        ['Undefined currency code in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'currency': undefined}), {requestShipping: true})
         }],
 
         // Invalid amount formats.
-        ['Invalid amount "-" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "-" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-'}), {requestShipping: true})
         }],
-        ['Invalid amount "notdigits" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "notdigits" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'notdigits'}), {requestShipping: true})
         }],
-        ['Invalid amount "ALSONOTDIGITS" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "ALSONOTDIGITS" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'ALSONOTDIGITS'}), {requestShipping: true})
         }],
-        ['Invalid amount "10." in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "10." in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10.'}), {requestShipping: true})
         }],
-        ['Invalid amount ".99" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount ".99" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '.99'}), {requestShipping: true})
         }],
-        ['Invalid amount "-10." in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "-10." in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-10.'}), {requestShipping: true})
         }],
-        ['Invalid amount "-.99" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "-.99" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-.99'}), {requestShipping: true})
         }],
-        ['Invalid amount "10-" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "10-" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10-'}), {requestShipping: true})
         }],
-        ['Invalid amount "1-0" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "1-0" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1-0'}), {requestShipping: true})
         }],
-        ['Invalid amount "1.0.0" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "1.0.0" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1.0.0'}), {requestShipping: true})
         }],
-        ['Invalid amount "1/3" in ' + detailNames[i] + ' should throw', null, function() {
+        ['Invalid amount "1/3" in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1/3'}), {requestShipping: true})
         }],
-        ['Empty amount in ' + detailNames[i] + ' should throw', null, function() {
+        ['Empty amount in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': ''}), {requestShipping: true})
         }],
-        ['Null amount in ' + detailNames[i] + ' should throw', null, function() {
+        ['Null amount in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': null}), {requestShipping: true})
         }],
-        ['Undefined amount in ' + detailNames[i] + ' should throw', null, function() {
+        ['Undefined amount in ' + detailNames[i] + ' should throw', new TypeError(), function() {
             new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': undefined}), {requestShipping: true})
         }],
     ]);
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
index 2a1c2e8..6784200 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
@@ -875,7 +875,7 @@
 int TextIteratorAlgorithm<Strategy>::StartOffsetInCurrentContainer() const {
   if (text_state_->PositionNode()) {
     text_state_->FlushPositionOffsets();
-    return text_state_->PositionStartOffset() + text_state_->TextStartOffset();
+    return text_state_->PositionStartOffset();
   }
   DCHECK(end_container_);
   return end_offset_;
@@ -885,7 +885,7 @@
 int TextIteratorAlgorithm<Strategy>::EndOffsetInCurrentContainer() const {
   if (text_state_->PositionNode()) {
     text_state_->FlushPositionOffsets();
-    return text_state_->PositionEndOffset() + text_state_->TextStartOffset();
+    return text_state_->PositionEndOffset();
   }
   DCHECK(end_container_);
   return end_offset_;
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
index d96e3c70..1fd0e41d 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextNodeHandler.cpp
@@ -330,7 +330,7 @@
         // come back again to finish handling this text box; don't advance to
         // the next one.
         if (static_cast<unsigned>(text_state_->PositionEndOffset()) <
-            text_box_end)
+            text_box_end + text_start_offset)
           return;
 
         if (behavior_.DoesNotEmitSpaceBeyondRangeEnd()) {
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
index e6cc3b1..6971b1f 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
@@ -52,7 +52,7 @@
     return single_character_buffer_;
   }
 
-  return text_[PositionStartOffset() + index];
+  return text_[text_start_offset_ + index];
 }
 
 String TextIteratorTextState::Substring(unsigned position,
@@ -66,7 +66,7 @@
     DCHECK_EQ(length, 1u);
     return String(&single_character_buffer_, 1);
   }
-  return text_.Substring(PositionStartOffset() + position, length);
+  return text_.Substring(text_start_offset_ + position, length);
 }
 
 void TextIteratorTextState::AppendTextToStringBuilder(
@@ -81,7 +81,7 @@
     DCHECK_EQ(position, 0u);
     builder.Append(single_character_buffer_);
   } else {
-    builder.Append(text_, PositionStartOffset() + position, length_to_append);
+    builder.Append(text_, text_start_offset_ + position, length_to_append);
   }
 }
 
@@ -94,15 +94,15 @@
   single_character_buffer_ = 0;
 
   text_length_ = 0;
-  last_character_ = 0;
   text_start_offset_ = 0;
+  last_character_ = 0;
 }
 
 void TextIteratorTextState::EmitAltText(Node* node) {
   text_ = ToHTMLElement(node)->AltText();
+  text_start_offset_ = 0;
   text_length_ = text_.length();
   last_character_ = text_length_ ? text_[text_length_ - 1] : 0;
-  text_start_offset_ = 0;
 }
 
 void TextIteratorTextState::FlushPositionOffsets() const {
@@ -135,12 +135,14 @@
   single_character_buffer_ = c;
   DCHECK(single_character_buffer_);
   text_length_ = 1;
+  text_start_offset_ = 0;
 
   // remember some iteration state
   last_character_ = c;
-  text_start_offset_ = 0;
 }
 
+// TODO(xiaochengh): Remove the dependency on LayoutText, so that the class can
+// also be used by Layout NG.
 void TextIteratorTextState::EmitText(Node* text_node,
                                      LayoutText* layout_object,
                                      int text_start_offset,
@@ -160,14 +162,14 @@
 
   position_node_ = text_node;
   position_offset_base_node_ = nullptr;
-  position_start_offset_ = text_start_offset;
-  position_end_offset_ = text_end_offset;
+  position_start_offset_ = text_start_offset + layout_object->TextStartOffset();
+  position_end_offset_ = text_end_offset + layout_object->TextStartOffset();
   single_character_buffer_ = 0;
+  text_start_offset_ = text_start_offset;
   text_length_ = text_end_offset - text_start_offset;
   last_character_ = text_[text_end_offset - 1];
 
   has_emitted_ = true;
-  text_start_offset_ = layout_object->TextStartOffset();
 }
 
 void TextIteratorTextState::AppendTextTo(ForwardsTextBuffer* output,
@@ -186,17 +188,11 @@
     output->PushCharacters(single_character_buffer_, 1);
     return;
   }
-  if (PositionNode()) {
-    FlushPositionOffsets();
-    unsigned offset = PositionStartOffset() + position;
-    if (text_.Is8Bit())
-      output->PushRange(text_.Characters8() + offset, length_to_append);
-    else
-      output->PushRange(text_.Characters16() + offset, length_to_append);
-    return;
-  }
-  // We shouldn't be attempting to append text that doesn't exist.
-  NOTREACHED();
+  unsigned offset = text_start_offset_ + position;
+  if (text_.Is8Bit())
+    output->PushRange(text_.Characters8() + offset, length_to_append);
+  else
+    output->PushRange(text_.Characters16() + offset, length_to_append);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
index f81bb8fe..64dbf2f 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
@@ -44,12 +44,16 @@
   explicit TextIteratorTextState(const TextIteratorBehavior&);
   ~TextIteratorTextState() {}
 
+  // Return properties of the current text.
   int length() const { return text_length_; }
   UChar CharacterAt(unsigned index) const;
   String Substring(unsigned position, unsigned length) const;
   void AppendTextToStringBuilder(StringBuilder&,
                                  unsigned position = 0,
                                  unsigned max_length = UINT_MAX) const;
+  void AppendTextTo(ForwardsTextBuffer* output,
+                    unsigned position,
+                    unsigned length_to_append) const;
 
   void SpliceBuffer(UChar,
                     Node* text_node,
@@ -62,35 +66,36 @@
                 int text_end_offset);
   void EmitAltText(Node*);
   void UpdateForReplacedElement(Node* base_node);
+
+  // Return position of the current text.
   void FlushPositionOffsets() const;
   int PositionStartOffset() const { return position_start_offset_; }
   int PositionEndOffset() const { return position_end_offset_; }
   Node* PositionNode() const { return position_node_; }
+
   bool HasEmitted() const { return has_emitted_; }
   UChar LastCharacter() const { return last_character_; }
-  int TextStartOffset() const { return text_start_offset_; }
   void ResetRunInformation() {
     position_node_ = nullptr;
     text_length_ = 0;
   }
 
-  void AppendTextTo(ForwardsTextBuffer* output,
-                    unsigned position,
-                    unsigned length_to_append) const;
-
   DECLARE_TRACE();
 
  private:
   int text_length_ = 0;
-  String text_;
 
   // Used for whitespace characters that aren't in the DOM, so we can point at
   // them.
-  // If non-zero, overrides m_text.
+  // If non-zero, overrides |text_|.
   UChar single_character_buffer_ = 0;
 
-  // The current text and its position, in the form to be returned from the
-  // iterator.
+  // The current text when |single_character_buffer_| is zero, in which case it
+  // is |text_.Substring(text_start_offset_, text_length_)|.
+  String text_;
+  int text_start_offset_ = 0;
+
+  // Position of the current text, in the form to be returned from the iterator.
   Member<Node> position_node_;
   mutable Member<Node> position_offset_base_node_;
   mutable int position_start_offset_ = 0;
@@ -103,10 +108,6 @@
 
   const TextIteratorBehavior behavior_;
 
-  // Stores the length of :first-letter when we are at the remaining text.
-  // Equals to 0 in all other cases.
-  int text_start_offset_ = 0;
-
   DISALLOW_COPY_AND_ASSIGN(TextIteratorTextState);
 };
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
index 77463d4..eddcc1c 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
@@ -757,6 +757,7 @@
   HeapVector<Member<Document>> documents = dom_agent_->Documents();
   for (Document* document : documents)
     UpdateActiveStyleSheets(document);
+  was_enabled_ = true;
 }
 
 Response InspectorCSSAgent::disable() {
@@ -764,6 +765,7 @@
   dom_agent_->SetDOMListener(nullptr);
   instrumenting_agents_->removeInspectorCSSAgent(this);
   state_->setBoolean(CSSAgentState::kCssAgentEnabled, false);
+  was_enabled_ = false;
   resource_content_loader_->Cancel(resource_content_loader_client_id_);
   state_->setBoolean(CSSAgentState::kRuleRecordingEnabled, false);
   SetCoverageEnabled(false);
@@ -915,8 +917,12 @@
         inherited_entries,
     Maybe<protocol::Array<protocol::CSS::CSSKeyframesRule>>*
         css_keyframes_rules) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
+
   Element* element = nullptr;
-  Response response = dom_agent_->AssertElement(node_id, element);
+  response = dom_agent_->AssertElement(node_id, element);
   if (!response.isSuccess())
     return response;
 
@@ -1092,8 +1098,11 @@
     int node_id,
     Maybe<protocol::CSS::CSSStyle>* inline_style,
     Maybe<protocol::CSS::CSSStyle>* attributes_style) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
   Element* element = nullptr;
-  Response response = dom_agent_->AssertElement(node_id, element);
+  response = dom_agent_->AssertElement(node_id, element);
   if (!response.isSuccess())
     return response;
 
@@ -1111,8 +1120,11 @@
     int node_id,
     std::unique_ptr<protocol::Array<protocol::CSS::CSSComputedStyleProperty>>*
         style) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
   Node* node = nullptr;
-  Response response = dom_agent_->AssertNode(node_id, node);
+  response = dom_agent_->AssertNode(node_id, node);
   if (!response.isSuccess())
     return response;
 
@@ -1178,8 +1190,11 @@
     int node_id,
     std::unique_ptr<protocol::Array<protocol::CSS::PlatformFontUsage>>*
         platform_fonts) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
   Node* node = nullptr;
-  Response response = dom_agent_->AssertNode(node_id, node);
+  response = dom_agent_->AssertNode(node_id, node);
   if (!response.isSuccess())
     return response;
 
@@ -1565,8 +1580,11 @@
 Response InspectorCSSAgent::forcePseudoState(
     int node_id,
     std::unique_ptr<protocol::Array<String>> forced_pseudo_classes) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
   Element* element = nullptr;
-  Response response = dom_agent_->AssertElement(node_id, element);
+  response = dom_agent_->AssertElement(node_id, element);
   if (!response.isSuccess())
     return response;
 
@@ -1891,9 +1909,17 @@
   return css_style_sheet_to_inspector_style_sheet_.at(&inspector_sheet);
 }
 
+Response InspectorCSSAgent::AssertEnabled() {
+  return was_enabled_ ? Response::OK()
+                      : Response::Error("CSS agent was not enabled");
+}
+
 Response InspectorCSSAgent::AssertInspectorStyleSheetForId(
     const String& style_sheet_id,
     InspectorStyleSheet*& result) {
+  Response response = AssertEnabled();
+  if (!response.isSuccess())
+    return response;
   IdToInspectorStyleSheet::iterator it =
       id_to_inspector_style_sheet_.find(style_sheet_id);
   if (it == id_to_inspector_style_sheet_.end())
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h
index 6b381b2..81f8b8b5 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h
@@ -280,6 +280,8 @@
   InspectorStyleSheet* InspectorStyleSheetForRule(CSSStyleRule*);
 
   InspectorStyleSheet* ViaInspectorStyleSheet(Document*);
+
+  protocol::Response AssertEnabled();
   protocol::Response AssertInspectorStyleSheetForId(const String&,
                                                     InspectorStyleSheet*&);
   protocol::Response AssertStyleSheetForId(const String&,
@@ -352,6 +354,7 @@
   Member<CSSStyleSheet> inspector_user_agent_style_sheet_;
 
   int resource_content_loader_client_id_;
+  bool was_enabled_ = false;
 
   friend class InspectorResourceContentLoaderCallback;
   friend class StyleSheetBinder;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index 2af18c4..0e4c826a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -1280,8 +1280,8 @@
                                 VisualRectFlags,
                                 TransformState&) const;
 
-  virtual bool HasRelativeLogicalWidth() const;
-  virtual bool HasRelativeLogicalHeight() const;
+  bool HasRelativeLogicalWidth() const;
+  bool HasRelativeLogicalHeight() const;
 
   bool HasHorizontalLayoutOverflow() const {
     if (!overflow_)
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index 2cb0204..1a2ec91 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -798,7 +798,7 @@
 
   if (AreMethodAndURLValidForSend()) {
     if (GetRequestHeader(HTTPNames::Content_Type).IsEmpty()) {
-      const String& blob_type = body->type();
+      const String& blob_type = FetchUtils::NormalizeHeaderValue(body->type());
       if (!blob_type.IsEmpty() && ParsedContentType(blob_type).IsValid()) {
         SetRequestHeaderInternal(HTTPNames::Content_Type,
                                  AtomicString(blob_type));
@@ -839,7 +839,7 @@
     if (GetRequestHeader(HTTPNames::Content_Type).IsEmpty()) {
       AtomicString content_type =
           AtomicString("multipart/form-data; boundary=") +
-          http_body->Boundary().data();
+          FetchUtils::NormalizeHeaderValue(http_body->Boundary().data());
       SetRequestHeaderInternal(HTTPNames::Content_Type, content_type);
     }
   }
@@ -1297,28 +1297,36 @@
   mime_type_override_ = mime_type;
 }
 
+// https://xhr.spec.whatwg.org/#the-setrequestheader()-method
 void XMLHttpRequest::setRequestHeader(const AtomicString& name,
                                       const AtomicString& value,
                                       ExceptionState& exception_state) {
+  // "1. If |state| is not "opened", throw an InvalidStateError exception.
+  //  2. If the send() flag is set, throw an InvalidStateError exception."
   if (state_ != kOpened || send_flag_) {
     exception_state.ThrowDOMException(kInvalidStateError,
                                       "The object's state must be OPENED.");
     return;
   }
 
+  // "3. Normalize |value|."
+  const String normalized_value = FetchUtils::NormalizeHeaderValue(value);
+
+  // "4. If |name| is not a name or |value| is not a value, throw a SyntaxError
+  //     exception."
   if (!IsValidHTTPToken(name)) {
     exception_state.ThrowDOMException(
         kSyntaxError, "'" + name + "' is not a valid HTTP header field name.");
     return;
   }
-
-  if (!IsValidHTTPHeaderValue(value)) {
+  if (!IsValidHTTPHeaderValue(normalized_value)) {
     exception_state.ThrowDOMException(
         kSyntaxError,
-        "'" + value + "' is not a valid HTTP header field value.");
+        "'" + normalized_value + "' is not a valid HTTP header field value.");
     return;
   }
 
+  // "5. Terminate these steps if |name| is a forbidden header name."
   // No script (privileged or not) can set unsafe headers.
   if (FetchUtils::IsForbiddenHeaderName(name)) {
     LogConsoleError(GetExecutionContext(),
@@ -1326,11 +1334,14 @@
     return;
   }
 
-  SetRequestHeaderInternal(name, value);
+  // "6. Combine |name|/|value| in author request headers."
+  SetRequestHeaderInternal(name, AtomicString(normalized_value));
 }
 
 void XMLHttpRequest::SetRequestHeaderInternal(const AtomicString& name,
                                               const AtomicString& value) {
+  DCHECK_EQ(value, FetchUtils::NormalizeHeaderValue(value))
+      << "Header values must be normalized";
   HTTPHeaderMap::AddResult result = request_headers_.Add(name, value);
   if (!result.is_new_entry) {
     AtomicString new_value = result.stored_value->value + ", " + value;
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
index 1c9adac..fead041 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
@@ -90,10 +90,11 @@
     console.assert(scripts.length);
     var script = scripts[scripts.length - 1];
     if (script.isInlineScriptWithSourceURL()) {
-      return this._debuggerModel.createRawLocation(
-          script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset);
+      return this._debuggerModel.createRawLocationByURL(
+          script.sourceURL, lineNumber + script.lineOffset,
+          lineNumber ? columnNumber : columnNumber + script.columnOffset);
     }
-    return this._debuggerModel.createRawLocation(script, lineNumber, columnNumber);
+    return this._debuggerModel.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/devices/DevicesView.js b/third_party/WebKit/Source/devtools/front_end/devices/DevicesView.js
index 082c942..91d2371 100644
--- a/third_party/WebKit/Source/devtools/front_end/devices/DevicesView.js
+++ b/third_party/WebKit/Source/devtools/front_end/devices/DevicesView.js
@@ -791,8 +791,7 @@
 
     var titleRow = element.createChild('div', 'device-page-title-row');
     var title = titleRow.createChild('div', 'device-page-title');
-    var inspect =
-        UI.createTextButton(Common.UIString('Inspect'), doAction.bind(null, 'inspect'), 'device-inspect-button');
+    var inspect = UI.createTextButton(Common.UIString('Inspect'), doAction.bind(null, 'inspect'));
     titleRow.appendChild(inspect);
 
     var toolbar = new UI.Toolbar('');
diff --git a/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteView.js b/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteView.js
index a8ab848..ddd24aaa 100644
--- a/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteView.js
+++ b/third_party/WebKit/Source/devtools/front_end/help/ReleaseNoteView.js
@@ -31,19 +31,14 @@
     }
 
     var actionContainer = container.createChild('div', 'release-note-action-container');
-    var viewMoreButton = actionContainer.createChild('button');
-    viewMoreButton.textContent = Common.UIString('Learn more');
-    viewMoreButton.addEventListener('click', event => {
+    actionContainer.appendChild(UI.createTextButton(Common.UIString('Learn more'), event => {
       event.consume(true);
       InspectorFrontendHost.openInNewTab(releaseNote.link);
-    });
-
-    var closeButton = actionContainer.createChild('button', 'close-release-note');
-    closeButton.textContent = Common.UIString('Close');
-    closeButton.addEventListener('click', event => {
+    }));
+    actionContainer.appendChild(UI.createTextButton(Common.UIString('Close'), event => {
       event.consume(true);
       UI.inspectorView.closeDrawerTab(Help._releaseNoteViewId, true);
-    });
+    }, 'close-release-note'));
 
     var imageLink = UI.createExternalLink(releaseNote.link, ' ');
     imageLink.classList.add('release-note-image');
diff --git a/third_party/WebKit/Source/devtools/front_end/help/releaseNote.css b/third_party/WebKit/Source/devtools/front_end/help/releaseNote.css
index e09c204..b49054e 100644
--- a/third_party/WebKit/Source/devtools/front_end/help/releaseNote.css
+++ b/third_party/WebKit/Source/devtools/front_end/help/releaseNote.css
@@ -75,6 +75,7 @@
     text-transform: uppercase;
     color: #757575;
     font-size: 13px;
+    box-shadow: none;
 }
 
 .release-note-action-container > button:hover {
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/FilmStripView.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/FilmStripView.js
index ee0774c..049bf3e 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/FilmStripView.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/FilmStripView.js
@@ -223,12 +223,12 @@
     this._imageElement = this.contentElement.createChild('img');
     var footerElement = this.contentElement.createChild('div', 'filmstrip-dialog-footer');
     footerElement.createChild('div', 'flex-auto');
-    var prevButton =
-        UI.createTextButton('\u25C0', this._onPrevFrame.bind(this), undefined, Common.UIString('Previous frame'));
+    var prevButton = UI.createTextButton('\u25C0', this._onPrevFrame.bind(this));
+    prevButton.title = Common.UIString('Previous frame');
     footerElement.appendChild(prevButton);
     this._timeLabel = footerElement.createChild('div', 'filmstrip-dialog-label');
-    var nextButton =
-        UI.createTextButton('\u25B6', this._onNextFrame.bind(this), undefined, Common.UIString('Next frame'));
+    var nextButton = UI.createTextButton('\u25B6', this._onNextFrame.bind(this));
+    nextButton.title = Common.UIString('Next frame');
     footerElement.appendChild(nextButton);
     footerElement.createChild('div', 'flex-auto');
 
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
index 0fc88a5..7cc895c 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
@@ -47,10 +47,11 @@
     targetDiv.createChild('div').textContent = Common.UIString('Target:');
     var targetsSelect = targetDiv.createChild('select', 'chrome-select');
     new Profiler.TargetsComboBoxController(targetsSelect, targetDiv);
-    this._controlButton = UI.createTextButton('', this._controlButtonClicked.bind(this));
-    controlDiv.appendChild(this._controlButton);
+    this._controlButton = UI.createTextButton('', this._controlButtonClicked.bind(this), 'profile-launcher-button');
+    this._contentElement.appendChild(this._controlButton);
     this._recordButtonEnabled = true;
-    this._loadButton = UI.createTextButton(Common.UIString('Load'), this._loadButtonClicked.bind(this), 'load-profile');
+    this._loadButton =
+        UI.createTextButton(Common.UIString('Load'), this._loadButtonClicked.bind(this), 'profile-launcher-button');
     this._contentElement.appendChild(this._loadButton);
 
     this._selectedProfileTypeSetting = Common.settings.createSetting('selectedProfileType', 'CPU');
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/profilesPanel.css b/third_party/WebKit/Source/devtools/front_end/profiler/profilesPanel.css
index eb70b40..407370e8 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/profilesPanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/profilesPanel.css
@@ -200,12 +200,12 @@
     width: 150px;
 }
 
-button.load-profile {
+.profile-launcher-button {
     margin-top: 10px;
+    margin-right: 8px;
     min-width: 110px;
 }
 
-
 .cpu-profile-flame-chart-overview-container {
     overflow: hidden;
     position: absolute;
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
index 6ed168c4..38989116 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
@@ -592,8 +592,6 @@
    * @return {?SDK.DebuggerModel.Location}
    */
   createRawLocation(script, lineNumber, columnNumber) {
-    if (script.sourceURL)
-      return this.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber);
     return new SDK.DebuggerModel.Location(this, script.scriptId, lineNumber, columnNumber);
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js b/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
index 3d33e23..8c8e917ac3 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
@@ -103,43 +103,40 @@
     // Build the buttons (Find, Previous, Replace, Replace All).
     this._buttonsContainer = this._footerElement.createChild('div', 'toolbar-search-buttons hidden');
 
-    var findButtonElement = this._buttonsContainer.createChild('button', 'search-action-button');
-    findButtonElement.textContent = Common.UIString('Find');
+    var findButtonElement =
+        UI.createTextButton(Common.UIString('Find'), this._onFindClick.bind(this), 'search-action-button');
     findButtonElement.tabIndex = -1;
-    findButtonElement.addEventListener('click', this._onFindClick.bind(this), false);
+    this._buttonsContainer.appendChild(findButtonElement);
 
-    var prevButtonElement = this._buttonsContainer.createChild('button', 'search-action-button');
-    prevButtonElement.textContent = Common.UIString('Previous');
+    var prevButtonElement =
+        UI.createTextButton(Common.UIString('Previous'), this._onPreviousClick.bind(this), 'search-action-button');
     prevButtonElement.tabIndex = -1;
-    prevButtonElement.addEventListener('click', this._onPreviousClick.bind(this), false);
+    this._buttonsContainer.appendChild(prevButtonElement);
 
-    this._replaceButtonElement = this._buttonsContainer.createChild('button', 'search-action-button');
-    this._replaceButtonElement.textContent = Common.UIString('Replace');
+    this._replaceButtonElement =
+        UI.createTextButton(Common.UIString('Replace'), this._replace.bind(this), 'search-action-button');
     this._replaceButtonElement.disabled = true;
     this._replaceButtonElement.tabIndex = -1;
-    this._replaceButtonElement.addEventListener('click', this._replace.bind(this), false);
+    this._buttonsContainer.appendChild(this._replaceButtonElement);
 
-    var replaceAllButtonElement = this._buttonsContainer.createChild('button', 'search-action-button');
-    replaceAllButtonElement.textContent = Common.UIString('Replace All');
-    replaceAllButtonElement.addEventListener('click', this._replaceAll.bind(this), false);
+    var replaceAllButtonElement =
+        UI.createTextButton(Common.UIString('Replace All'), this._replaceAll.bind(this), 'search-action-button');
+    this._buttonsContainer.appendChild(replaceAllButtonElement);
 
     // Build the replace checkbox and cancel button.
     this._replaceElement = this._footerElement.createChild('div').createChild('span', 'toolbar-replace-checkbox');
 
     var replaceLabelElement = UI.CheckboxLabel.create(Common.UIString('Replace'));
     this._replaceCheckboxElement = replaceLabelElement.checkboxElement;
-    var uniqueId = ++UI.SearchableView._lastUniqueId;
-    var replaceCheckboxId = 'search-replace-trigger' + uniqueId;
-    this._replaceCheckboxElement.id = replaceCheckboxId;
     this._replaceCheckboxElement.addEventListener('change', this._updateSecondRowVisibility.bind(this), false);
 
     this._replaceElement.appendChild(replaceLabelElement);
 
-    var cancelButtonElement = this._footerElement.createChild('div').createChild('button', 'search-action-button');
-    cancelButtonElement.textContent = Common.UIString('Cancel');
+    var cancelButtonElement =
+        UI.createTextButton(Common.UIString('Cancel'), this.closeSearch.bind(this), 'search-action-button');
     cancelButtonElement.tabIndex = -1;
-    cancelButtonElement.addEventListener('click', this.closeSearch.bind(this), false);
     this._minimalSearchQuerySize = 3;
+    this._footerElement.createChild('div').appendChild(cancelButtonElement);
 
     this._loadSetting();
   }
@@ -510,7 +507,6 @@
   }
 };
 
-UI.SearchableView._lastUniqueId = 0;
 
 UI.SearchableView._symbol = Symbol('searchableView');
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
index ea0a021..13125efc 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -1170,16 +1170,13 @@
  * @param {string} text
  * @param {function(!Event)=} clickHandler
  * @param {string=} className
- * @param {string=} title
  * @return {!Element}
  */
-UI.createTextButton = function(text, clickHandler, className, title) {
+UI.createTextButton = function(text, clickHandler, className) {
   var element = createElementWithClass('button', className || '', 'text-button');
   element.textContent = text;
   if (clickHandler)
     element.addEventListener('click', clickHandler, false);
-  if (title)
-    element.title = title;
   return element;
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
index b5c4f1e..b5b74fa 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
@@ -77,7 +77,7 @@
     border-radius: 8px;
     margin: 1px 3px 0 3px;
     background-image: linear-gradient(rgb(241, 241, 241), rgb(220, 220, 220));
-    width: 75px;
+    width: 82px;
     height: 20px;
     white-space: nowrap;
 }
@@ -93,7 +93,7 @@
 }
 
 .toolbar-search-buttons {
-    flex-basis: 165px;
+    flex-basis: 179px;
 }
 
 .toolbar-replace-control,
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/textButton.css b/third_party/WebKit/Source/devtools/front_end/ui/textButton.css
index d4a718b..be3ce161 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/textButton.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/textButton.css
@@ -13,7 +13,7 @@
     font-size: 12px;
     margin: 0 1px 0 0;
     text-shadow: 0 1px 0 hsl(0, 0%, 94%);
-    min-height: 2em !important;
+    height: 24px;
     padding-left: 10px;
     padding-right: 10px;
     -webkit-user-select: none;
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.idl b/third_party/WebKit/Source/modules/budget/BudgetService.idl
index 3f89319..e0235e2b 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.idl
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.idl
@@ -12,7 +12,7 @@
     RuntimeEnabled=Budget,
     Exposed=(Window,Worker)
 ] interface BudgetService {
-    [OriginTrialEnabled=BudgetQuery, CallWith=ScriptState] Promise<double> getCost(OperationType operation);
-    [OriginTrialEnabled=BudgetQuery, CallWith=ScriptState] Promise<sequence<BudgetState>> getBudget();
+    [OriginTrialEnabled=BudgetQuery, CallWith=ScriptState, MeasureAs=BudgetAPIGetCost] Promise<double> getCost(OperationType operation);
+    [OriginTrialEnabled=BudgetQuery, CallWith=ScriptState, MeasureAs=BudgetAPIGetBudget] Promise<sequence<BudgetState>> getBudget();
     [CallWith=ScriptState] Promise<boolean> reserve(OperationType operation);
 };
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.cpp b/third_party/WebKit/Source/modules/fetch/Headers.cpp
index 7e6ea5f..985c6fb 100644
--- a/third_party/WebKit/Source/modules/fetch/Headers.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Headers.cpp
@@ -79,37 +79,40 @@
                      ExceptionState& exception_state) {
   // "To append a name/value (|name|/|value|) pair to a Headers object
   // (|headers|), run these steps:"
-  // "1. If |name| is not a name or |value| is not a value, throw a
+  // "1. Normalize |value|."
+  const String normalized_value = FetchUtils::NormalizeHeaderValue(value);
+  // "2. If |name| is not a name or |value| is not a value, throw a
   //     TypeError."
   if (!FetchHeaderList::IsValidHeaderName(name)) {
     exception_state.ThrowTypeError("Invalid name");
     return;
   }
-  if (!FetchHeaderList::IsValidHeaderValue(value)) {
+  if (!FetchHeaderList::IsValidHeaderValue(normalized_value)) {
     exception_state.ThrowTypeError("Invalid value");
     return;
   }
-  // "2. If guard is |request|, throw a TypeError."
+  // "3. If guard is |request|, throw a TypeError."
   if (guard_ == kImmutableGuard) {
     exception_state.ThrowTypeError("Headers are immutable");
     return;
   }
-  // "3. Otherwise, if guard is |request| and |name| is a forbidden header
+  // "4. Otherwise, if guard is |request| and |name| is a forbidden header
   //     name, return."
   if (guard_ == kRequestGuard && FetchUtils::IsForbiddenHeaderName(name))
     return;
-  // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a
+  // "5. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a
   //     simple header, return."
   if (guard_ == kRequestNoCORSGuard &&
-      !FetchUtils::IsSimpleHeader(AtomicString(name), AtomicString(value)))
+      !FetchUtils::IsSimpleHeader(AtomicString(name),
+                                  AtomicString(normalized_value)))
     return;
-  // "5. Otherwise, if guard is |response| and |name| is a forbidden response
+  // "6. Otherwise, if guard is |response| and |name| is a forbidden response
   //     header name, return."
   if (guard_ == kResponseGuard &&
       FetchUtils::IsForbiddenResponseHeaderName(name))
     return;
-  // "6. Append |name|/|value| to header list."
-  header_list_->Append(name, value);
+  // "7. Append |name|/|value| to header list."
+  header_list_->Append(name, normalized_value);
 }
 
 void Headers::remove(const String& name, ExceptionState& exception_state) {
@@ -173,37 +176,40 @@
                   const String& value,
                   ExceptionState& exception_state) {
   // "The set(|name|, |value|) method, when invoked, must run these steps:"
-  // "1. If |name| is not a name or |value| is not a value, throw a
+  // "1. Normalize |value|."
+  const String normalized_value = FetchUtils::NormalizeHeaderValue(value);
+  // "2. If |name| is not a name or |value| is not a value, throw a
   //     TypeError."
   if (!FetchHeaderList::IsValidHeaderName(name)) {
     exception_state.ThrowTypeError("Invalid name");
     return;
   }
-  if (!FetchHeaderList::IsValidHeaderValue(value)) {
+  if (!FetchHeaderList::IsValidHeaderValue(normalized_value)) {
     exception_state.ThrowTypeError("Invalid value");
     return;
   }
-  // "2. If guard is |immutable|, throw a TypeError."
+  // "3. If guard is |immutable|, throw a TypeError."
   if (guard_ == kImmutableGuard) {
     exception_state.ThrowTypeError("Headers are immutable");
     return;
   }
-  // "3. Otherwise, if guard is |request| and |name| is a forbidden header
+  // "4. Otherwise, if guard is |request| and |name| is a forbidden header
   //     name, return."
   if (guard_ == kRequestGuard && FetchUtils::IsForbiddenHeaderName(name))
     return;
-  // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a
+  // "5. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a
   //     simple header, return."
   if (guard_ == kRequestNoCORSGuard &&
-      !FetchUtils::IsSimpleHeader(AtomicString(name), AtomicString(value)))
+      !FetchUtils::IsSimpleHeader(AtomicString(name),
+                                  AtomicString(normalized_value)))
     return;
-  // "5. Otherwise, if guard is |response| and |name| is a forbidden response
+  // "6. Otherwise, if guard is |response| and |name| is a forbidden response
   //     header name, return."
   if (guard_ == kResponseGuard &&
       FetchUtils::IsForbiddenResponseHeaderName(name))
     return;
-  // "6. Set |name|/|value| in header list."
-  header_list_->Set(name, value);
+  // "7. Set |name|/|value| in header list."
+  header_list_->Set(name, normalized_value);
 }
 
 // This overload is not called directly by Web APIs, but rather by other C++
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
index 9e1d3f5..1e91b421 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
@@ -170,7 +170,7 @@
       has_vertical_offsets_(other.has_vertical_offsets_) {
   runs_.ReserveCapacity(other.runs_.size());
   for (const auto& run : other.runs_)
-    runs_.push_back(WTF::WrapUnique(new ShapeResult::RunInfo(*run)));
+    runs_.push_back(base::MakeUnique<RunInfo>(*run));
 }
 
 ShapeResult::~ShapeResult() {}
@@ -447,11 +447,11 @@
       unsigned end = std::min(end_offset - run_start, run_end);
       DCHECK(end > start);
 
-      ShapeResult::RunInfo* sub_run = (*run).CreateSubRun(start, end);
+      auto sub_run = (*run).CreateSubRun(start, end);
       sub_run->start_index_ = index;
-      target->runs_.push_back(WTF::WrapUnique(sub_run));
       target->width_ += sub_run->width_;
       index += sub_run->num_characters_;
+      target->runs_.push_back(std::move(sub_run));
     }
   }
 
@@ -466,10 +466,9 @@
   const SimpleFontData* font_data = font->PrimaryFont();
   // Tab characters are always LTR or RTL, not TTB, even when
   // isVerticalAnyUpright().
-  std::unique_ptr<ShapeResult::RunInfo> run =
-      WTF::WrapUnique(new ShapeResult::RunInfo(
-          font_data, text_run.Rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR,
-          HB_SCRIPT_COMMON, 0, count, count));
+  std::unique_ptr<ShapeResult::RunInfo> run = base::MakeUnique<RunInfo>(
+      font_data, text_run.Rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR,
+      HB_SCRIPT_COMMON, 0, count, count);
   float position = text_run.XPos() + position_offset;
   float start_position = position;
   for (unsigned i = 0; i < count; i++) {
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
index 6fa7635..8337bb8 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
@@ -99,7 +99,7 @@
   }
 
   // Creates a new RunInfo instance representing a subset of the current run.
-  RunInfo* CreateSubRun(unsigned start, unsigned end) {
+  std::unique_ptr<RunInfo> CreateSubRun(unsigned start, unsigned end) {
     DCHECK(end > start);
     unsigned number_of_characters = std::min(end - start, num_characters_);
 
@@ -121,9 +121,9 @@
           });
     }
 
-    RunInfo* run =
-        new RunInfo(font_data_.Get(), direction_, script_, start_index_ + start,
-                    number_of_glyphs, number_of_characters);
+    auto run = base::MakeUnique<RunInfo>(font_data_.Get(), direction_, script_,
+                                         start_index_ + start, number_of_glyphs,
+                                         number_of_characters);
 
     unsigned sub_glyph_index = 0;
     float total_advance = 0;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
index acc92e2..944abccd 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
@@ -296,6 +296,7 @@
 bool DrawingBuffer::FinishPrepareTextureMailboxSoftware(
     cc::TextureMailbox* out_mailbox,
     std::unique_ptr<cc::SingleReleaseCallback>* out_release_callback) {
+  DCHECK(state_restorer_);
   std::unique_ptr<cc::SharedBitmap> bitmap = CreateOrRecycleBitmap();
   if (!bitmap)
     return false;
@@ -308,6 +309,8 @@
     WebGLImageConversion::AlphaOp op =
         need_premultiply ? WebGLImageConversion::kAlphaDoPremultiply
                          : WebGLImageConversion::kAlphaDoNothing;
+    state_restorer_->SetFramebufferBindingDirty();
+    gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
     ReadBackFramebuffer(pixels, Size().Width(), Size().Height(), kReadbackSkia,
                         op);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
index edcd8be5..ade6f6f 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
@@ -53,8 +53,10 @@
         provider = WTF::WrapUnique(
             new WebGraphicsContext3DProviderSoftwareRenderingForTests(
                 std::move(gl)));
+    GLES2InterfaceForTests* gl_ =
+        static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     drawing_buffer_ = DrawingBufferForTests::Create(
-        std::move(provider), nullptr, initial_size, DrawingBuffer::kPreserve,
+        std::move(provider), gl_, initial_size, DrawingBuffer::kPreserve,
         kDisableMultisampling);
     CHECK(drawing_buffer_);
   }
@@ -63,7 +65,7 @@
   bool is_software_rendering_ = false;
 };
 
-TEST_F(DrawingBufferSoftwareRenderingTest, bitmapRecycling) {
+TEST_F(DrawingBufferSoftwareRenderingTest, BitmapRecycling) {
   cc::TextureMailbox texture_mailbox;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback2;
@@ -101,5 +103,29 @@
   drawing_buffer_->BeginDestruction();
 }
 
+TEST_F(DrawingBufferSoftwareRenderingTest, FramebufferBinding) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+  cc::TextureMailbox texture_mailbox;
+  std::unique_ptr<cc::SingleReleaseCallback> release_callback;
+  IntSize initial_size(kInitialWidth, kInitialHeight);
+  GLint drawBinding = 0, readBinding = 0;
+
+  GLuint draw_framebuffer_binding = 0xbeef3;
+  GLuint read_framebuffer_binding = 0xbeef4;
+  gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
+  gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
+  gl_->SaveState();
+  drawing_buffer_->Resize(initial_size);
+  drawing_buffer_->MarkContentsChanged();
+  drawing_buffer_->PrepareTextureMailbox(&texture_mailbox, &release_callback);
+  gl_->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawBinding);
+  gl_->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readBinding);
+  EXPECT_EQ(static_cast<GLint>(draw_framebuffer_binding), drawBinding);
+  EXPECT_EQ(static_cast<GLint>(read_framebuffer_binding), readBinding);
+  release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+  drawing_buffer_->BeginDestruction();
+}
+
 }  // unnamed namespace
 }  // blink
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
index 6cd8201..9cbaccf 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
@@ -73,21 +73,23 @@
     IntSize initial_size(kInitialWidth, kInitialHeight);
     std::unique_ptr<GLES2InterfaceForTests> gl =
         WTF::WrapUnique(new GLES2InterfaceForTests);
-    gl_ = gl.get();
-    SetAndSaveRestoreState(false);
     std::unique_ptr<WebGraphicsContext3DProviderForTests> provider =
         WTF::WrapUnique(
             new WebGraphicsContext3DProviderForTests(std::move(gl)));
+    GLES2InterfaceForTests* gl_ =
+        static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     drawing_buffer_ = DrawingBufferForTests::Create(
         std::move(provider), gl_, initial_size, DrawingBuffer::kPreserve,
         use_multisampling);
     CHECK(drawing_buffer_);
+    SetAndSaveRestoreState(false);
   }
 
   // Initialize GL state with unusual values, to verify that they are restored.
   // The |invert| parameter will reverse all boolean parameters, so that all
   // values are tested.
   void SetAndSaveRestoreState(bool invert) {
+    GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
     GLboolean scissor_enabled = !invert;
     GLfloat clear_color[4] = {0.1, 0.2, 0.3, 0.4};
     GLfloat clear_depth = 0.8;
@@ -124,9 +126,11 @@
     gl_->SaveState();
   }
 
-  void VerifyStateWasRestored() { gl_->VerifyStateHasNotChangedSinceSave(); }
+  void VerifyStateWasRestored() {
+    GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+    gl_->VerifyStateHasNotChangedSinceSave();
+  }
 
-  GLES2InterfaceForTests* gl_;
   RefPtr<DrawingBufferForTests> drawing_buffer_;
 };
 
@@ -152,6 +156,7 @@
 }
 
 TEST_F(DrawingBufferTest, verifyResizingProperlyAffectsMailboxes) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
   VerifyStateWasRestored();
   cc::TextureMailbox texture_mailbox;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback;
@@ -349,6 +354,7 @@
 }
 
 TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncTokenCorrectly) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
   cc::TextureMailbox texture_mailbox;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback;
 
@@ -391,18 +397,19 @@
     IntSize initial_size(kInitialWidth, kInitialHeight);
     std::unique_ptr<GLES2InterfaceForTests> gl =
         WTF::WrapUnique(new GLES2InterfaceForTests);
-    gl_ = gl.get();
-    SetAndSaveRestoreState(true);
     std::unique_ptr<WebGraphicsContext3DProviderForTests> provider =
         WTF::WrapUnique(
             new WebGraphicsContext3DProviderForTests(std::move(gl)));
     RuntimeEnabledFeatures::setWebGLImageChromiumEnabled(true);
+    GLES2InterfaceForTests* gl_ =
+        static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     image_id0_ = gl_->NextImageIdToBeCreated();
     EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
     drawing_buffer_ = DrawingBufferForTests::Create(
         std::move(provider), gl_, initial_size, DrawingBuffer::kPreserve,
         kDisableMultisampling);
     CHECK(drawing_buffer_);
+    SetAndSaveRestoreState(true);
     testing::Mock::VerifyAndClearExpectations(gl_);
   }
 
@@ -416,6 +423,7 @@
 };
 
 TEST_F(DrawingBufferImageChromiumTest, verifyResizingReallocatesImages) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
   cc::TextureMailbox texture_mailbox;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback;
 
@@ -497,6 +505,7 @@
 }
 
 TEST_F(DrawingBufferImageChromiumTest, allocationFailure) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
   cc::TextureMailbox texture_mailbox1;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
   cc::TextureMailbox texture_mailbox2;
@@ -695,6 +704,7 @@
 }
 
 TEST_F(DrawingBufferTest, verifySetIsHiddenProperlyAffectsMailboxes) {
+  GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
   cc::TextureMailbox texture_mailbox;
   std::unique_ptr<cc::SingleReleaseCallback> release_callback;
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTestHelpers.h b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTestHelpers.h
index c231f09..b98717b 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTestHelpers.h
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTestHelpers.h
@@ -24,58 +24,6 @@
   kEnableMultisampling,
 };
 
-class DrawingBufferForTests : public DrawingBuffer {
- public:
-  static PassRefPtr<DrawingBufferForTests> Create(
-      std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
-      DrawingBuffer::Client* client,
-      const IntSize& size,
-      PreserveDrawingBuffer preserve,
-      UseMultisampling use_multisampling) {
-    std::unique_ptr<Extensions3DUtil> extensions_util =
-        Extensions3DUtil::Create(context_provider->ContextGL());
-    RefPtr<DrawingBufferForTests> drawing_buffer =
-        AdoptRef(new DrawingBufferForTests(std::move(context_provider),
-                                           std::move(extensions_util), client,
-                                           preserve));
-    if (!drawing_buffer->Initialize(
-            size, use_multisampling != kDisableMultisampling)) {
-      drawing_buffer->BeginDestruction();
-      return nullptr;
-    }
-    return drawing_buffer.Release();
-  }
-
-  DrawingBufferForTests(
-      std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
-      std::unique_ptr<Extensions3DUtil> extensions_util,
-      DrawingBuffer::Client* client,
-      PreserveDrawingBuffer preserve)
-      : DrawingBuffer(
-            std::move(context_provider),
-            std::move(extensions_util),
-            client,
-            false /* discardFramebufferSupported */,
-            true /* wantAlphaChannel */,
-            false /* premultipliedAlpha */,
-            preserve,
-            kWebGL1,
-            false /* wantDepth */,
-            false /* wantStencil */,
-            DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
-            CanvasColorParams()),
-        live_(0) {}
-
-  ~DrawingBufferForTests() override {
-    if (live_)
-      *live_ = false;
-  }
-
-  bool* live_;
-
-  int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
-};
-
 class WebGraphicsContext3DProviderForTests
     : public WebGraphicsContext3DProvider {
  public:
@@ -214,8 +162,18 @@
   }
 
   void GetIntegerv(GLenum pname, GLint* value) override {
-    if (pname == GL_MAX_TEXTURE_SIZE)
-      *value = 1024;
+    switch (pname) {
+      case GL_DRAW_FRAMEBUFFER_BINDING:
+        *value = state_.draw_framebuffer_binding;
+        break;
+      case GL_READ_FRAMEBUFFER_BINDING:
+        *value = state_.read_framebuffer_binding;
+        break;
+      case GL_MAX_TEXTURE_SIZE:
+        *value = 1024;
+      default:
+        break;
+    }
   }
 
   void GenMailboxCHROMIUM(GLbyte* mailbox) override {
@@ -408,4 +366,60 @@
   HashMap<GLuint, GLuint> image_to_texture_map_;
 };
 
+class DrawingBufferForTests : public DrawingBuffer {
+ public:
+  static PassRefPtr<DrawingBufferForTests> Create(
+      std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+      DrawingBuffer::Client* client,
+      const IntSize& size,
+      PreserveDrawingBuffer preserve,
+      UseMultisampling use_multisampling) {
+    std::unique_ptr<Extensions3DUtil> extensions_util =
+        Extensions3DUtil::Create(context_provider->ContextGL());
+    RefPtr<DrawingBufferForTests> drawing_buffer =
+        AdoptRef(new DrawingBufferForTests(std::move(context_provider),
+                                           std::move(extensions_util), client,
+                                           preserve));
+    if (!drawing_buffer->Initialize(
+            size, use_multisampling != kDisableMultisampling)) {
+      drawing_buffer->BeginDestruction();
+      return nullptr;
+    }
+    return drawing_buffer.Release();
+  }
+
+  DrawingBufferForTests(
+      std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+      std::unique_ptr<Extensions3DUtil> extensions_util,
+      DrawingBuffer::Client* client,
+      PreserveDrawingBuffer preserve)
+      : DrawingBuffer(
+            std::move(context_provider),
+            std::move(extensions_util),
+            client,
+            false /* discardFramebufferSupported */,
+            true /* wantAlphaChannel */,
+            false /* premultipliedAlpha */,
+            preserve,
+            kWebGL1,
+            false /* wantDepth */,
+            false /* wantStencil */,
+            DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
+            CanvasColorParams()),
+        live_(0) {}
+
+  ~DrawingBufferForTests() override {
+    if (live_)
+      *live_ = false;
+  }
+
+  GLES2InterfaceForTests* ContextGLForTests() {
+    return static_cast<GLES2InterfaceForTests*>(ContextGL());
+  }
+
+  bool* live_;
+
+  int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
+};
+
 }  // blink
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
index 6023efa..a72b4e1 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -92,10 +92,6 @@
   return WebVector<WebIconURL>();
 }
 
-void WebRemoteFrameImpl::SetCanHaveScrollbars(bool) {
-  NOTREACHED();
-}
-
 WebSize WebRemoteFrameImpl::GetScrollOffset() const {
   NOTREACHED();
   return WebSize();
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
index 5544cee..b8dbf1d 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
@@ -35,7 +35,6 @@
   WebString AssignedName() const override;
   void SetName(const WebString&) override;
   WebVector<WebIconURL> IconURLs(int icon_types_mask) const override;
-  void SetCanHaveScrollbars(bool) override;
   WebSize GetScrollOffset() const override;
   void SetScrollOffset(const WebSize&) override;
   WebSize ContentsSize() const override;
diff --git a/third_party/WebKit/public/platform/UseCounterFeature.def b/third_party/WebKit/public/platform/UseCounterFeature.def
index 1fac9d4..2d9b84e 100644
--- a/third_party/WebKit/public/platform/UseCounterFeature.def
+++ b/third_party/WebKit/public/platform/UseCounterFeature.def
@@ -1552,7 +1552,8 @@
 kCSSRegisterProperty = 2018,
 kRelativeOrientationSensorConstructor = 2019,
 kSmoothScrollJSInterventionActivated = 2020,
-
+kBudgetAPIGetCost = 2021,
+kBudgetAPIGetBudget = 2022,
 // Add new features immediately above this line. Don't change assigned
 // numbers of any item, and don't reuse removed slots.
 // Also, run update_use_counter_feature_enum.py in
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 7f244d8..434a862214 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -160,9 +160,6 @@
   // NOTE: These routines do not force page layout so their results may
   // not be accurate if the page layout is out-of-date.
 
-  // If set to false, do not draw scrollbars on this frame's view.
-  virtual void SetCanHaveScrollbars(bool) = 0;
-
   // The scroll offset from the top-left corner of the frame in pixels.
   virtual WebSize GetScrollOffset() const = 0;
   virtual void SetScrollOffset(const WebSize&) = 0;
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 2d01d5b..ead2f68 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -572,6 +572,11 @@
   // frame is attached to a document.
   virtual std::unique_ptr<WebURLLoader> CreateURLLoader() = 0;
 
+  // Geometry -----------------------------------------------------------------
+
+  // If set to false, do not draw scrollbars on this frame's view.
+  virtual void SetCanHaveScrollbars(bool) = 0;
+
  protected:
   explicit WebLocalFrame(WebTreeScopeType scope) : WebFrame(scope) {}
 
diff --git a/third_party/closure_compiler/externs/accessibility_private.js b/third_party/closure_compiler/externs/accessibility_private.js
index 43fd60d8..9efc7c60 100644
--- a/third_party/closure_compiler/externs/accessibility_private.js
+++ b/third_party/closure_compiler/externs/accessibility_private.js
@@ -102,6 +102,14 @@
 chrome.accessibilityPrivate.darkenScreen = function(enabled) {};
 
 /**
+ * Change the keyboard keys captured by Switch Access.
+ * @param {!Array<number>} key_codes The key codes for the keys that will be
+ *     captured.
+ * @see https://developer.chrome.com/extensions/accessibilityPrivate#method-setSwitchAccessKeys
+ */
+chrome.accessibilityPrivate.setSwitchAccessKeys = function(key_codes) {};
+
+/**
  * Fired whenever ChromeVox should output introduction.
  * @type {!ChromeEvent}
  * @see https://developer.chrome.com/extensions/accessibilityPrivate#event-onIntroduceChromeVox
diff --git a/tools/chrome_proxy/webdriver/lite_page.py b/tools/chrome_proxy/webdriver/lite_page.py
index 59b7c40..39b6d43e 100644
--- a/tools/chrome_proxy/webdriver/lite_page.py
+++ b/tools/chrome_proxy/webdriver/lite_page.py
@@ -14,26 +14,36 @@
   # directive is provided when always-on.
   def testLitePageForcedExperiment(self):
     # If it was attempted to run with another experiment, skip this test.
+    if common.ParseFlags().browser_args and ('--data-reduction-proxy-experiment'
+        in common.ParseFlags().browser_args):
+      self.skipTest('This test cannot be run with other experiments.')
     with TestDriver() as test_driver:
       test_driver.AddChromeArg('--enable-spdy-proxy-auth')
       test_driver.AddChromeArg('--data-reduction-proxy-lo-fi=always-on')
       test_driver.AddChromeArg('--enable-data-reduction-proxy-lite-page')
 
+      # Force ECT to be 4G to confirm that we get Lite Page even for fast
+      # conneciton.
+      test_driver.AddChromeArg('--force-fieldtrial-params='
+                               'NetworkQualityEstimator.Enabled:'
+                               'force_effective_connection_type/4G')
+      test_driver.AddChromeArg('--force-fieldtrials='
+                               'NetworkQualityEstimator/Enabled/')
+
       test_driver.LoadURL('http://check.googlezip.net/test.html')
 
       lite_page_responses = 0
       for response in test_driver.GetHTTPResponses():
+        # Verify client sends force directive on every request for session.
+        self.assertIn('exp=force_lite_page',
+          response.request_headers['chrome-proxy'])
+        self.assertEqual('4G', response.request_headers['chrome-proxy-ect'])
         # Skip CSI requests when validating Lite Page headers. CSI requests
         # aren't expected to have LoFi headers.
         if '/csi?' in response.url:
           continue
         if response.url.startswith('data:'):
           continue
-        if not common.ParseFlags().browser_args or (
-          '--data-reduction-proxy-experiment' not in
-          common.ParseFlags().browser_args):
-            self.assertIn('exp=force_lite_page',
-              response.request_headers['chrome-proxy'])
         if (self.checkLitePageResponse(response)):
           lite_page_responses = lite_page_responses + 1
 
@@ -44,8 +54,12 @@
   # of the page and is able to load all resources.
   def testLitePageBTF(self):
     # If it was attempted to run with another experiment, skip this test.
+    if common.ParseFlags().browser_args and ('--data-reduction-proxy-experiment'
+        in common.ParseFlags().browser_args):
+      self.skipTest('This test cannot be run with other experiments.')
     with TestDriver() as test_driver:
       test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+      # Need to force lite page so target page doesn't fallback to Lo-Fi
       test_driver.AddChromeArg('--data-reduction-proxy-lo-fi=always-on')
       test_driver.AddChromeArg('--enable-data-reduction-proxy-lite-page')
 
@@ -61,11 +75,6 @@
           continue
         if response.url.startswith('data:'):
           continue
-        if not common.ParseFlags().browser_args or (
-          '--data-reduction-proxy-experiment' not in
-          common.ParseFlags().browser_args):
-            self.assertIn('exp=force_lite_page',
-              response.request_headers['chrome-proxy'])
         if (self.checkLitePageResponse(response)):
           lite_page_responses = lite_page_responses + 1
       self.assertEqual(1, lite_page_responses)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 0fcf89a..715c4c6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1749,6 +1749,232 @@
   <int value="17" label="fp05cc03e1"/>
 </enum>
 
+<enum name="AutofillFieldPredictionQuality" type="int">
+  <int value="0" label="True Positive"/>
+  <int value="1" label="True Negative (Ambiguous)"/>
+  <int value="2" label="True Negative (Unknown)"/>
+  <int value="3" label="True Negative (Empty)"/>
+  <int value="4" label="False Positive (Mismatch)"/>
+  <int value="5" label="False Positive (Ambiguous)"/>
+  <int value="6" label="False Positive (Unknown)"/>
+  <int value="7" label="False Positive (Empty)"/>
+  <int value="8" label="False Negative (Mismatch)"/>
+  <int value="9" label="False Negative (Unknown)"/>
+</enum>
+
+<enum name="AutofillFieldPredictionQualityByFieldType" type="int">
+  <int value="0" label="Ambiguous: True Positive"/>
+  <int value="1" label="Ambiguous: True Negative (Ambiguous)"/>
+  <int value="2" label="Ambiguous: True Negative (Unknown)"/>
+  <int value="3" label="Ambiguous: True Negative (Empty)"/>
+  <int value="4" label="Ambiguous: False Positive (Mismatch)"/>
+  <int value="5" label="Ambiguous: False Positive (Ambiguous)"/>
+  <int value="6" label="Ambiguous: False Positive (Unknown)"/>
+  <int value="7" label="Ambiguous: False Positive (Empty)"/>
+  <int value="8" label="Ambiguous: False Negative (Mismatch)"/>
+  <int value="9" label="Ambiguous: False Negative (Unknown)"/>
+  <int value="256" label="Name: True Positive"/>
+  <int value="257" label="Name: True Negative (Ambiguous)"/>
+  <int value="258" label="Name: True Negative (Unknown)"/>
+  <int value="259" label="Name: True Negative (Empty)"/>
+  <int value="260" label="Name: False Positive (Mismatch)"/>
+  <int value="261" label="Name: False Positive (Ambiguous)"/>
+  <int value="262" label="Name: False Positive (Unknown)"/>
+  <int value="263" label="Name: False Positive (Empty)"/>
+  <int value="264" label="Name: False Negative (Mismatch)"/>
+  <int value="265" label="Name: False Negative (Unknown)"/>
+  <int value="512" label="Company: True Positive"/>
+  <int value="513" label="Company: True Negative (Ambiguous)"/>
+  <int value="514" label="Company: True Negative (Unknown)"/>
+  <int value="515" label="Company: True Negative (Empty)"/>
+  <int value="516" label="Company: False Positive (Mismatch)"/>
+  <int value="517" label="Company: False Positive (Ambiguous)"/>
+  <int value="518" label="Company: False Positive (Unknown)"/>
+  <int value="519" label="Company: False Positive (Empty)"/>
+  <int value="520" label="Company: False Negative (Mismatch)"/>
+  <int value="521" label="Company: False Negative (Unknown)"/>
+  <int value="768" label="Address Line 1: True Positive"/>
+  <int value="769" label="Address Line 1: True Negative (Ambiguous)"/>
+  <int value="770" label="Address Line 1: True Negative (Unknown)"/>
+  <int value="771" label="Address Line 1: True Negative (Empty)"/>
+  <int value="772" label="Address Line 1: False Positive (Mismatch)"/>
+  <int value="773" label="Address Line 1: False Positive (Ambiguous)"/>
+  <int value="774" label="Address Line 1: False Positive (Unknown)"/>
+  <int value="775" label="Address Line 1: False Positive (Empty)"/>
+  <int value="776" label="Address Line 1: False Negative (Mismatch)"/>
+  <int value="777" label="Address Line 1: False Negative (Unknown)"/>
+  <int value="1024" label="Address Line 2: True Positive"/>
+  <int value="1025" label="Address Line 2: True Negative (Ambiguous)"/>
+  <int value="1026" label="Address Line 2: True Negative (Unknown)"/>
+  <int value="1027" label="Address Line 2: True Negative (Empty)"/>
+  <int value="1028" label="Address Line 2: False Positive (Mismatch)"/>
+  <int value="1029" label="Address Line 2: False Positive (Ambiguous)"/>
+  <int value="1030" label="Address Line 2: False Positive (Unknown)"/>
+  <int value="1031" label="Address Line 2: False Positive (Empty)"/>
+  <int value="1032" label="Address Line 2: False Negative (Mismatch)"/>
+  <int value="1033" label="Address Line 2: False Negative (Unknown)"/>
+  <int value="1280" label="City: True Positive"/>
+  <int value="1281" label="City: True Negative (Ambiguous)"/>
+  <int value="1282" label="City: True Negative (Unknown)"/>
+  <int value="1283" label="City: True Negative (Empty)"/>
+  <int value="1284" label="City: False Positive (Mismatch)"/>
+  <int value="1285" label="City: False Positive (Ambiguous)"/>
+  <int value="1286" label="City: False Positive (Unknown)"/>
+  <int value="1287" label="City: False Positive (Empty)"/>
+  <int value="1288" label="City: False Negative (Mismatch)"/>
+  <int value="1289" label="City: False Negative (Unknown)"/>
+  <int value="1536" label="State: True Positive"/>
+  <int value="1537" label="State: True Negative (Ambiguous)"/>
+  <int value="1538" label="State: True Negative (Unknown)"/>
+  <int value="1539" label="State: True Negative (Empty)"/>
+  <int value="1540" label="State: False Positive (Mismatch)"/>
+  <int value="1541" label="State: False Positive (Ambiguous)"/>
+  <int value="1542" label="State: False Positive (Unknown)"/>
+  <int value="1543" label="State: False Positive (Empty)"/>
+  <int value="1544" label="State: False Negative (Mismatch)"/>
+  <int value="1545" label="State: False Negative (Unknown)"/>
+  <int value="1792" label="ZIP: True Positive"/>
+  <int value="1793" label="ZIP: True Negative (Ambiguous)"/>
+  <int value="1794" label="ZIP: True Negative (Unknown)"/>
+  <int value="1795" label="ZIP: True Negative (Empty)"/>
+  <int value="1796" label="ZIP: False Positive (Mismatch)"/>
+  <int value="1797" label="ZIP: False Positive (Ambiguous)"/>
+  <int value="1798" label="ZIP: False Positive (Unknown)"/>
+  <int value="1799" label="ZIP: False Positive (Empty)"/>
+  <int value="1800" label="ZIP: False Negative (Mismatch)"/>
+  <int value="1801" label="ZIP: False Negative (Unknown)"/>
+  <int value="2048" label="Country: True Positive"/>
+  <int value="2049" label="Country: True Negative (Ambiguous)"/>
+  <int value="2050" label="Country: True Negative (Unknown)"/>
+  <int value="2051" label="Country: True Negative (Empty)"/>
+  <int value="2052" label="Country: False Positive (Mismatch)"/>
+  <int value="2053" label="Country: False Positive (Ambiguous)"/>
+  <int value="2054" label="Country: False Positive (Unknown)"/>
+  <int value="2055" label="Country: False Positive (Empty)"/>
+  <int value="2056" label="Country: False Negative (Mismatch)"/>
+  <int value="2057" label="Country: False Negative (Unknown)"/>
+  <int value="2304" label="Phone: True Positive"/>
+  <int value="2305" label="Phone: True Negative (Ambiguous)"/>
+  <int value="2306" label="Phone: True Negative (Unknown)"/>
+  <int value="2307" label="Phone: True Negative (Empty)"/>
+  <int value="2308" label="Phone: False Positive (Mismatch)"/>
+  <int value="2309" label="Phone: False Positive (Ambiguous)"/>
+  <int value="2310" label="Phone: False Positive (Unknown)"/>
+  <int value="2311" label="Phone: False Positive (Empty)"/>
+  <int value="2312" label="Phone: False Negative (Mismatch)"/>
+  <int value="2313" label="Phone: False Negative (Unknown)"/>
+  <int value="2560" label="Fax: True Positive"/>
+  <int value="2561" label="Fax: True Negative (Ambiguous)"/>
+  <int value="2562" label="Fax: True Negative (Unknown)"/>
+  <int value="2563" label="Fax: True Negative (Empty)"/>
+  <int value="2564" label="Fax: False Positive (Mismatch)"/>
+  <int value="2565" label="Fax: False Positive (Ambiguous)"/>
+  <int value="2566" label="Fax: False Positive (Unknown)"/>
+  <int value="2567" label="Fax: False Positive (Empty)"/>
+  <int value="2568" label="Fax: False Negative (Mismatch)"/>
+  <int value="2569" label="Fax: False Negative (Unknown)"/>
+  <int value="2816" label="Email: True Positive"/>
+  <int value="2817" label="Email: True Negative (Ambiguous)"/>
+  <int value="2818" label="Email: True Negative (Unknown)"/>
+  <int value="2819" label="Email: True Negative (Empty)"/>
+  <int value="2820" label="Email: False Positive (Mismatch)"/>
+  <int value="2821" label="Email: False Positive (Ambiguous)"/>
+  <int value="2822" label="Email: False Positive (Unknown)"/>
+  <int value="2823" label="Email: False Positive (Empty)"/>
+  <int value="2824" label="Email: False Negative (Mismatch)"/>
+  <int value="2825" label="Email: False Negative (Unknown)"/>
+  <int value="3072" label="CC Name: True Positive"/>
+  <int value="3073" label="CC Name: True Negative (Ambiguous)"/>
+  <int value="3074" label="CC Name: True Negative (Unknown)"/>
+  <int value="3075" label="CC Name: True Negative (Empty)"/>
+  <int value="3076" label="CC Name: False Positive (Mismatch)"/>
+  <int value="3077" label="CC Name: False Positive (Ambiguous)"/>
+  <int value="3078" label="CC Name: False Positive (Unknown)"/>
+  <int value="3079" label="CC Name: False Positive (Empty)"/>
+  <int value="3080" label="CC Name: False Negative (Mismatch)"/>
+  <int value="3081" label="CC Name: False Negative (Unknown)"/>
+  <int value="3328" label="CC Number: True Positive"/>
+  <int value="3329" label="CC Number: True Negative (Ambiguous)"/>
+  <int value="3330" label="CC Number: True Negative (Unknown)"/>
+  <int value="3331" label="CC Number: True Negative (Empty)"/>
+  <int value="3332" label="CC Number: False Positive (Mismatch)"/>
+  <int value="3333" label="CC Number: False Positive (Ambiguous)"/>
+  <int value="3334" label="CC Number: False Positive (Unknown)"/>
+  <int value="3335" label="CC Number: False Positive (Empty)"/>
+  <int value="3336" label="CC Number: False Negative (Mismatch)"/>
+  <int value="3337" label="CC Number: False Negative (Unknown)"/>
+  <int value="3584" label="CC Expiry: True Positive"/>
+  <int value="3585" label="CC Expiry: True Negative (Ambiguous)"/>
+  <int value="3586" label="CC Expiry: True Negative (Unknown)"/>
+  <int value="3587" label="CC Expiry: True Negative (Empty)"/>
+  <int value="3588" label="CC Expiry: False Positive (Mismatch)"/>
+  <int value="3589" label="CC Expiry: False Positive (Ambiguous)"/>
+  <int value="3590" label="CC Expiry: False Positive (Unknown)"/>
+  <int value="3591" label="CC Expiry: False Positive (Empty)"/>
+  <int value="3592" label="CC Expiry: False Negative (Mismatch)"/>
+  <int value="3593" label="CC Expiry: False Negative (Unknown)"/>
+  <int value="3840" label="CC Type: True Positive"/>
+  <int value="3841" label="CC Type: True Negative (Ambiguous)"/>
+  <int value="3842" label="CC Type: True Negative (Unknown)"/>
+  <int value="3843" label="CC Type: True Negative (Empty)"/>
+  <int value="3844" label="CC Type: False Positive (Mismatch)"/>
+  <int value="3845" label="CC Type: False Positive (Ambiguous)"/>
+  <int value="3846" label="CC Type: False Positive (Unknown)"/>
+  <int value="3847" label="CC Type: False Positive (Empty)"/>
+  <int value="3848" label="CC Type: False Negative (Mismatch)"/>
+  <int value="3849" label="CC Type: False Negative (Unknown)"/>
+  <int value="4096" label="Password: True Positive"/>
+  <int value="4097" label="Password: True Negative (Ambiguous)"/>
+  <int value="4098" label="Password: True Negative (Unknown)"/>
+  <int value="4099" label="Password: True Negative (Empty)"/>
+  <int value="4100" label="Password: False Positive (Mismatch)"/>
+  <int value="4101" label="Password: False Positive (Ambiguous)"/>
+  <int value="4102" label="Password: False Positive (Unknown)"/>
+  <int value="4103" label="Password: False Positive (Empty)"/>
+  <int value="4104" label="Password: False Negative (Mismatch)"/>
+  <int value="4105" label="Password: False Negative (Unknown)"/>
+  <int value="4352" label="Address Line 3: True Positive"/>
+  <int value="4353" label="Address Line 3: True Negative (Ambiguous)"/>
+  <int value="4354" label="Address Line 3: True Negative (Unknown)"/>
+  <int value="4355" label="Address Line 3: True Negative (Empty)"/>
+  <int value="4356" label="Address Line 3: False Positive (Mismatch)"/>
+  <int value="4357" label="Address Line 3: False Positive (Ambiguous)"/>
+  <int value="4358" label="Address Line 3: False Positive (Unknown)"/>
+  <int value="4359" label="Address Line 3: False Positive (Empty)"/>
+  <int value="4360" label="Address Line 3: False Negative (Mismatch)"/>
+  <int value="4361" label="Address Line 3: False Negative (Unknown)"/>
+  <int value="4608" label="Username: True Positive"/>
+  <int value="4609" label="Username: True Negative (Ambiguous)"/>
+  <int value="4610" label="Username: True Negative (Unknown)"/>
+  <int value="4611" label="Username: True Negative (Empty)"/>
+  <int value="4612" label="Username: False Positive (Mismatch)"/>
+  <int value="4613" label="Username: False Positive (Ambiguous)"/>
+  <int value="4614" label="Username: False Positive (Unknown)"/>
+  <int value="4615" label="Username: False Positive (Empty)"/>
+  <int value="4616" label="Username: False Negative (Mismatch)"/>
+  <int value="4617" label="Username: False Negative (Unknown)"/>
+  <int value="4864" label="Street Address: True Positive"/>
+  <int value="4865" label="Street Address: True Negative (Ambiguous)"/>
+  <int value="4866" label="Street Address: True Negative (Unknown)"/>
+  <int value="4867" label="Street Address: True Negative (Empty)"/>
+  <int value="4868" label="Street Address: False Positive (Mismatch)"/>
+  <int value="4869" label="Street Address: False Positive (Ambiguous)"/>
+  <int value="4870" label="Street Address: False Positive (Unknown)"/>
+  <int value="4871" label="Street Address: False Positive (Empty)"/>
+  <int value="4872" label="Street Address: False Negative (Mismatch)"/>
+  <int value="4873" label="Street Address: False Negative (Unknown)"/>
+  <int value="5120" label="CVC: True Positive"/>
+  <int value="5121" label="CVC: True Negative (Ambiguous)"/>
+  <int value="5122" label="CVC: True Negative (Unknown)"/>
+  <int value="5123" label="CVC: True Negative (Empty)"/>
+  <int value="5124" label="CVC: False Positive (Mismatch)"/>
+  <int value="5125" label="CVC: False Positive (Ambiguous)"/>
+  <int value="5126" label="CVC: False Positive (Unknown)"/>
+  <int value="5127" label="CVC: False Positive (Empty)"/>
+  <int value="5128" label="CVC: False Negative (Mismatch)"/>
+  <int value="5129" label="CVC: False Negative (Unknown)"/>
+</enum>
+
 <enum name="AutofillFormEvent" type="int">
   <int value="0" label="Interacted (once)"/>
   <int value="1" label="Suggestions shown"/>
@@ -12422,6 +12648,7 @@
   <int value="1175" label="MEDIAPERCEPTIONPRIVATE_SETSTATE"/>
   <int value="1176" label="MEDIAPERCEPTIONPRIVATE_GETDIAGNOSTICS"/>
   <int value="1177" label="NETWORKINGPRIVATE_GETCERTIFICATELISTS"/>
+  <int value="1178" label="ACCESSIBILITY_PRIVATE_SETSWITCHACCESSKEYS"/>
 </enum>
 
 <enum name="ExtensionIconState" type="int">
@@ -15135,6 +15362,8 @@
   <int value="2018" label="CSSRegisterProperty"/>
   <int value="2019" label="RelativeOrientationSensorConstructor"/>
   <int value="2020" label="SmoothScrollJSInterventionActivated"/>
+  <int value="2021" label="BudgetAPIGetCost"/>
+  <int value="2022" label="BudgetAPIGetBudget"/>
 </enum>
 
 <enum name="FeedbackSource" type="int">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2970b8f..9bbb211 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3880,6 +3880,44 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.FieldPrediction">
+<!-- Name completed by histogram_suffixes
+     name="AutofillFieldPredictionSource" -->
+
+  <owner>rogerm@chromium.org</owner>
+  <summary>
+    Predicted and actual form field type. This is a computed sparse histogram
+    where the value is ((predicted &lt;&lt; 16)| actual). Predicted and actual
+    are taken from the autofill::ServerFieldType enumeration.
+  </summary>
+</histogram>
+
+<histogram name="Autofill.FieldPredictionQuality.Aggregate"
+    enum="AutofillFieldPredictionQuality">
+<!-- Name completed by histogram_suffixes
+     name="AutofillFieldPredictionSource" -->
+
+  <owner>rogerm@chromium.org</owner>
+  <summary>
+    Aggregate Autofill field-type prediction outcomes. See
+    https://en.wikipedia.org/wiki/Confusion_matrix for an explanation of the
+    values.
+  </summary>
+</histogram>
+
+<histogram name="Autofill.FieldPredictionQuality.ByFieldType"
+    enum="AutofillFieldPredictionQualityByFieldType">
+<!-- Name completed by histogram_suffixes
+     name="AutofillFieldPredictionSource" -->
+
+  <owner>rogerm@chromium.org</owner>
+  <summary>
+    Autofill field-type prediction outcomes, broken down by field type. See
+    https://en.wikipedia.org/wiki/Confusion_matrix for an explanation of the
+    values.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.FillDuration.FromInteraction.WithAutofill">
   <owner>isherman@chromium.org</owner>
   <summary>
@@ -4206,12 +4244,20 @@
 </histogram>
 
 <histogram name="Autofill.Quality.HeuristicType" enum="AutofillTypeQuality">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.Aggregate.Heuristic.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>The quality of Autofill's heuristic field type detection.</summary>
 </histogram>
 
 <histogram name="Autofill.Quality.HeuristicType.ByFieldType"
     enum="AutofillTypeQualityByFieldType">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.ByFieldType.Heuristic.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>
     The quality of Autofill's heuristic field type detection, broken down by the
@@ -4221,12 +4267,20 @@
 </histogram>
 
 <histogram name="Autofill.Quality.PredictedType" enum="AutofillTypeQuality">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.Aggregate.Overall.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>The overall quality of the Autofill field type predictions.</summary>
 </histogram>
 
 <histogram name="Autofill.Quality.PredictedType.ByFieldType"
     enum="AutofillTypeQualityByFieldType">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.ByFieldType.Overall.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>
     The overall quality of the Autofill field type predictions, broken down by
@@ -4236,12 +4290,20 @@
 </histogram>
 
 <histogram name="Autofill.Quality.ServerType" enum="AutofillTypeQuality">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.Aggregate.Server.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>The quality of the Autofill server's field type detection.</summary>
 </histogram>
 
 <histogram name="Autofill.Quality.ServerType.ByFieldType"
     enum="AutofillTypeQualityByFieldType">
+  <obsolete>
+    Deprecated as of 6/2017, replaced by
+    Autofill.FieldPredictionQuality.ByFieldType.Server.
+  </obsolete>
   <owner>isherman@chromium.org</owner>
   <summary>
     The quality of the Autofill server's field type detection, broken down by
@@ -85771,6 +85833,20 @@
   <affected-histogram name="Autofill.FormEvents.CreditCard"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AutofillFieldPredictionSource" separator=".">
+  <suffix name="Heuristic"
+      label="Field type predictions using local heuristics."/>
+  <suffix name="Server"
+      label="Field types based on crowd-sourced prediction fetched from the
+             Autofill Server"/>
+  <suffix name="Overall"
+      label="Field type prediction as seen by the user after combining the
+             heuristic and crowd sourced predictions."/>
+  <affected-histogram name="Autofill.FieldPrediction"/>
+  <affected-histogram name="Autofill.FieldPredictionQuality.Aggregate"/>
+  <affected-histogram name="Autofill.FieldPredictionQuality.ByFieldType"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AutofillPayloadCompressionType" separator=".">
   <suffix name="Query" label="Query request compression"/>
   <suffix name="Upload" label="Upload request compression"/>
@@ -87803,6 +87879,19 @@
   <suffix name="NoSubmission" label="No observed submission."/>
   <suffix name="BasedOnAutocomplete"
       label="Quality metrics based on autocomplete attributes."/>
+  <affected-histogram name="Autofill.FieldPrediction.Heuristic"/>
+  <affected-histogram name="Autofill.FieldPrediction.Overall"/>
+  <affected-histogram name="Autofill.FieldPrediction.Server"/>
+  <affected-histogram
+      name="Autofill.FieldPredictionQuality.Aggregate.Heuristic"/>
+  <affected-histogram name="Autofill.FieldPredictionQuality.Aggregate.Overall"/>
+  <affected-histogram name="Autofill.FieldPredictionQuality.Aggregate.Server"/>
+  <affected-histogram
+      name="Autofill.FieldPredictionQuality.ByFieldType.Heuristic"/>
+  <affected-histogram
+      name="Autofill.FieldPredictionQuality.ByFieldType.Overall"/>
+  <affected-histogram
+      name="Autofill.FieldPredictionQuality.ByFieldType.Server"/>
   <affected-histogram
       name="Autofill.NumberOfEditedAutofilledFieldsAtSubmission"/>
   <affected-histogram name="Autofill.Quality.HeuristicType"/>
diff --git a/tools/perf/benchmarks/kraken.py b/tools/perf/benchmarks/kraken.py
index 1715f62..df0fcda3 100644
--- a/tools/perf/benchmarks/kraken.py
+++ b/tools/perf/benchmarks/kraken.py
@@ -132,10 +132,12 @@
     ps = story.StorySet(
         archive_data_file='../page_sets/data/kraken.json',
         base_dir=os.path.dirname(os.path.abspath(__file__)),
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
     ps.AddStory(page_module.Page(
         'http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html',
-        ps, ps.base_dir))
+        ps, ps.base_dir,
+        name='http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html'))
     return ps
 
   @classmethod
diff --git a/ui/app_list/app_list_model.cc b/ui/app_list/app_list_model.cc
index 6ee9d525..f7653e1 100644
--- a/ui/app_list/app_list_model.cc
+++ b/ui/app_list/app_list_model.cc
@@ -364,11 +364,6 @@
     observer.OnSearchEngineIsGoogleChanged(is_google);
 }
 
-void AppListModel::SetSearchAnswerAvailable(bool has_answer) {
-  for (auto& observer : observers_)
-    observer.OnSearchAnswerAvailableChanged(has_answer);
-}
-
 // Private methods
 
 void AppListModel::OnListItemMoved(size_t from_index,
diff --git a/ui/app_list/app_list_model.h b/ui/app_list/app_list_model.h
index 2a413e7..1d0644a7 100644
--- a/ui/app_list/app_list_model.h
+++ b/ui/app_list/app_list_model.h
@@ -166,8 +166,6 @@
   void SetSearchEngineIsGoogle(bool is_google);
   bool search_engine_is_google() const { return search_engine_is_google_; }
 
-  void SetSearchAnswerAvailable(bool has_answer);
-
   // Filters the given |results| by |display_type|. The returned list is
   // truncated to |max_results|.
   static std::vector<SearchResult*> FilterSearchResultsByDisplayType(
diff --git a/ui/app_list/app_list_model_observer.h b/ui/app_list/app_list_model_observer.h
index fafa2c3..09cccc0 100644
--- a/ui/app_list/app_list_model_observer.h
+++ b/ui/app_list/app_list_model_observer.h
@@ -39,9 +39,6 @@
   // Triggered when the search engine is changed to and from Google.
   virtual void OnSearchEngineIsGoogleChanged(bool is_google) {}
 
-  // Triggered when the availability of the search answer is changed.
-  virtual void OnSearchAnswerAvailableChanged(bool has_answer) {}
-
  protected:
   virtual ~AppListModelObserver() {}
 };
diff --git a/ui/app_list/app_list_view_delegate.h b/ui/app_list/app_list_view_delegate.h
index 9bdf665..3d61cb1 100644
--- a/ui/app_list/app_list_view_delegate.h
+++ b/ui/app_list/app_list_view_delegate.h
@@ -83,10 +83,6 @@
   virtual std::vector<views::View*> CreateCustomPageWebViews(
       const gfx::Size& size) = 0;
 
-  // Gets the web view for the search answer. The caller doesn't take the
-  // ownership of the returned view.
-  virtual views::View* GetSearchAnswerWebView() = 0;
-
   // Invoked when the custom launcher page's animation changes.
   virtual void CustomLauncherPageAnimationChanged(double progress) = 0;
 
diff --git a/ui/app_list/search_result.cc b/ui/app_list/search_result.cc
index 63862ef..e9f1756 100644
--- a/ui/app_list/search_result.cc
+++ b/ui/app_list/search_result.cc
@@ -89,6 +89,7 @@
     case DISPLAY_LIST:
       return kListIconSize;
     case DISPLAY_NONE:
+    case DISPLAY_CARD:
       return 0;
     case DISPLAY_TYPE_LAST:
       break;
diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h
index a3b8fec..d721c7e 100644
--- a/ui/app_list/search_result.h
+++ b/ui/app_list/search_result.h
@@ -21,6 +21,10 @@
 class MenuModel;
 }
 
+namespace views {
+class View;
+}
+
 namespace app_list {
 
 class SearchResultObserver;
@@ -39,6 +43,7 @@
     DISPLAY_LIST,
     DISPLAY_TILE,
     DISPLAY_RECOMMENDATION,
+    DISPLAY_CARD,
     // Add new values here.
 
     DISPLAY_TYPE_LAST,
@@ -108,6 +113,9 @@
   const Tags& details_tags() const { return details_tags_; }
   void set_details_tags(const Tags& tags) { details_tags_ = tags; }
 
+  views::View* view() const { return view_; }
+  void set_view(views::View* view) { view_ = view; }
+
   const std::string& id() const { return id_; }
 
   double relevance() const { return relevance_; }
@@ -187,6 +195,12 @@
   base::string16 details_;
   Tags details_tags_;
 
+  // Unowned pointer to a view containing a rendered result, or nullptr if there
+  // is no such view for the result.
+  // The view has set_owned_by_client() property set. It's a responsibility of
+  // SearchProvider to set this property and own this view.
+  views::View* view_ = nullptr;
+
   std::string id_;
   double relevance_;
   DisplayType display_type_;
diff --git a/ui/app_list/test/app_list_test_view_delegate.cc b/ui/app_list/test/app_list_test_view_delegate.cc
index efef3e22..9202127b 100644
--- a/ui/app_list/test/app_list_test_view_delegate.cc
+++ b/ui/app_list/test/app_list_test_view_delegate.cc
@@ -80,10 +80,6 @@
   return std::vector<views::View*>();
 }
 
-views::View* AppListTestViewDelegate::GetSearchAnswerWebView() {
-  return nullptr;
-}
-
 bool AppListTestViewDelegate::IsSpeechRecognitionEnabled() {
   return false;
 }
diff --git a/ui/app_list/test/app_list_test_view_delegate.h b/ui/app_list/test/app_list_test_view_delegate.h
index 1d4c242..5cafa04f 100644
--- a/ui/app_list/test/app_list_test_view_delegate.h
+++ b/ui/app_list/test/app_list_test_view_delegate.h
@@ -68,7 +68,6 @@
   views::View* CreateStartPageWebView(const gfx::Size& size) override;
   std::vector<views::View*> CreateCustomPageWebViews(
       const gfx::Size& size) override;
-  views::View* GetSearchAnswerWebView() override;
   void CustomLauncherPageAnimationChanged(double progress) override {}
   void CustomLauncherPagePopSubpage() override {}
   bool IsSpeechRecognitionEnabled() override;
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index bb8218b..9a3a73b7 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -9,6 +9,7 @@
 
 #include "base/logging.h"
 #include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/views/app_list_folder_view.h"
@@ -70,15 +71,13 @@
   search_results_page_view_ = new SearchResultPageView();
 
   // Search result containers.
-  views::View* const search_answer_view =
-      view_delegate->GetSearchAnswerWebView();
-  if (search_answer_view) {
+  AppListModel::SearchResults* results = view_delegate->GetModel()->results();
+
+  if (features::IsAnswerCardEnabled()) {
     search_results_page_view_->AddSearchResultContainerView(
-        nullptr, new SearchResultAnswerCardView(
-                     model_, search_results_page_view_, search_answer_view));
+        results, new SearchResultAnswerCardView(view_delegate));
   }
 
-  AppListModel::SearchResults* results = view_delegate->GetModel()->results();
   search_results_page_view_->AddSearchResultContainerView(
       results, new SearchResultListView(app_list_main_view_, view_delegate));
 
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc
index 43ac790..3e36ba06 100644
--- a/ui/app_list/views/search_result_answer_card_view.cc
+++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -6,7 +6,7 @@
 
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
-#include "ui/app_list/views/search_result_page_view.h"
+#include "ui/app_list/app_list_view_delegate.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/custom_button.h"
 #include "ui/views/layout/box_layout.h"
@@ -14,26 +14,20 @@
 
 namespace app_list {
 
-namespace {
-
-// Answer card relevance is high to always have it first.
-constexpr double kSearchAnswerCardRelevance = 100;
-
-}  // namespace
-
 // Container of the search answer view.
 class SearchResultAnswerCardView::SearchAnswerContainerView
-    : public views::CustomButton {
+    : public views::CustomButton,
+      public views::ButtonListener {
  public:
-  explicit SearchAnswerContainerView(views::View* search_results_page_view)
-      : CustomButton(nullptr),
-        search_results_page_view_(search_results_page_view) {
+  explicit SearchAnswerContainerView(AppListViewDelegate* view_delegate)
+      : CustomButton(this), view_delegate_(view_delegate) {
     // Center the card horizontally in the container.
     views::BoxLayout* answer_container_layout =
         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
     answer_container_layout->set_main_axis_alignment(
         views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
     SetLayoutManager(answer_container_layout);
+    SetVisible(false);
   }
 
   void SetSelected(bool selected) {
@@ -43,23 +37,38 @@
     UpdateBackgroundColor();
   }
 
+  void SetSearchResult(SearchResult* search_result) {
+    views::View* const old_result_view = child_count() ? child_at(0) : nullptr;
+    views::View* const new_result_view =
+        search_result ? search_result->view() : nullptr;
+
+    if (old_result_view != new_result_view) {
+      if (old_result_view != nullptr)
+        RemoveChildView(old_result_view);
+      if (new_result_view != nullptr)
+        AddChildView(new_result_view);
+    }
+
+    search_result_ = search_result ? search_result->Duplicate() : nullptr;
+
+    SetVisible(new_result_view != nullptr);
+  }
+
   // views::CustomButton overrides:
-  void ChildPreferredSizeChanged(View* child) override {
-    // Card size changed.
-    if (visible())
-      search_results_page_view_->Layout();
-  }
-
-  int GetHeightForWidth(int w) const override {
-    return visible() ? CustomButton::GetHeightForWidth(w) : 0;
-  }
-
   const char* GetClassName() const override {
     return "SearchAnswerContainerView";
   }
 
   void StateChanged(ButtonState old_state) override { UpdateBackgroundColor(); }
 
+  // views::ButtonListener overrides:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override {
+    DCHECK(sender == this);
+    DCHECK(search_result_);
+    view_delegate_->OpenSearchResult(search_result_.get(), false,
+                                     event.flags());
+  }
+
  private:
   void UpdateBackgroundColor() {
     if (selected_)
@@ -70,29 +79,22 @@
     SchedulePaint();
   }
 
-  views::View* const search_results_page_view_;
+  AppListViewDelegate* const view_delegate_;  // Not owned.
   bool selected_ = false;
+  std::unique_ptr<SearchResult> search_result_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchAnswerContainerView);
 };
 
 SearchResultAnswerCardView::SearchResultAnswerCardView(
-    AppListModel* model,
-    SearchResultPageView* search_results_page_view,
-    views::View* search_answer_view)
-    : model_(model),
-      search_answer_container_view_(
-          new SearchAnswerContainerView(search_results_page_view)) {
-  search_answer_container_view_->SetVisible(false);
-  search_answer_container_view_->AddChildView(search_answer_view);
+    AppListViewDelegate* view_delegate)
+    : search_answer_container_view_(
+          new SearchAnswerContainerView(view_delegate)) {
   AddChildView(search_answer_container_view_);
-  model->AddObserver(this);
   SetLayoutManager(new views::FillLayout);
 }
 
-SearchResultAnswerCardView::~SearchResultAnswerCardView() {
-  model_->RemoveObserver(this);
-}
+SearchResultAnswerCardView::~SearchResultAnswerCardView() {}
 
 const char* SearchResultAnswerCardView::GetClassName() const {
   return "SearchResultAnswerCardView";
@@ -112,8 +114,17 @@
 }
 
 int SearchResultAnswerCardView::DoUpdate() {
-  const bool have_result = search_answer_container_view_->visible();
-  set_container_score(have_result ? kSearchAnswerCardRelevance : 0);
+  std::vector<SearchResult*> display_results =
+      AppListModel::FilterSearchResultsByDisplayType(
+          results(), SearchResult::DISPLAY_CARD, 1);
+
+  const bool have_result =
+      !display_results.empty() && !features::IsAnswerCardDarkRunEnabled();
+
+  search_answer_container_view_->SetSearchResult(
+      have_result ? display_results[0] : nullptr);
+
+  set_container_score(have_result ? display_results.front()->relevance() : 0);
   return have_result ? 1 : 0;
 }
 
@@ -128,14 +139,13 @@
     NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
 }
 
-void SearchResultAnswerCardView::OnSearchAnswerAvailableChanged(
-    bool has_answer) {
-  const bool visible = has_answer && !features::IsAnswerCardDarkRunEnabled();
-  if (visible == search_answer_container_view_->visible())
-    return;
+bool SearchResultAnswerCardView::OnKeyPressed(const ui::KeyEvent& event) {
+  if (selected_index() == 0 &&
+      search_answer_container_view_->OnKeyPressed(event)) {
+    return true;
+  }
 
-  search_answer_container_view_->SetVisible(visible);
-  ScheduleUpdate();
+  return SearchResultContainerView::OnKeyPressed(event);
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/search_result_answer_card_view.h b/ui/app_list/views/search_result_answer_card_view.h
index 745b5fad..80ad1c8 100644
--- a/ui/app_list/views/search_result_answer_card_view.h
+++ b/ui/app_list/views/search_result_answer_card_view.h
@@ -5,22 +5,17 @@
 #ifndef UI_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_
 #define UI_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_
 
-#include "ui/app_list/app_list_model_observer.h"
 #include "ui/app_list/views/search_result_container_view.h"
 
 namespace app_list {
 
-class AppListModel;
-class SearchResultPageView;
+class AppListViewDelegate;
 
 // Result container for the search answer card.
 class APP_LIST_EXPORT SearchResultAnswerCardView
-    : public SearchResultContainerView,
-      public AppListModelObserver {
+    : public SearchResultContainerView {
  public:
-  SearchResultAnswerCardView(AppListModel* model,
-                             SearchResultPageView* search_results_page_view,
-                             views::View* search_answer_view);
+  explicit SearchResultAnswerCardView(AppListViewDelegate* view_delegate);
   ~SearchResultAnswerCardView() override;
 
  private:
@@ -36,12 +31,7 @@
   int GetYSize() override;
   int DoUpdate() override;
   void UpdateSelectedIndex(int old_selected, int new_selected) override;
-
-  // Overridden from AppListModelObserver
-  void OnSearchAnswerAvailableChanged(bool has_answer) override;
-
-  // Unowned pointer to application list model.
-  AppListModel* const model_;
+  bool OnKeyPressed(const ui::KeyEvent& event) override;
 
   // Pointer to the container of the search answer; owned by the view hierarchy.
   // It's visible iff we have a search answer result.
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h
index 19c02c13..b5f8496 100644
--- a/ui/app_list/views/search_result_container_view.h
+++ b/ui/app_list/views/search_result_container_view.h
@@ -84,12 +84,11 @@
   virtual void OnContainerSelected(bool from_bottom,
                                    bool directional_movement) = 0;
 
- protected:
+ private:
   // Schedules an Update call using |update_factory_|. Do nothing if there is a
   // pending call.
   void ScheduleUpdate();
 
- private:
   // Updates UI with model. Returns the number of visible results.
   virtual int DoUpdate() = 0;
 
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 89481a1..7987464 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -2000,6 +2000,16 @@
       WindowMus::Get(window_tree_host->window())->server_id());
 }
 
+void WindowTreeClient::OnWindowTreeHostMoveCursorToDisplayLocation(
+    const gfx::Point& location_in_pixels,
+    int64_t display_id) {
+  DCHECK(window_manager_client_);
+  if (window_manager_client_) {
+    window_manager_client_->WmMoveCursorToDisplayLocation(location_in_pixels,
+                                                          display_id);
+  }
+}
+
 std::unique_ptr<WindowPortMus> WindowTreeClient::CreateWindowPortForTopLevel(
     const std::map<std::string, std::vector<uint8_t>>* properties) {
   std::unique_ptr<WindowPortMus> window_port =
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index a297e32..44f3f3a 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -515,6 +515,9 @@
       const base::Callback<void(bool)>& callback) override;
   void OnWindowTreeHostCancelWindowMove(
       WindowTreeHostMus* window_tree_host) override;
+  void OnWindowTreeHostMoveCursorToDisplayLocation(
+      const gfx::Point& location_in_pixels,
+      int64_t display_id) override;
   std::unique_ptr<WindowPortMus> CreateWindowPortForTopLevel(
       const std::map<std::string, std::vector<uint8_t>>* properties) override;
   void OnWindowTreeHostCreated(WindowTreeHostMus* window_tree_host) override;
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index 85cdf6a4d..5e60905 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -200,10 +200,12 @@
 
 void WindowTreeHostMus::MoveCursorToScreenLocationInPixels(
     const gfx::Point& location_in_pixels) {
-  // TODO: this needs to message the server http://crbug.com/693340. Setting
-  // the location is really only appropriate in tests, outside of tests this
-  // value is ignored.
-  NOTIMPLEMENTED();
+  gfx::Point screen_location_in_pixels = location_in_pixels;
+  gfx::Point location = GetLocationOnScreenInPixels();
+  screen_location_in_pixels.Offset(-location.x(), -location.y());
+  delegate_->OnWindowTreeHostMoveCursorToDisplayLocation(
+      screen_location_in_pixels, display_id_);
+
   Env::GetInstance()->set_last_mouse_location(location_in_pixels);
 }
 
diff --git a/ui/aura/mus/window_tree_host_mus_delegate.h b/ui/aura/mus/window_tree_host_mus_delegate.h
index b93c6598..4ea7c2bf 100644
--- a/ui/aura/mus/window_tree_host_mus_delegate.h
+++ b/ui/aura/mus/window_tree_host_mus_delegate.h
@@ -71,6 +71,11 @@
   virtual void OnWindowTreeHostCancelWindowMove(
       WindowTreeHostMus* window_tree_host) = 0;
 
+  // Called to move the location of the cursor.
+  virtual void OnWindowTreeHostMoveCursorToDisplayLocation(
+      const gfx::Point& location_in_pixels,
+      int64_t display_id) = 0;
+
   // Called when a WindowTreeHostMus is created without a WindowPort.
   // TODO: this should take an unordered_map, see http://crbug.com/670515.
   virtual std::unique_ptr<WindowPortMus> CreateWindowPortForTopLevel(
diff --git a/ui/aura/test/mus/test_window_manager_client.cc b/ui/aura/test/mus/test_window_manager_client.cc
index 0131e12..8ecb9dea 100644
--- a/ui/aura/test/mus/test_window_manager_client.cc
+++ b/ui/aura/test/mus/test_window_manager_client.cc
@@ -72,6 +72,10 @@
 void TestWindowManagerClient::WmSetGlobalOverrideCursor(
     base::Optional<ui::CursorData> cursor) {}
 
+void TestWindowManagerClient::WmMoveCursorToDisplayLocation(
+    const gfx::Point& display_pixels,
+    int64_t display_id) {}
+
 void TestWindowManagerClient::OnWmCreatedTopLevelWindow(
     uint32_t change_id,
     Id transport_window_id) {}
diff --git a/ui/aura/test/mus/test_window_manager_client.h b/ui/aura/test/mus/test_window_manager_client.h
index 87373d231..0d56414 100644
--- a/ui/aura/test/mus/test_window_manager_client.h
+++ b/ui/aura/test/mus/test_window_manager_client.h
@@ -58,6 +58,8 @@
   void WmSetCursorVisible(bool visible) override;
   void WmSetGlobalOverrideCursor(
       base::Optional<ui::CursorData> cursor) override;
+  void WmMoveCursorToDisplayLocation(const gfx::Point& display_pixels,
+                                     int64_t display_id) override;
   void OnWmCreatedTopLevelWindow(uint32_t change_id,
                                  Id transport_window_id) override;
   void OnAcceleratorAck(
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc
index 5a183bf..8e3e8a86 100644
--- a/ui/message_center/views/message_center_view.cc
+++ b/ui/message_center/views/message_center_view.cc
@@ -92,15 +92,10 @@
   const int button_height = button_bar_->GetPreferredSize().height();
 
   scroller_ = new views::ScrollView();
+  scroller_->SetBackgroundColor(kMessageCenterBackgroundColor);
   scroller_->ClipHeightTo(kMinScrollViewHeight, max_height - button_height);
   scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
   scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true));
-  scroller_->SetBackground(
-      views::CreateSolidBackground(kMessageCenterBackgroundColor));
-
-  scroller_->SetPaintToLayer();
-  scroller_->layer()->SetFillsBoundsOpaquely(false);
-  scroller_->layer()->SetMasksToBounds(true);
 
   message_list_view_.reset(new MessageListView());
   message_list_view_->set_scroller(scroller_);
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 6d97969..539d23e 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -49,6 +49,17 @@
   DISALLOW_COPY_AND_ASSIGN(ScrollCornerView);
 };
 
+// Returns true if any descendants of |view| have a layer (not including
+// |view|).
+bool DoesDescendantHaveLayer(View* view) {
+  for (int i = 0; i < view->child_count(); ++i) {
+    View* child = view->child_at(i);
+    if (child->layer() || DoesDescendantHaveLayer(child))
+      return true;
+  }
+  return false;
+}
+
 // Returns the position for the view so that it isn't scrolled off the visible
 // region.
 int CheckScrollBounds(int viewport_size, int content_size, int current_pos) {
@@ -110,7 +121,7 @@
 // Viewport contains the contents View of the ScrollView.
 class ScrollView::Viewport : public View {
  public:
-  Viewport() {}
+  explicit Viewport(ScrollView* scroll_view) : scroll_view_(scroll_view) {}
   ~Viewport() override {}
 
   const char* GetClassName() const override { return "ScrollView::Viewport"; }
@@ -122,8 +133,7 @@
     View* contents = child_at(0);
     gfx::Rect scroll_rect(rect);
 
-    ScrollView* scroll_view = static_cast<ScrollView*>(parent());
-    if (scroll_view->ScrollsWithLayers()) {
+    if (scroll_view_->ScrollsWithLayers()) {
       // With layer scrolling, there's no need to "undo" the offset done in the
       // child's View::ScrollRectToVisible() before it calls this.
       DCHECK_EQ(0, contents->x());
@@ -132,7 +142,7 @@
       scroll_rect.Offset(-contents->x(), -contents->y());
     }
 
-    scroll_view->ScrollContentsRegionToBeVisible(scroll_rect);
+    scroll_view_->ScrollContentsRegionToBeVisible(scroll_rect);
   }
 
   void ChildPreferredSizeChanged(View* child) override {
@@ -140,21 +150,37 @@
       parent()->Layout();
   }
 
+  void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) override {
+    if (details.is_add && IsContentsViewport() && Contains(details.parent))
+      scroll_view_->UpdateViewportLayerForClipping();
+  }
+
+  void OnChildLayerChanged(View* child) override {
+    if (IsContentsViewport())
+      scroll_view_->UpdateViewportLayerForClipping();
+  }
+
  private:
+  bool IsContentsViewport() const {
+    return parent() && scroll_view_->contents_viewport_ == this;
+  }
+
+  ScrollView* scroll_view_;
+
   DISALLOW_COPY_AND_ASSIGN(Viewport);
 };
 
 ScrollView::ScrollView()
-    : contents_(NULL),
-      contents_viewport_(new Viewport()),
-      header_(NULL),
-      header_viewport_(new Viewport()),
+    : contents_(nullptr),
+      contents_viewport_(new Viewport(this)),
+      header_(nullptr),
+      header_viewport_(new Viewport(this)),
       horiz_sb_(PlatformStyle::CreateScrollBar(true).release()),
       vert_sb_(PlatformStyle::CreateScrollBar(false).release()),
       corner_view_(new ScrollCornerView()),
       min_height_(-1),
       max_height_(-1),
-      background_color_(SK_ColorTRANSPARENT),
       hide_horizontal_scrollbar_(false),
       scroll_with_layers_enabled_(
           base::FeatureList::IsEnabled(kToolkitViewsScrollWithLayers)) {
@@ -171,10 +197,9 @@
   vert_sb_->set_controller(this);
   corner_view_->SetVisible(false);
 
-  if (!scroll_with_layers_enabled_)
-    return;
-
-  EnableViewPortLayer();
+  if (scroll_with_layers_enabled_)
+    EnableViewPortLayer();
+  UpdateBackground();
 }
 
 ScrollView::~ScrollView() {
@@ -208,8 +233,8 @@
   // Protect against clients passing a contents view that has its own Layer.
   DCHECK(!a_view->layer());
   if (ScrollsWithLayers()) {
-    if (!a_view->background() && background_color_ != SK_ColorTRANSPARENT) {
-      a_view->SetBackground(CreateSolidBackground(background_color_));
+    if (!a_view->background() && GetBackgroundColor() != SK_ColorTRANSPARENT) {
+      a_view->SetBackground(CreateSolidBackground(GetBackgroundColor()));
     }
     a_view->SetPaintToLayer();
     a_view->layer()->SetScrollable(
@@ -224,12 +249,15 @@
 }
 
 void ScrollView::SetBackgroundColor(SkColor color) {
-  background_color_ = color;
-  contents_viewport_->SetBackground(CreateSolidBackground(background_color_));
-  if (contents_ && ScrollsWithLayers() &&
-      background_color_ != SK_ColorTRANSPARENT) {
-    contents_->SetBackground(CreateSolidBackground(background_color_));
-  }
+  background_color_data_.color = color;
+  use_color_id_ = false;
+  UpdateBackground();
+}
+
+void ScrollView::SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id) {
+  background_color_data_.color_id = color_id;
+  use_color_id_ = true;
+  UpdateBackground();
 }
 
 gfx::Rect ScrollView::GetVisibleRect() const {
@@ -530,16 +558,8 @@
 
 void ScrollView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
   UpdateBorder();
-}
-
-void ScrollView::ViewHierarchyChanged(
-    const ViewHierarchyChangedDetails& details) {
-  if (details.is_add && !viewport_layer_enabled_ && Contains(details.parent))
-    EnableLayeringRecursivelyForChild(details.child);
-}
-
-void ScrollView::OnChildLayerChanged(View* child) {
-  EnableViewPortLayer();
+  if (use_color_id_)
+    UpdateBackground();
 }
 
 void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
@@ -590,6 +610,24 @@
                          contents_viewport_->height() / 5;
 }
 
+bool ScrollView::DoesViewportOrScrollViewHaveLayer() const {
+  return layer() || contents_viewport_->layer();
+}
+
+void ScrollView::UpdateViewportLayerForClipping() {
+  if (scroll_with_layers_enabled_)
+    return;
+
+  const bool has_layer = DoesViewportOrScrollViewHaveLayer();
+  const bool needs_layer = DoesDescendantHaveLayer(contents_viewport_);
+  if (has_layer == needs_layer)
+    return;
+  if (needs_layer)
+    EnableViewPortLayer();
+  else
+    contents_viewport_->DestroyLayer();
+}
+
 void ScrollView::SetHeaderOrContents(View* parent,
                                      View* new_view,
                                      View** member) {
@@ -740,22 +778,12 @@
 }
 
 void ScrollView::EnableViewPortLayer() {
-  if (viewport_layer_enabled_)
+  if (DoesViewportOrScrollViewHaveLayer())
     return;
 
-  viewport_layer_enabled_ = true;
-
   contents_viewport_->SetPaintToLayer();
-
-  if (scroll_with_layers_enabled_) {
-    background_color_ = SK_ColorWHITE;
-    contents_viewport_->SetBackground(CreateSolidBackground(background_color_));
-  } else {
-    // We may have transparent children who want to blend into the default
-    // background.
-    contents_viewport_->layer()->SetFillsBoundsOpaquely(false);
-  }
   contents_viewport_->layer()->SetMasksToBounds(true);
+  UpdateBackground();
 }
 
 void ScrollView::OnLayerScrolled(const gfx::ScrollOffset&) {
@@ -791,20 +819,27 @@
               : ui::NativeTheme::kColorId_UnfocusedBorderColor)));
 }
 
-bool ScrollView::EnableLayeringRecursivelyForChild(View* view) {
-  if (viewport_layer_enabled_ || scroll_with_layers_enabled_)
-    return true;
+void ScrollView::UpdateBackground() {
+  const SkColor background_color = GetBackgroundColor();
 
-  if (view->layer()) {
-    EnableViewPortLayer();
-    return true;
+  SetBackground(CreateSolidBackground(background_color));
+  // In addition to setting the background of |this|, set the background on
+  // the viewport as well. This way if the viewport has a layer
+  // SetFillsBoundsOpaquely() is honored.
+  contents_viewport_->SetBackground(CreateSolidBackground(background_color));
+  if (contents_ && ScrollsWithLayers())
+    contents_->SetBackground(CreateSolidBackground(background_color));
+  if (contents_viewport_->layer()) {
+    contents_viewport_->layer()->SetFillsBoundsOpaquely(background_color !=
+                                                        SK_ColorTRANSPARENT);
   }
+  SchedulePaint();
+}
 
-  for (int i = 0; i < view->child_count(); ++i) {
-    if (EnableLayeringRecursivelyForChild(view->child_at(i)))
-      return true;
-  }
-  return false;
+SkColor ScrollView::GetBackgroundColor() const {
+  return use_color_id_
+             ? GetNativeTheme()->GetSystemColor(background_color_data_.color_id)
+             : background_color_data_.color;
 }
 
 // VariableRowHeightScrollHelper ----------------------------------------------
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h
index 8fe732f..a39fbbd 100644
--- a/ui/views/controls/scroll_view.h
+++ b/ui/views/controls/scroll_view.h
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/scrollbar/scroll_bar.h"
 
 namespace gfx {
@@ -17,8 +18,6 @@
 }
 
 namespace views {
-class ViewObserverTest;
-
 namespace test {
 class ScrollViewTestApi;
 }
@@ -61,10 +60,15 @@
   // Sets the header, deleting the previous header.
   void SetHeader(View* header);
 
-  // Sets the background color. The default is white when scrolling with layers,
-  // otherwise transparent. An opaque color when scrolling with layers ensures
-  // fonts can be drawn with subpixel antialiasing.
+  // The background color can be configured in two distinct ways:
+  // . By way of SetBackgroundThemeColorId(). This is the default and when
+  //   called the background color comes from the theme (and changes if the
+  //   theme changes).
+  // . By way of setting an explicit color, i.e. SetBackgroundColor(). Use
+  //   SK_ColorTRANSPARENT if you don't want any color, but be warned this
+  //   produces awful results when layers are used with subpixel rendering.
   void SetBackgroundColor(SkColor color);
+  void SetBackgroundThemeColorId(ui::NativeTheme::ColorId color_id);
 
   // Returns the visible region of the content View.
   gfx::Rect GetVisibleRect() const;
@@ -107,9 +111,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   const char* GetClassName() const override;
   void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
-  void ViewHierarchyChanged(
-      const ViewHierarchyChangedDetails& details) override;
-  void OnChildLayerChanged(View* child) override;
 
   // ScrollBarController overrides:
   void ScrollToPosition(ScrollBar* source, int position) override;
@@ -117,16 +118,27 @@
                          bool is_page,
                          bool is_positive) override;
 
-  // TODO(djacobo): Remove this method when http://crbug.com/656198  is closed.
-  // Force |contents_viewport_| to enable a Layer().
-  void EnableViewPortLayer();
-
  private:
   friend class test::ScrollViewTestApi;
-  FRIEND_TEST_ALL_PREFIXES(ViewObserverTest, ScrollViewChildAddLayerTest);
 
   class Viewport;
 
+  union BackgroundColorData {
+    SkColor color;
+    ui::NativeTheme::ColorId color_id;
+  };
+
+  // Forces |contents_viewport_| to have a Layer (assuming it doesn't already).
+  void EnableViewPortLayer();
+
+  // Returns true if this or the viewport has a layer.
+  bool DoesViewportOrScrollViewHaveLayer() const;
+
+  // Updates or destroys the viewport layer as necessary. If any descendants
+  // of the viewport have a layer, then the viewport needs to have a layer,
+  // otherwise it doesn't.
+  void UpdateViewportLayerForClipping();
+
   // Used internally by SetHeader() and SetContents() to reset the view.  Sets
   // |member| to |new_view|. If |new_view| is non-null it is added to |parent|.
   void SetHeaderOrContents(View* parent, View* new_view, View** member);
@@ -168,10 +180,8 @@
   void AddBorder();
   void UpdateBorder();
 
-  // Enables view port layering if |child| or any of its descendants has a
-  // layer. Returns true if yes. We short circuit the recursion if we enabled
-  // layering.
-  bool EnableLayeringRecursivelyForChild(View* child);
+  void UpdateBackground();
+  SkColor GetBackgroundColor() const;
 
   // The current contents and its viewport. |contents_| is contained in
   // |contents_viewport_|.
@@ -197,9 +207,10 @@
   int min_height_;
   int max_height_;
 
-  // The background color given to the viewport (for overscroll), and to the
-  // contents when scrolling with layers.
-  SkColor background_color_;
+  // See description of SetBackgroundColor() for details.
+  BackgroundColorData background_color_data_ = {
+      ui::NativeTheme::kColorId_DialogBackground};
+  bool use_color_id_ = true;
 
   // If true, never show the horizontal scrollbar (even if the contents is wider
   // than the viewport).
@@ -215,9 +226,6 @@
   // Focus ring, if one is installed.
   View* focus_ring_ = nullptr;
 
-  // Set to true if we enabled layering for the viewport.
-  bool viewport_layer_enabled_ = false;
-
   // Set to true if the scroll with layers feature is enabled.
   const bool scroll_with_layers_enabled_;
 
diff --git a/ui/views/controls/scroll_view_unittest.cc b/ui/views/controls/scroll_view_unittest.cc
index 574893ba..cc0a318 100644
--- a/ui/views/controls/scroll_view_unittest.cc
+++ b/ui/views/controls/scroll_view_unittest.cc
@@ -707,6 +707,54 @@
   EXPECT_TRUE(corner_view->visible());
 }
 
+TEST_F(ScrollViewTest, ChildWithLayerTest) {
+  View* contents = InstallContents();
+  ScrollViewTestApi test_api(&scroll_view_);
+
+  if (test_api.contents_viewport()->layer())
+    return;
+
+  View* child = new View();
+  contents->AddChildView(child);
+  child->SetPaintToLayer(ui::LAYER_TEXTURED);
+
+  ASSERT_TRUE(test_api.contents_viewport()->layer());
+  // The default ScrollView color is opaque, so that fills bounds opaquely
+  // should be true.
+  EXPECT_TRUE(test_api.contents_viewport()->layer()->fills_bounds_opaquely());
+
+  // Setting a transparent color should make fills opaquely false.
+  scroll_view_.SetBackgroundColor(SK_ColorTRANSPARENT);
+  EXPECT_FALSE(test_api.contents_viewport()->layer()->fills_bounds_opaquely());
+
+  child->DestroyLayer();
+  EXPECT_FALSE(test_api.contents_viewport()->layer());
+
+  View* child_child = new View();
+  child->AddChildView(child_child);
+  EXPECT_FALSE(test_api.contents_viewport()->layer());
+  child->SetPaintToLayer(ui::LAYER_TEXTURED);
+  EXPECT_TRUE(test_api.contents_viewport()->layer());
+}
+
+// Validates that if a child of a ScrollView adds a layer, then a layer
+// is added to the ScrollView's viewport.
+TEST_F(ScrollViewTest, DontCreateLayerOnViewportIfLayerOnScrollViewCreated) {
+  View* contents = InstallContents();
+  ScrollViewTestApi test_api(&scroll_view_);
+
+  if (test_api.contents_viewport()->layer())
+    return;
+
+  scroll_view_.SetPaintToLayer();
+
+  View* child = new View();
+  contents->AddChildView(child);
+  child->SetPaintToLayer(ui::LAYER_TEXTURED);
+
+  EXPECT_FALSE(test_api.contents_viewport()->layer());
+}
+
 #if defined(OS_MACOSX)
 // Tests the overlay scrollbars on Mac. Ensure that they show up properly and
 // do not overlap each other.
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index d71f0f6..e45cbb7 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -5023,31 +5023,6 @@
   EXPECT_EQ(child_view2.get(), view_reordered());
 }
 
-// Validates that if a child of a ScrollView adds a layer, then a layer
-// is added to the ScrollView's viewport.
-TEST_F(ViewObserverTest, ScrollViewChildAddLayerTest) {
-  std::unique_ptr<ScrollView> scroll_view(new ScrollView());
-  scroll_view->SetContents(new View());
-  // Bail if the scroll view already has a layer.
-  if (scroll_view->contents_viewport_->layer())
-    return;
-
-  EXPECT_FALSE(scroll_view->contents_viewport_->layer());
-
-  std::unique_ptr<View> child_view = NewView();
-  scroll_view->AddChildView(child_view.get());
-  child_view->SetPaintToLayer(ui::LAYER_TEXTURED);
-
-  EXPECT_TRUE(scroll_view->contents_viewport_->layer());
-  // We don't want the viewport's layer to have the fill_bounds_opaquely() bit
-  // set, as we may have transparent children who want to blend into the
-  // default background.
-  EXPECT_FALSE(
-      scroll_view->contents_viewport_->layer()->fills_bounds_opaquely());
-
-  scroll_view->RemoveChildView(child_view.get());
-}
-
 // Provides a simple parent view implementation which tracks layer change
 // notifications from child views.
 class TestParentView : public View {