diff --git a/DEPS b/DEPS
index 274c99d9..f213c00 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,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': '74c6ed3d1f179209ec90025532310d9c8151999f',
+  'skia_revision': 'e782f8472f61a5a553c57fef788ad4405844887b',
   # 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': 'e8e10fdf6577dbb360e4b5d404fc719ac3978571',
+  'v8_revision': '8b8cc1cca5b1235993acb504f1e0373e47e14cad',
   # 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.
@@ -129,7 +129,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': 'b53ef1e52d40f586c401a7e3948259f8ebbfd3cc',
+  'pdfium_revision': 'cede561c5ae665a563409ba3bfc93c3cd6da5248',
   # 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.
@@ -768,7 +768,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + 'd955c63ec7048d59dffd20af25eeec23da878d27',
 
   'src/third_party/libaom/source/libaom': {
-    'url': Var('aomedia_git') + '/aom.git' + '@' +  '4f1fd9640433a9ee83dfd48c83a3d281fbdfce3d',
+    'url': Var('aomedia_git') + '/aom.git' + '@' +  'bc484c485277bc19c7a1b273c8cf5472f741b73a',
     'condition': 'checkout_libaom',
   },
 
@@ -912,7 +912,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c2ace4e71f593d8b34b03d02c439d663dd20f50a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'a52306a4a7f2ae7966f8f1766c14cd850f62908f',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1034,7 +1034,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '21dbf06b5aa6c7dc8cf56314d4a3f96f57956c53',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2a99c0bf6765d9c4615236ba4e00886678b2ef76',
+    Var('webrtc_git') + '/src.git' + '@' + '45a4c41eda9066a62e7e324b008a35c0295f15c7',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8bbde3dc..15b2b01 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -677,7 +677,7 @@
   # We only scan .cc files and the like, as the declaration of
   # for-testing functions in header files are hard to distinguish from
   # calls to such functions without a proper C++ parser.
-  file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
+  file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
 
   base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
   inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
@@ -692,7 +692,7 @@
                   input_api.DEFAULT_BLACK_LIST)
     return input_api.FilterSourceFile(
       affected_file,
-      white_list=(file_inclusion_pattern, ),
+      white_list=file_inclusion_pattern,
       black_list=black_list)
 
   problems = []
@@ -730,7 +730,7 @@
     x,
     black_list=(('(?i).*test', r'.*\/junit\/')
                 + input_api.DEFAULT_BLACK_LIST),
-    white_list=(r'.*\.java$',)
+    white_list=[r'.*\.java$']
   )
   for f in input_api.AffectedFiles(include_deletes=False, file_filter=sources):
     local_path = f.LocalPath()
@@ -1292,7 +1292,7 @@
     """
     return input_api.FilterSourceFile(
       affected_file,
-      white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
+      white_list=[r'^(android_webview|base|content|net)[\\\/].*'],
       black_list=(_EXCLUDED_PATHS +
                   _TEST_CODE_EXCLUDED_PATHS +
                   input_api.DEFAULT_BLACK_LIST))
@@ -1317,14 +1317,15 @@
     return []
 
 
+# TODO: add unit tests.
 def _CheckNoAbbreviationInPngFileName(input_api, output_api):
   """Makes sure there are no abbreviations in the name of PNG files.
   The native_client_sdk directory is excluded because it has auto-generated PNG
   files for documentation.
   """
   errors = []
-  white_list = (r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$',)
-  black_list = (r'^native_client_sdk[\\\/]',)
+  white_list = [r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$']
+  black_list = [r'^native_client_sdk[\\\/]']
   file_filter = lambda f: input_api.FilterSourceFile(
       f, white_list=white_list, black_list=black_list)
   for f in input_api.AffectedFiles(include_deletes=False,
@@ -1488,8 +1489,9 @@
   return []
 
 
+# TODO: add unit tests.
 def _CheckSpamLogging(input_api, output_api):
-  file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
+  file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
   black_list = (_EXCLUDED_PATHS +
                 _TEST_CODE_EXCLUDED_PATHS +
                 input_api.DEFAULT_BLACK_LIST +
@@ -1500,6 +1502,7 @@
                  r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
                      r"startup_browser_creator\.cc$",
                  r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
+                 r"^chrome[\\\/]chrome_cleaner[\\\/].*",
                  r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
                      r"diagnostics_writer\.cc$",
                  r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
@@ -1531,7 +1534,7 @@
                      r"dump_file_system.cc$",
                  r"^headless[\\\/]app[\\\/]headless_shell\.cc$"))
   source_file_filter = lambda x: input_api.FilterSourceFile(
-      x, white_list=(file_inclusion_pattern,), black_list=black_list)
+      x, white_list=file_inclusion_pattern, black_list=black_list)
 
   log_info = set([])
   printf = set([])
@@ -1616,12 +1619,12 @@
 
 
 def _CheckUniquePtr(input_api, output_api):
-  file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
+  file_inclusion_pattern = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
   sources = lambda affected_file: input_api.FilterSourceFile(
       affected_file,
       black_list=(_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
                   input_api.DEFAULT_BLACK_LIST),
-      white_list=(file_inclusion_pattern,))
+      white_list=file_inclusion_pattern)
 
   # Pattern to capture a single "<...>" block of template arguments. It can
   # handle linearly nested blocks, such as "<std::vector<std::set<T>>>", but
@@ -2054,6 +2057,7 @@
   return results
 
 
+# TODO: add unit tests
 def _CheckAndroidToastUsage(input_api, output_api):
   """Checks that code uses org.chromium.ui.widget.Toast instead of
      android.widget.Toast (Chromium Toast doesn't force hardware
@@ -2071,7 +2075,7 @@
                   input_api.DEFAULT_BLACK_LIST +
                   (r'^chromecast[\\\/].*',
                    r'^remoting[\\\/].*')),
-      white_list=(r'.*\.java$',))
+      white_list=[r'.*\.java$'])
 
   for f in input_api.AffectedSourceFiles(sources):
     for line_num, line in f.ChangedContents():
@@ -2120,7 +2124,7 @@
 
   REF_MSG = ('See docs/android_logging.md '
             'or contact dgn@chromium.org for more info.')
-  sources = lambda x: input_api.FilterSourceFile(x, white_list=(r'.*\.java$',),
+  sources = lambda x: input_api.FilterSourceFile(x, white_list=[r'.*\.java$'],
       black_list=cr_log_check_excluded_paths)
 
   tag_decl_errors = []
@@ -2204,7 +2208,7 @@
       r'^import junit\.framework\..*;',
       input_api.re.MULTILINE)
   sources = lambda x: input_api.FilterSourceFile(
-      x, white_list=(r'.*\.java$',), black_list=None)
+      x, white_list=[r'.*\.java$'], black_list=None)
   errors = []
   for f in input_api.AffectedFiles(sources):
     for line_num, line in f.ChangedContents():
@@ -2228,7 +2232,7 @@
   class_declaration_pattern = input_api.re.compile(r'^public class \w*Test ')
 
   sources = lambda x: input_api.FilterSourceFile(
-      x, white_list=(r'.*Test\.java$',), black_list=None)
+      x, white_list=[r'.*Test\.java$'], black_list=None)
   errors = []
   for f in input_api.AffectedFiles(sources):
     if not f.OldContents():
@@ -2257,7 +2261,7 @@
       r'^import android\.test\.suitebuilder\.annotation\..*;',
       input_api.re.MULTILINE)
   sources = lambda x: input_api.FilterSourceFile(
-      x, white_list=(r'.*\.java$',), black_list=None)
+      x, white_list=[r'.*\.java$'], black_list=None)
   errors = []
   for f in input_api.AffectedFiles(sources):
     for line_num, line in f.ChangedContents():
@@ -2309,7 +2313,7 @@
                   _TEST_CODE_EXCLUDED_PATHS +
                   input_api.DEFAULT_BLACK_LIST +
                   (r'^android_webview[\\\/]glue[\\\/].*',)),
-      white_list=(r'.*\.java$',))
+      white_list=[r'.*\.java$'])
 
   for f in input_api.AffectedSourceFiles(sources):
     for line_num, line in f.ChangedContents():
@@ -2501,6 +2505,7 @@
 ]
 
 
+# TODO: add unit tests
 def _CheckNoDeprecatedCss(input_api, output_api):
   """ Make sure that we don't use deprecated CSS
       properties, functions or values. Our external
@@ -2508,7 +2513,7 @@
       (reader mode) are ignored by the hooks as it
       needs to be consumed by WebKit. """
   results = []
-  file_inclusion_pattern = (r".+\.css$",)
+  file_inclusion_pattern = [r".+\.css$"]
   black_list = (_EXCLUDED_PATHS +
                 _TEST_CODE_EXCLUDED_PATHS +
                 input_api.DEFAULT_BLACK_LIST +
@@ -2536,10 +2541,11 @@
 ]
 
 
+# TODO: add unit tests
 def _CheckNoDeprecatedJs(input_api, output_api):
   """Make sure that we don't use deprecated JS in Chrome code."""
   results = []
-  file_inclusion_pattern = (r".+\.js$",)  # TODO(dbeam): .html?
+  file_inclusion_pattern = [r".+\.js$"]  # TODO(dbeam): .html?
   black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
                 input_api.DEFAULT_BLACK_LIST)
   file_filter = lambda f: input_api.FilterSourceFile(
@@ -2569,7 +2575,7 @@
 
 
 def _CheckForRiskyJsFeatures(input_api, output_api):
-  maybe_ios_js = (r"^(ios|components|ui\/webui\/resources)\/.+\.js$", )
+  maybe_ios_js = [r"^(ios|components|ui\/webui\/resources)\/.+\.js$"]
   # 'ui/webui/resources/cr_components are not allowed on ios'
   not_ios_filter = (r".*ui\/webui\/resources\/cr_components.*", )
   file_filter = lambda f: input_api.FilterSourceFile(f, white_list=maybe_ios_js,
@@ -3278,7 +3284,7 @@
   """Checks that crbug(.com) links are correctly prefixed by https://,
    unless they come in the accepted form TODO(crbug.com/...)
   """
-  white_list = (r'.+%s' % _IMPLEMENTATION_EXTENSIONS, )
+  white_list = [r'.+%s' % _IMPLEMENTATION_EXTENSIONS]
   black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS)
   sources = lambda f: input_api.FilterSourceFile(
       f, white_list=white_list, black_list=black_list)
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 0d6a06ac..65fed70 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -191,6 +191,11 @@
     *config = default_config_;
   }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   net::SSLConfig default_config_;
 };
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java b/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java
index 90daa5f3..74f2c932 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java
@@ -87,10 +87,11 @@
         }
     }
 
-    // Silently returns null in case of incomplete/corrupt/missing seed, which is expected in case
+    // Silently returns null in case of a missing or truncated seed, which is expected in case
     // of an incomplete downoad or copy. Other IO problems are actual errors, and are logged.
     @Nullable
     public static SeedInfo readSeedFile(File inFile) {
+        if (!inFile.exists()) return null;
         FileInputStream in = null;
         try {
             in = new FileInputStream(inFile);
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index dedef2e..971af7d 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -225,23 +225,26 @@
 
   touch_dragging_ = touch_dragging;
 
-  if (!touch_dragging)
-    apps_grid_view_->EndDrag(false);
-
   SetState(STATE_NORMAL);
   SetUIState(touch_dragging_ ? UI_STATE_DRAGGING : UI_STATE_NORMAL);
+
+  // EndDrag may delete |this|.
+  if (!touch_dragging)
+    apps_grid_view_->EndDrag(false);
 }
 
 void AppListItemView::SetMouseDragging(bool mouse_dragging) {
   mouse_dragging_ = mouse_dragging;
 
-  if (!mouse_dragging_) {
-    apps_grid_view_->EndDrag(false);
-    mouse_drag_proxy_created_ = false;
-  }
-
   SetState(STATE_NORMAL);
   SetUIState(mouse_dragging_ ? UI_STATE_DRAGGING : UI_STATE_NORMAL);
+
+  if (!mouse_dragging_) {
+    mouse_drag_proxy_created_ = false;
+
+    // EndDrag may delete |this|.
+    apps_grid_view_->EndDrag(false);
+  }
 }
 
 void AppListItemView::OnMouseDragTimer() {
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index ab8fd833..0a5b63fc 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -1431,7 +1431,7 @@
 
   // Updates the opacity of the items in the app list.
   search_box_view_->UpdateOpacity();
-  GetAppsContainerView()->UpdateOpacity();
+  app_list_main_view_->contents_view()->UpdateOpacity();
 
   Layout();
 }
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index 3892c10..c13d77e 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -13,7 +13,6 @@
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/app_list/views/apps_grid_view.h"
 #include "ash/app_list/views/contents_view.h"
-#include "ash/app_list/views/expand_arrow_view.h"
 #include "ash/app_list/views/folder_background_view.h"
 #include "ash/app_list/views/horizontal_page_container.h"
 #include "ash/app_list/views/page_switcher.h"
@@ -58,11 +57,6 @@
 constexpr float kSuggestionChipOpacityStartProgress = 0;
 constexpr float kSuggestionChipOpacityEndProgress = 0.67;
 
-// The range of app list transition progress in which the expand arrow'
-// opacity changes from 0 to 1.
-constexpr float kExpandArrowOpacityStartProgress = 0;
-constexpr float kExpandArrowOpacityEndProgress = 0.62;
-
 }  // namespace
 
 AppsContainerView::AppsContainerView(ContentsView* contents_view,
@@ -70,10 +64,6 @@
     : contents_view_(contents_view),
       is_new_style_launcher_enabled_(features::IsNewStyleLauncherEnabled()) {
   if (is_new_style_launcher_enabled_) {
-    expand_arrow_view_ =
-        new ExpandArrowView(contents_view_, contents_view_->app_list_view());
-    AddChildView(expand_arrow_view_);
-
     suggestion_chip_container_view_ =
         new SuggestionChipContainerView(contents_view);
     AddChildView(suggestion_chip_container_view_);
@@ -202,20 +192,6 @@
     suggestion_chip_container_view_->layer()->SetOpacity(
         should_restore_opacity ? 1.0f : chips_opacity);
   }
-
-  if (expand_arrow_view_) {
-    // Changes the opacity of expand arrow between 0 and 1 when app list
-    // transition progress changes between |kExpandArrowOpacityStartProgress|
-    // and |kExpandArrowOpacityEndProgress|.
-    float arrow_opacity =
-        std::min(std::max((progress - kExpandArrowOpacityStartProgress) /
-                              (kExpandArrowOpacityEndProgress -
-                               kExpandArrowOpacityStartProgress),
-                          0.f),
-                 1.0f);
-    expand_arrow_view_->layer()->SetOpacity(
-        should_restore_opacity ? 1.0f : arrow_opacity);
-  }
 }
 
 gfx::Size AppsContainerView::CalculatePreferredSize() const {
@@ -237,14 +213,6 @@
   switch (show_state_) {
     case SHOW_APPS: {
       if (is_new_style_launcher_enabled_) {
-        // Layout expand arrow.
-        gfx::Rect arrow_rect(rect);
-        const gfx::Size arrow_size(expand_arrow_view_->GetPreferredSize());
-        arrow_rect.set_height(arrow_size.height());
-        arrow_rect.ClampToCenteredSize(arrow_size);
-        expand_arrow_view_->SetBoundsRect(arrow_rect);
-        expand_arrow_view_->SchedulePaint();
-
         // Layout suggestion chips.
         gfx::Rect chip_container_rect(rect);
         const float progress =
diff --git a/ash/app_list/views/apps_container_view.h b/ash/app_list/views/apps_container_view.h
index 665ae59..e331803 100644
--- a/ash/app_list/views/apps_container_view.h
+++ b/ash/app_list/views/apps_container_view.h
@@ -21,7 +21,6 @@
 class AppListFolderView;
 class AppListModel;
 class ContentsView;
-class ExpandArrowView;
 class FolderBackgroundView;
 class PageSwitcher;
 class SuggestionChipContainerView;
@@ -121,7 +120,6 @@
   AppListFolderView* app_list_folder_view_ = nullptr;
   PageSwitcher* page_switcher_ = nullptr;
   FolderBackgroundView* folder_background_view_ = nullptr;
-  ExpandArrowView* expand_arrow_view_ = nullptr;
 
   ShowState show_state_ = SHOW_NONE;
 
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index f8df497..02315411 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -13,6 +13,7 @@
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/app_list/views/apps_container_view.h"
 #include "ash/app_list/views/apps_grid_view.h"
+#include "ash/app_list/views/expand_arrow_view.h"
 #include "ash/app_list/views/horizontal_page_container.h"
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/search_result_answer_card_view.h"
@@ -34,6 +35,11 @@
 
 namespace {
 
+// The range of app list transition progress in which the expand arrow'
+// opacity changes from 0 to 1.
+constexpr float kExpandArrowOpacityStartProgress = 0;
+constexpr float kExpandArrowOpacityEndProgress = 0.62;
+
 void DoAnimation(base::TimeDelta animation_duration,
                  ui::Layer* layer,
                  float target_opacity) {
@@ -84,6 +90,11 @@
         results, search_result_answer_card_view_);
   }
 
+  if (features::IsNewStyleLauncherEnabled()) {
+    expand_arrow_view_ = new ExpandArrowView(this, app_list_view_);
+    AddChildView(expand_arrow_view_);
+  }
+
   search_result_tile_item_list_view_ = new SearchResultTileItemListView(
       search_results_page_view_, GetSearchBoxView()->search_box(),
       view_delegate);
@@ -218,6 +229,8 @@
   app_list_pages_[GetActivePageIndex()]->OnWillBeShown();
 
   GetAppListMainView()->model()->SetState(state);
+
+  UpdateExpandArrowFocusBehavior(state);
 }
 
 void ContentsView::ShowSearchResults(bool show) {
@@ -267,6 +280,9 @@
 
   // Update the search box.
   UpdateSearchBox(progress, current_state, target_state);
+
+  // Update the expand arrow view's opacity.
+  UpdateExpandArrowOpacity(progress, current_state, target_state);
 }
 
 void ContentsView::UpdateSearchBox(double progress,
@@ -291,6 +307,41 @@
           ConvertRectToWidget(search_box_rect)));
 }
 
+void ContentsView::UpdateExpandArrowOpacity(double progress,
+                                            ash::AppListState current_state,
+                                            ash::AppListState target_state) {
+  if (!expand_arrow_view_)
+    return;
+
+  if (current_state == ash::AppListState::kStateSearchResults &&
+      (target_state == ash::AppListState::kStateStart ||
+       target_state == ash::AppListState::kStateApps)) {
+    // Fade in the expand arrow when search results page is opened.
+    expand_arrow_view_->layer()->SetOpacity(
+        gfx::Tween::FloatValueBetween(progress, 0, 1));
+  } else if (target_state == ash::AppListState::kStateSearchResults &&
+             (current_state == ash::AppListState::kStateStart ||
+              current_state == ash::AppListState::kStateApps)) {
+    // Fade out the expand arrow when search results page is closed.
+    expand_arrow_view_->layer()->SetOpacity(
+        gfx::Tween::FloatValueBetween(progress, 1, 0));
+  }
+}
+
+void ContentsView::UpdateExpandArrowFocusBehavior(
+    ash::AppListState current_state) {
+  if (!expand_arrow_view_)
+    return;
+
+  if (current_state == ash::AppListState::kStateStart) {
+    // The expand arrow is only focusable in peeking state.
+    expand_arrow_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
+    return;
+  }
+
+  expand_arrow_view_->SetFocusBehavior(FocusBehavior::NEVER);
+}
+
 PaginationModel* ContentsView::GetAppsPaginationModel() {
   return GetAppsContainerView()->apps_grid_view()->pagination_model();
 }
@@ -397,9 +448,20 @@
 }
 
 void ContentsView::Layout() {
-  if (GetContentsBounds().IsEmpty())
+  const gfx::Rect rect = GetContentsBounds();
+  if (rect.IsEmpty())
     return;
 
+  if (expand_arrow_view_) {
+    // Layout expand arrow.
+    gfx::Rect arrow_rect(rect);
+    const gfx::Size arrow_size(expand_arrow_view_->GetPreferredSize());
+    arrow_rect.set_height(arrow_size.height());
+    arrow_rect.ClampToCenteredSize(arrow_size);
+    expand_arrow_view_->SetBoundsRect(arrow_rect);
+    expand_arrow_view_->SchedulePaint();
+  }
+
   UpdatePageBounds();
 }
 
@@ -454,6 +516,30 @@
   return app_list_pages_[GetActivePageIndex()]->GetSelectedView();
 }
 
+void ContentsView::UpdateOpacity() {
+  if (expand_arrow_view_) {
+    const bool should_restore_opacity =
+        !app_list_view_->is_in_drag() &&
+        (app_list_view_->app_list_state() != AppListViewState::CLOSED);
+
+    // Changes the opacity of expand arrow between 0 and 1 when app list
+    // transition progress changes between |kExpandArrowOpacityStartProgress|
+    // and |kExpandArrowOpacityEndProgress|.
+    expand_arrow_view_->layer()->SetOpacity(
+        should_restore_opacity
+            ? 1.0f
+            : std::min(
+                  std::max((app_list_view_->GetAppListTransitionProgress() -
+                            kExpandArrowOpacityStartProgress) /
+                               (kExpandArrowOpacityEndProgress -
+                                kExpandArrowOpacityStartProgress),
+                           0.f),
+                  1.0f));
+  }
+
+  GetAppsContainerView()->UpdateOpacity();
+}
+
 bool ContentsView::ShouldLayoutPage(AppListPage* page,
                                     ash::AppListState current_state,
                                     ash::AppListState target_state) const {
diff --git a/ash/app_list/views/contents_view.h b/ash/app_list/views/contents_view.h
index f299906..4ebf3b3 100644
--- a/ash/app_list/views/contents_view.h
+++ b/ash/app_list/views/contents_view.h
@@ -33,6 +33,7 @@
 class AppListMainView;
 class AppsContainerView;
 class AppsGridView;
+class ExpandArrowView;
 class HorizontalPageContainer;
 class PaginationModel;
 class SearchBoxView;
@@ -169,6 +170,9 @@
   // Returns selected view in active page.
   views::View* GetSelectedView() const;
 
+  // Updates the opacity of the items in this view during dragging.
+  void UpdateOpacity();
+
  private:
   // Sets the active launcher page, accounting for whether the change is for
   // search results.
@@ -190,6 +194,15 @@
                        ash::AppListState current_state,
                        ash::AppListState target_state);
 
+  // Updates the expand arrow's opacity based on the progress of transition from
+  // current state to target state.
+  void UpdateExpandArrowOpacity(double progress,
+                                ash::AppListState current_state,
+                                ash::AppListState target_state);
+
+  // Updates the expand arrow's focus behavior based on the current state.
+  void UpdateExpandArrowFocusBehavior(ash::AppListState current_state);
+
   // Adds |view| as a new page to the end of the list of launcher pages. The
   // view is inserted as a child of the ContentsView. There is no name
   // associated with the page. Returns the index of the new page.
@@ -227,6 +240,9 @@
   // Owned by the views hierarchy.
   AppListView* const app_list_view_;
 
+  // Owned by the views hierarchy.
+  ExpandArrowView* expand_arrow_view_ = nullptr;
+
   // Maps State onto |view_model_| indices.
   std::map<ash::AppListState, int> state_to_view_;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 0914a3f..7b99d2b 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1504,6 +1504,12 @@
       <message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_MESSAGE" desc="Text shown in the user pod to remind user that fingerprint unlock is supported">
         Unlock with fingerprint
       </message>
+      <message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_FAILED_MESSAGE" desc="Text shown in the user pod to tell user that couldn't unlock because finger is not recognized">
+        Not recognized
+      </message>
+      <message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_MESSAGE" desc="Text shown in the user pod to tell user that fingerprint unlock has reached maximum attempt">
+        Too many attempts. Try again later.
+      </message>
       <message name="IDS_ASH_LOGIN_TAKE_BREAK_MESSAGE" desc="Message shown to user when unlocking the device is not allowed because the time limit of using the device has reached.">
         Take a break!
       </message>
diff --git a/ash/assistant/assistant_screen_context_controller.cc b/ash/assistant/assistant_screen_context_controller.cc
index 5bd5f17..45a526a 100644
--- a/ash/assistant/assistant_screen_context_controller.cc
+++ b/ash/assistant/assistant_screen_context_controller.cc
@@ -21,6 +21,7 @@
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/snapshot/snapshot.h"
 #include "ui/snapshot/snapshot_aura.h"
+#include "ui/wm/core/focus_controller.h"
 #include "ui/wm/core/window_util.h"
 
 namespace ash {
@@ -143,8 +144,7 @@
 void AssistantScreenContextController::RequestScreenshot(
     const gfx::Rect& rect,
     mojom::AssistantController::RequestScreenshotCallback callback) {
-  // TODO(muyuanli): Handle multi-display when Assistant's behavior is defined.
-  aura::Window* root_window = Shell::GetPrimaryRootWindow();
+  aura::Window* root_window = Shell::Get()->GetRootWindowForNewWindows();
 
   std::unique_ptr<ui::LayerTreeOwner> layer_owner =
       CreateLayerForAssistantSnapshot(root_window);
diff --git a/ash/display/persistent_window_controller.cc b/ash/display/persistent_window_controller.cc
index 29586c6e..7869003a 100644
--- a/ash/display/persistent_window_controller.cc
+++ b/ash/display/persistent_window_controller.cc
@@ -96,7 +96,7 @@
     wm::WindowState* window_state = wm::GetWindowState(window);
     if (!window_state->persistent_window_info())
       continue;
-    const auto& persistent_window_info =
+    PersistentWindowInfo persistent_window_info =
         *window_state->persistent_window_info();
     const int64_t persistent_display_id = persistent_window_info.display_id;
     if (persistent_display_id == screen->GetDisplayNearestWindow(window).id())
diff --git a/ash/frame/custom_frame_view_ash_unittest.cc b/ash/frame/custom_frame_view_ash_unittest.cc
index d7c5f140..ef9305b 100644
--- a/ash/frame/custom_frame_view_ash_unittest.cc
+++ b/ash/frame/custom_frame_view_ash_unittest.cc
@@ -32,6 +32,7 @@
 #include "ui/aura/window_targeter.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/test_accelerator_target.h"
+#include "ui/compositor/test/draw_waiter_for_test.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/image/image_skia.h"
@@ -139,6 +140,32 @@
             delegate->custom_frame_view()->GetHeaderView()->height());
 }
 
+// Regression test for https://crbug.com/839955
+TEST_F(CustomFrameViewAshTest, ActiveStateOfButtonMatchesWidget) {
+  CustomFrameTestWidgetDelegate* delegate = new CustomFrameTestWidgetDelegate;
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
+  FrameCaptionButtonContainerView::TestApi test_api(
+      delegate->custom_frame_view()
+          ->GetHeaderView()
+          ->caption_button_container());
+
+  widget->Show();
+  EXPECT_TRUE(widget->IsActive());
+  // The paint state doesn't change till the next paint.
+  ui::DrawWaiterForTest::WaitForCompositingEnded(
+      widget->GetLayer()->GetCompositor());
+  EXPECT_TRUE(test_api.size_button()->paint_as_active());
+
+  // Activate a different widget so the original one loses activation.
+  std::unique_ptr<views::Widget> widget2 =
+      CreateTestWidget(new CustomFrameTestWidgetDelegate);
+  widget2->Show();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(widget->IsActive());
+  EXPECT_FALSE(test_api.size_button()->paint_as_active());
+}
+
 // Verify that CustomFrameViewAsh returns the correct minimum and maximum frame
 // sizes when the client view does not specify any size constraints.
 TEST_F(CustomFrameViewAshTest, NoSizeConstraints) {
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc
index 561127321..028cea62 100644
--- a/ash/highlighter/highlighter_controller.cc
+++ b/ash/highlighter/highlighter_controller.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/scale_utility.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
+#include "ash/shell_state.h"
 #include "ash/system/palette/palette_utils.h"
 #include "base/metrics/histogram_macros.h"
 #include "chromeos/chromeos_switches.h"
@@ -223,6 +224,10 @@
 
   if (!box.IsEmpty() &&
       gesture_type != HighlighterGestureType::kNotRecognized) {
+    // The window for selection should be the root window to show assistant.
+    Shell::Get()->shell_state()->SetRootWindowForNewWindows(
+        current_window->GetRootWindow());
+
     // TODO(muyuanli): Delete the check when native assistant is default on.
     // This is a temporary workaround to support both ARC-based assistant
     // and native assistant. In ARC-based assistant, we send the rect in pixels
diff --git a/ash/login/resources/BUILD.gn b/ash/login/resources/BUILD.gn
new file mode 100644
index 0000000..1b97ea9
--- /dev/null
+++ b/ash/login/resources/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//tools/grit/grit_rule.gni")
+
+grit("resources") {
+  source = "login_resources.grd"
+  outputs = [
+    "grit/login_resources.h",
+    "grit/login_resources_map.cc",
+    "grit/login_resources_map.h",
+    "login_resources_100_percent.pak",
+    "login_resources_200_percent.pak",
+    "login_resources_300_percent.pak",
+  ]
+}
diff --git a/ash/login/resources/default_100_percent/common/fingerprint_unlock_spinner.png b/ash/login/resources/default_100_percent/common/fingerprint_unlock_spinner.png
new file mode 100644
index 0000000..b902076
--- /dev/null
+++ b/ash/login/resources/default_100_percent/common/fingerprint_unlock_spinner.png
Binary files differ
diff --git a/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png b/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png
new file mode 100644
index 0000000..78ec9fed
--- /dev/null
+++ b/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png
Binary files differ
diff --git a/ash/login/resources/login_resources.grd b/ash/login/resources/login_resources.grd
new file mode 100644
index 0000000..7187ec6
--- /dev/null
+++ b/ash/login/resources/login_resources.grd
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/login_resources.h" type="rc_header" context="default_100_percent">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/login_resources_map.cc" type="resource_map_source" context="default_100_percent" />
+    <output filename="grit/login_resources_map.h" type="resource_map_header" context="default_100_percent" />
+    <output filename="login_resources_100_percent.pak" type="data_package" context="default_100_percent" />
+    <output filename="login_resources_200_percent.pak" type="data_package" context="default_200_percent" />
+    <output filename="login_resources_300_percent.pak" type="data_package" context="default_300_percent" />
+  </outputs>
+  <release seq="1">
+    <structures fallback_to_low_resolution="true">
+      <!-- KEEP THESE IN ALPHABETICAL ORDER!  DO NOT ADD TO RANDOM PLACES JUST
+           BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
+           SAME CONDITIONALS. -->
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER" file="common/fingerprint_unlock_spinner.png" />
+    </structures>
+  </release>
+</grit>
diff --git a/ash/resources/BUILD.gn b/ash/resources/BUILD.gn
index 5f807a8..314655e 100644
--- a/ash/resources/BUILD.gn
+++ b/ash/resources/BUILD.gn
@@ -20,6 +20,7 @@
     sources = [
       "$root_gen_dir/ash/app_list/resources/app_list_resources_${percent}_percent.pak",
       "$root_gen_dir/ash/components/resources/ash_components_resources_${percent}_percent.pak",
+      "$root_gen_dir/ash/login/resources/login_resources_${percent}_percent.pak",
       "$root_gen_dir/ash/public/cpp/resources/ash_public_unscaled_resources.pak",
       "$root_gen_dir/ui/chromeos/resources/ui_chromeos_resources_${percent}_percent.pak",
       "$root_gen_dir/ui/resources/ui_resources_${percent}_percent.pak",
@@ -40,6 +41,7 @@
     deps = [
       "//ash/app_list/resources",
       "//ash/components/resources",
+      "//ash/login/resources",
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
       "//mojo/public/js:resources",
       "//ui/chromeos/resources",
diff --git a/ash/shell.cc b/ash/shell.cc
index 1cc23be7..1511796 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -178,6 +178,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/ws2/gpu_interface_provider.h"
+#include "services/ui/ws2/window_service.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
@@ -393,6 +394,11 @@
 }
 
 // static
+bool Shell::HasRemoteClient(aura::Window* window) {
+  return ui::ws2::WindowService::HasRemoteClient(window);
+}
+
+// static
 Config Shell::GetAshConfig() {
   return Get()->shell_port_->GetAshConfig();
 }
diff --git a/ash/shell.h b/ash/shell.h
index 7a67c84..7400f6d 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -271,6 +271,10 @@
   // Returns true if a system-modal dialog window is currently open.
   static bool IsSystemModalWindowOpen();
 
+  // Whether |window| hosts a remote client (e.g. the keyboard shortcut viewer
+  // app under classic ash, or a browser window under mash).
+  static bool HasRemoteClient(aura::Window* window);
+
   static Config GetAshConfig();
 
   // Registers all ash related local state prefs to the given |registry|.
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index ec1e3f7..9ea3a85 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -175,7 +175,8 @@
 
 // static
 void PaletteTray::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kHasSeenStylus, false);
+  registry->RegisterBooleanPref(prefs::kHasSeenStylus, false,
+                                PrefRegistry::PUBLIC);
 }
 
 // static
diff --git a/base/fuchsia/file_utils.cc b/base/fuchsia/file_utils.cc
index 92bd854d..0e1bfa8 100644
--- a/base/fuchsia/file_utils.cc
+++ b/base/fuchsia/file_utils.cc
@@ -20,16 +20,13 @@
   zx_handle_t handles[FDIO_MAX_HANDLES] = {};
   uint32_t types[FDIO_MAX_HANDLES] = {};
   zx_status_t num_handles =
-      fdio_transfer_fd(file.GetPlatformFile(), 0, handles, types);
+      fdio_transfer_fd(file.TakePlatformFile(), 0, handles, types);
   if (num_handles <= 0) {
     DCHECK_LT(num_handles, 0);
     ZX_DLOG(ERROR, num_handles) << "fdio_transfer_fd";
     return zx::handle();
   }
 
-  // fdio_transfer_fd() has torn-down the file-descriptor, on success.
-  ignore_result(file.TakePlatformFile());
-
   // Wrap the returned handles, so they will be closed on error.
   zx::handle owned_handles[FDIO_MAX_HANDLES];
   for (int i = 0; i < FDIO_MAX_HANDLES; ++i)
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc
index 498cc08..3fe392d 100644
--- a/base/process/launch_fuchsia.cc
+++ b/base/process/launch_fuchsia.cc
@@ -158,11 +158,8 @@
 
   // Add actions to clone handles for any specified paths into the new process'
   // namespace.
-  std::vector<const char*> mapped_paths_cstr;
   if (!options.paths_to_clone.empty() || !options.paths_to_transfer.empty()) {
     DCHECK((options.spawn_flags & FDIO_SPAWN_CLONE_NAMESPACE) == 0);
-    mapped_paths_cstr.reserve(options.paths_to_clone.size() +
-                              options.paths_to_transfer.size());
     transferred_handles.reserve(transferred_handles.size() +
                                 options.paths_to_clone.size() +
                                 options.paths_to_transfer.size());
@@ -171,7 +168,6 @@
       zx::handle handle(path_to_transfer.handle);
       spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
           path_to_transfer.path.value().c_str(), handle.get()));
-      mapped_paths_cstr.push_back(path_to_transfer.path.value().c_str());
       transferred_handles.push_back(std::move(handle));
     }
 
@@ -186,7 +182,6 @@
 
       spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
           path_to_clone.value().c_str(), handle.get()));
-      mapped_paths_cstr.push_back(path_to_clone.value().c_str());
       transferred_handles.push_back(std::move(handle));
     }
   }
diff --git a/chrome/android/java/res/layout/consent_bump_view.xml b/chrome/android/java/res/layout/consent_bump_more_options_view.xml
similarity index 100%
rename from chrome/android/java/res/layout/consent_bump_view.xml
rename to chrome/android/java/res/layout/consent_bump_more_options_view.xml
diff --git a/chrome/android/java/res/layout/downloads_empty_view.xml b/chrome/android/java/res/layout/downloads_empty_view.xml
index a15ffa7..85432d69 100644
--- a/chrome/android/java/res/layout/downloads_empty_view.xml
+++ b/chrome/android/java/res/layout/downloads_empty_view.xml
@@ -9,12 +9,18 @@
     android:layout_height="match_parent" >
 
     <TextView
-        android:id="@+id/empty_view"
+        android:id="@+id/empty"
         android:layout_marginTop="100dp"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center"
+        android:layout_gravity="center"
         android:drawablePadding="3dp"
         android:textAppearance="@style/BlackDisabledText1"/>
 
+    <org.chromium.chrome.browser.widget.LoadingView
+        android:id="@+id/loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center" />
+
 </FrameLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 96b86c6ab..0c237131 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1311,7 +1311,8 @@
      * @return The {@link ModalDialogManager} created for this class.
      */
     protected ModalDialogManager createModalDialogManager() {
-        return new ModalDialogManager(new AppModalPresenter(this), ModalDialogManager.APP_MODAL);
+        return new ModalDialogManager(
+                new AppModalPresenter(this), ModalDialogManager.ModalDialogType.APP);
     }
 
     /**
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 35080a9b..60b6fd7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -672,10 +672,13 @@
 
         if (FeatureUtilities.isTabModelMergingEnabled()) {
             boolean inMultiWindowMode = MultiWindowUtils.getInstance().isInMultiWindowMode(this);
-            // Merge tabs if the activity is not in multi-window mode and mMergeTabsOnResume is true
-            // or unset because the activity is just starting or was destroyed.
-            if (!inMultiWindowMode && (mMergeTabsOnResume == null || mMergeTabsOnResume)) {
+            // Don't need to merge tabs when mMergeTabsOnResume is null (cold start) since they get
+            // merged when TabPersistentStore.loadState(boolean) is called from initializeState().
+            if (!inMultiWindowMode && (mMergeTabsOnResume != null && mMergeTabsOnResume)) {
                 maybeMergeTabs();
+            } else if (!inMultiWindowMode && mMergeTabsOnResume == null) {
+                // This happens on cold start to kill any second activity that might exist.
+                killOtherTask();
             }
             mMergeTabsOnResume = false;
         }
@@ -2278,13 +2281,8 @@
         mTabModelSelectorImpl.saveState();
     }
 
-    /**
-     * Merges tabs from a second ChromeTabbedActivity instance if necesssary and calls
-     * finishAndRemoveTask() on the other activity.
-     */
     @TargetApi(Build.VERSION_CODES.M)
-    @VisibleForTesting
-    public void maybeMergeTabs() {
+    private void killOtherTask() {
         if (!FeatureUtilities.isTabModelMergingEnabled()) return;
 
         Class<?> otherWindowActivityClass =
@@ -2326,12 +2324,23 @@
             // 3. Kill the other activity's task to remove it from Android recents.
             otherActivityTask.finishAndRemoveTask();
         }
+        setMergedInstanceTaskId(getTaskId());
+    }
+
+    /**
+     * Merges tabs from a second ChromeTabbedActivity instance if necesssary and calls
+     * finishAndRemoveTask() on the other activity.
+     */
+    @TargetApi(Build.VERSION_CODES.M)
+    @VisibleForTesting
+    public void maybeMergeTabs() {
+        if (!FeatureUtilities.isTabModelMergingEnabled()) return;
+
+        killOtherTask();
 
         // 4. Ask TabPersistentStore to merge state.
         RecordUserAction.record("Android.MergeState.Live");
         mTabModelSelectorImpl.mergeState();
-
-        setMergedInstanceTaskId(getTaskId());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
index 402a1163..0d673f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
@@ -108,7 +108,7 @@
             assert !(this instanceof JavascriptAppPromptDialog);
             mDialogView = JavascriptModalDialogView.create(this, mTitle, mMessage, null, false,
                     mPositiveButtonTextId, mNegativeButtonTextId);
-            mModalDialogManager.showDialog(mDialogView, ModalDialogManager.TAB_MODAL);
+            mModalDialogManager.showDialog(mDialogView, ModalDialogManager.ModalDialogType.TAB);
         } else {
             LayoutInflater inflater = LayoutInflater.from(context);
             ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.js_modal_dialog, null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 255bd54..9eded38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -273,7 +273,7 @@
         mContext = activity;
         mModalDialogManager = activity.getModalDialogManager();
 
-        mModalDialogManager.showDialog(mDialog, ModalDialogManager.APP_MODAL);
+        mModalDialogManager.showDialog(mDialog, ModalDialogManager.ModalDialogType.APP);
 
         showExpirationDateInputsInputs();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
index a451cb60..de219f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
@@ -52,7 +52,7 @@
         mLocationDialog = DownloadLocationDialog.create(
                 this, activity, totalBytes, dialogType, new File(suggestedPath));
 
-        mModalDialogManager.showDialog(mLocationDialog, ModalDialogManager.APP_MODAL);
+        mModalDialogManager.showDialog(mLocationDialog, ModalDialogManager.ModalDialogType.APP);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 4bca328..1508eba5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -125,12 +125,12 @@
     /**
      * Possible sizes of type-based icons.
      */
-    @IntDef({ICON_SIZE_24_DP, ICON_SIZE_36_DP})
+    @IntDef({IconSize.DP_24, IconSize.DP_36})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface IconSize {}
-
-    public static final int ICON_SIZE_24_DP = 24;
-    public static final int ICON_SIZE_36_DP = 36;
+    public @interface IconSize {
+        int DP_24 = 24;
+        int DP_36 = 36;
+    }
 
     /**
      * Displays the download manager UI. Note the UI is different on tablets and on phones.
@@ -1066,23 +1066,23 @@
         // TODO(huayinz): Make image view size same as icon size so that 36dp icons can be removed.
         switch (fileType) {
             case DownloadFilter.Type.PAGE:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_globe_24dp
-                                                   : R.drawable.ic_globe_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_globe_24dp
+                                                  : R.drawable.ic_globe_36dp;
             case DownloadFilter.Type.VIDEO:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_videocam_24dp
-                                                   : R.drawable.ic_videocam_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_videocam_24dp
+                                                  : R.drawable.ic_videocam_36dp;
             case DownloadFilter.Type.AUDIO:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_music_note_24dp
-                                                   : R.drawable.ic_music_note_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_music_note_24dp
+                                                  : R.drawable.ic_music_note_36dp;
             case DownloadFilter.Type.IMAGE:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_drive_image_24dp
-                                                   : R.drawable.ic_drive_image_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_drive_image_24dp
+                                                  : R.drawable.ic_drive_image_36dp;
             case DownloadFilter.Type.DOCUMENT:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_drive_document_24dp
-                                                   : R.drawable.ic_drive_document_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_drive_document_24dp
+                                                  : R.drawable.ic_drive_document_36dp;
             default:
-                return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_drive_file_24dp
-                                                   : R.drawable.ic_drive_file_36dp;
+                return iconSize == IconSize.DP_24 ? R.drawable.ic_drive_file_24dp
+                                                  : R.drawable.ic_drive_file_36dp;
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
index 6388313..3219cff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
@@ -171,11 +171,11 @@
         if ((item.getItemId() == R.id.close_menu_id
                     || item.getItemId() == R.id.with_settings_close_menu_id)
                 && mIsSeparateActivity) {
-            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MENU_ACTION_CLOSE);
+            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MenuAction.CLOSE);
             mActivity.finish();
             return true;
         } else if (item.getItemId() == R.id.selection_mode_delete_menu_id) {
-            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MENU_ACTION_MULTI_DELETE);
+            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MenuAction.MULTI_DELETE);
             RecordHistogram.recordCount100Histogram(
                     "Android.DownloadManager.Menu.Delete.SelectedCount",
                     mSelectionDelegate.getSelectedItems().size());
@@ -187,7 +187,7 @@
             //                    startActivityForResult() and the selection would only be cleared
             //                    after receiving an OK response. See https://crbug.com/638916.
 
-            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MENU_ACTION_MULTI_SHARE);
+            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MenuAction.MULTI_SHARE);
             RecordHistogram.recordCount100Histogram(
                     "Android.DownloadManager.Menu.Share.SelectedCount",
                     mSelectionDelegate.getSelectedItems().size());
@@ -201,7 +201,7 @@
             // TODO(shaktisahu): Check with UX and remove header.
             mSelectableListLayout.onStartSearch();
             mToolbar.showSearchView();
-            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MENU_ACTION_SEARCH);
+            DownloadManagerUi.recordMenuActionHistogram(DownloadManagerUi.MenuAction.SEARCH);
             RecordUserAction.record("Android.DownloadManager.Search");
             return true;
         } else if (item.getItemId() == R.id.settings_menu_id) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java
index 0e439dd..2692f05a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java
@@ -32,6 +32,9 @@
     private final Map<ContentId, OfflineItem> mItems = new HashMap<>();
     private final ObserverList<OfflineItemFilterObserver> mObservers = new ObserverList<>();
 
+    /** Used to track whether or not the items have been loaded from {@code mProvider} or not. */
+    private boolean mItemsAvailable;
+
     /**
      * Used to track whether or not this is destroyed so we know whether or not to do additional
      * work when outstanding callbacks return.
@@ -48,7 +51,11 @@
         mProvider.addObserver(this);
 
         mProvider.getAllItems(items -> {
-            if (!mDestroyed) onItemsAdded(items);
+            if (mDestroyed) return;
+
+            mItemsAvailable = true;
+            for (OfflineItemFilterObserver observer : mObservers) observer.onItemsAvailable();
+            onItemsAdded(items);
         });
     }
 
@@ -70,6 +77,11 @@
     }
 
     @Override
+    public boolean areItemsAvailable() {
+        return mItemsAvailable;
+    }
+
+    @Override
     public void addObserver(OfflineItemFilterObserver observer) {
         mObservers.addObserver(observer);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java
new file mode 100644
index 0000000..ff260883
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home;
+
+/** Helper class to expose the status of the offline prefetch feature. */
+public class PrefetchStatusProvider {
+    /** @return Whether or not the offline prefetch feature is enabled. */
+    public boolean enabled() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
new file mode 100644
index 0000000..fab8c7d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.empty;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
+import org.chromium.chrome.browser.download.home.empty.EmptyProperties.State;
+import org.chromium.chrome.browser.download.home.filter.FilterCoordinator;
+import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
+import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterObserver;
+import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.components.offline_items_collection.OfflineItem;
+
+import java.util.Collection;
+
+/** A class that determines whether an empty view should be shown and inserts into the model. */
+public class EmptyCoordinator implements OfflineItemFilterObserver, FilterCoordinator.Observer {
+    private final PrefetchStatusProvider mPrefetchStatusProvider;
+    private final OfflineItemFilterSource mSource;
+
+    private final PropertyModel mModel;
+    private final EmptyView mView;
+
+    private boolean mShowingPrefetch = false;
+
+    /** Creates a {@link EmptyCoordinator} instance that monitors {@code source}. */
+    public EmptyCoordinator(Context context, PrefetchStatusProvider prefetchStatusProvider,
+            OfflineItemFilterSource source) {
+        mPrefetchStatusProvider = prefetchStatusProvider;
+        mSource = source;
+
+        mSource.addObserver(this);
+
+        mModel = new PropertyModel(EmptyProperties.ALL_KEYS);
+        mView = new EmptyView(context);
+        mModel.addObserver(
+                new PropertyModelChangeProcessor<>(mModel, mView, new EmptyViewBinder()));
+
+        calculateState();
+    }
+
+    /** @return The {@link View} that represents the empty screen. */
+    public View getView() {
+        return mView.getView();
+    }
+
+    // OfflineItemFilterObserver implementation.
+    @Override
+    public void onItemsAdded(Collection<OfflineItem> items) {
+        calculateState();
+    }
+
+    @Override
+    public void onItemsRemoved(Collection<OfflineItem> items) {
+        calculateState();
+    }
+
+    @Override
+    public void onItemUpdated(OfflineItem oldItem, OfflineItem item) {}
+
+    @Override
+    public void onItemsAvailable() {
+        calculateState();
+    }
+
+    // FilterCoordinator.Observer implementation.
+    @Override
+    public void onFilterChanged(@FilterType int selectedTab) {
+        mShowingPrefetch = selectedTab == FilterType.PREFETCHED;
+        calculateState();
+    }
+
+    private void calculateState() {
+        @State
+        int state;
+        if (!mSource.areItemsAvailable()) {
+            state = State.LOADING;
+        } else if (mSource.getItems().isEmpty()) {
+            state = State.EMPTY;
+
+            @StringRes
+            int textId;
+            @DrawableRes
+            int iconId;
+            if (mShowingPrefetch) {
+                iconId = R.drawable.ic_library_news_feed;
+
+                if (mPrefetchStatusProvider.enabled()) {
+                    textId = R.string.download_manager_prefetch_tab_empty;
+                } else {
+                    textId = R.string.download_manager_enable_prefetch_message;
+                }
+            } else {
+                iconId = R.drawable.downloads_big;
+                textId = R.string.download_manager_ui_empty;
+            }
+
+            mModel.setValue(EmptyProperties.EMPTY_TEXT_RES_ID, textId);
+            mModel.setValue(EmptyProperties.EMPTY_ICON_RES_ID, iconId);
+        } else {
+            state = State.GONE;
+        }
+
+        mModel.setValue(EmptyProperties.STATE, state);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java
new file mode 100644
index 0000000..40caa38
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.empty;
+
+import android.support.annotation.IntDef;
+
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** The properties required to build a {@link EmptyView}. */
+interface EmptyProperties {
+    @IntDef({State.LOADING, State.EMPTY, State.GONE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+        int LOADING = 0;
+        int EMPTY = 1;
+        int GONE = 2;
+    }
+
+    /** The current state of the empty view. */
+    public static final IntPropertyKey STATE = new IntPropertyKey();
+
+    /** The current text resource to use for the empty view. */
+    public static final IntPropertyKey EMPTY_TEXT_RES_ID = new IntPropertyKey();
+
+    /** The current icon resource to use for the empty view. */
+    public static final IntPropertyKey EMPTY_ICON_RES_ID = new IntPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {STATE, EMPTY_TEXT_RES_ID, EMPTY_ICON_RES_ID};
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java
new file mode 100644
index 0000000..7c5fe1e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.empty;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.download.home.empty.EmptyProperties.State;
+import org.chromium.chrome.browser.widget.LoadingView;
+
+/** A view that represents the visuals required for the empty state of the download home list. */
+class EmptyView {
+    private final ViewGroup mView;
+    private final TextView mEmptyView;
+    private final LoadingView mLoadingView;
+
+    /** Creates a new {@link EmptyView} instance from {@code context}. */
+    public EmptyView(Context context) {
+        mView = (ViewGroup) LayoutInflater.from(context).inflate(
+                R.layout.downloads_empty_view, null);
+        mEmptyView = (TextView) mView.findViewById(R.id.empty);
+        mLoadingView = (LoadingView) mView.findViewById(R.id.loading);
+    }
+
+    /** The Android {@link View} representing the empty view. */
+    public View getView() {
+        return mView;
+    }
+
+    /** Sets the internal UI based on {@code state}. */
+    public void setState(@State int state) {
+        mEmptyView.setVisibility(state == State.EMPTY ? View.VISIBLE : View.INVISIBLE);
+
+        if (state == State.LOADING) {
+            mLoadingView.showLoadingUI();
+        } else {
+            mLoadingView.hideLoadingUI();
+        }
+    }
+
+    /** Sets the text resource to use for the empty view. */
+    public void setEmptyText(@StringRes int textId) {
+        mEmptyView.setText(textId);
+    }
+
+    /** Sets the icon resource to use for the empty view. */
+    public void setEmptyIcon(@DrawableRes int iconId) {
+        Drawable drawable = VectorDrawableCompat.create(
+                mView.getResources(), iconId, mView.getContext().getTheme());
+        mEmptyView.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
new file mode 100644
index 0000000..e07d9525
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.empty;
+
+import org.chromium.chrome.browser.modelutil.PropertyKey;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+
+/**
+ * A helper {@link ViewBinder} responsible for gluing {@link EmptyProperties} to
+ * {@link EmptyView}.
+ */
+class EmptyViewBinder implements ViewBinder<PropertyModel, EmptyView, PropertyKey> {
+    @Override
+    public void bind(PropertyModel model, EmptyView view, PropertyKey propertyKey) {
+        if (propertyKey == EmptyProperties.STATE) {
+            view.setState(model.getValue(EmptyProperties.STATE));
+        } else if (propertyKey == EmptyProperties.EMPTY_TEXT_RES_ID) {
+            view.setEmptyText(model.getValue(EmptyProperties.EMPTY_TEXT_RES_ID));
+        } else if (propertyKey == EmptyProperties.EMPTY_ICON_RES_ID) {
+            view.setEmptyIcon(model.getValue(EmptyProperties.EMPTY_ICON_RES_ID));
+        }
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
index af18f36..3145a04 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
@@ -9,9 +9,9 @@
 import android.view.View;
 
 import org.chromium.base.ObserverList;
+import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.chips.ChipsCoordinator;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 
@@ -33,6 +33,7 @@
         void onFilterChanged(@FilterType int selectedTab);
     }
 
+    private final PrefetchStatusProvider mPrefetchStatusProvider;
     private final ObserverList<Observer> mObserverList = new ObserverList<>();
     private final PropertyModel mModel;
     private final FilterViewBinder mViewBinder;
@@ -45,22 +46,22 @@
      * Builds a new FilterCoordinator.
      * @param context The context to build the views and pull parameters from.
      */
-    public FilterCoordinator(Context context, OfflineItemFilterSource chipFilterSource) {
+    public FilterCoordinator(Context context, PrefetchStatusProvider prefetchStatusProvider,
+            OfflineItemFilterSource chipFilterSource) {
+        mPrefetchStatusProvider = prefetchStatusProvider;
         mChipsProvider = new FilterChipsProvider(type -> handleChipSelected(), chipFilterSource);
         mChipsCoordinator = new ChipsCoordinator(context, mChipsProvider);
 
         mModel = new PropertyModel(FilterProperties.ALL_KEYS);
         mViewBinder = new FilterViewBinder();
         mView = new FilterView(context);
-        mModel.addObserver(new PropertyModelChangeProcessor<PropertyModel, FilterView, PropertyKey>(
-                mModel, mView, mViewBinder));
+        mModel.addObserver(new PropertyModelChangeProcessor<>(mModel, mView, mViewBinder));
 
         mModel.setValue(
                 FilterProperties.CHANGE_LISTENER, selectedTab -> handleTabSelected(selectedTab));
         selectTab(TabType.FILES);
 
-        // TODO(shaktisahu): Check if prefetch UI is enabled.
-        mModel.setValue(FilterProperties.SHOW_TABS, true);
+        mModel.setValue(FilterProperties.SHOW_TABS, mPrefetchStatusProvider.enabled());
     }
 
     /** @return The {@link View} representing this widget. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilter.java
index b45079bc..36f22f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilter.java
@@ -70,13 +70,18 @@
         addItems(mSource.getItems());
     }
 
-    // OfflineItemSource implementation.
+    // OfflineItemFilterSource implementation.
     @Override
     public Set<OfflineItem> getItems() {
         return mItems;
     }
 
     @Override
+    public boolean areItemsAvailable() {
+        return mSource.areItemsAvailable();
+    }
+
+    @Override
     public void addObserver(OfflineItemFilterObserver observer) {
         mObservers.addObserver(observer);
     }
@@ -112,6 +117,11 @@
         for (OfflineItemFilterObserver obs : mObservers) obs.onItemUpdated(oldItem, item);
     }
 
+    @Override
+    public void onItemsAvailable() {
+        for (OfflineItemFilterObserver obs : mObservers) obs.onItemsAvailable();
+    }
+
     // Helper method to help incorporate a collection of items into this filtered version.
     private void addItems(Collection<OfflineItem> items) {
         Set<OfflineItem> added = new HashSet<>();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterObserver.java
index 7f81c8e..f8ab92ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterObserver.java
@@ -32,4 +32,11 @@
      * @param item    The new {@link OfflineItem} after the update.
      */
     void onItemUpdated(OfflineItem oldItem, OfflineItem item);
+
+    /**
+     * Called when the underlying {@link OfflineItem}s are available.  This is meant to help detect
+     * a difference between an empty set and a set that is not loaded yet.
+     */
+    default void
+        onItemsAvailable() {}
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterSource.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterSource.java
index 791f47e..7a2875d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterSource.java
@@ -20,6 +20,12 @@
     Collection<OfflineItem> getItems();
 
     /**
+     * @return Whether or not the items are available, which is meant to help determine the
+     * difference between an empty set and a set that hasn't loaded yet.
+     */
+    boolean areItemsAvailable();
+
+    /**
      * Registers {@code observer} to be notified of changes to the item collection managed by this
      * source.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
index cbce6f2..87a4c4b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
@@ -8,6 +8,8 @@
 import android.view.View;
 
 import org.chromium.base.Callback;
+import org.chromium.chrome.browser.download.home.PrefetchStatusProvider;
+import org.chromium.chrome.browser.download.home.empty.EmptyCoordinator;
 import org.chromium.chrome.browser.download.home.filter.FilterCoordinator;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
@@ -43,7 +45,7 @@
     }
 
     private final FilterCoordinator mFilterCoordinator;
-    private final EmptyViewCoordinator mEmptyViewCoordinator;
+    private final EmptyCoordinator mEmptyCoordinator;
     private final DateOrderedListMediator mMediator;
     private final DateOrderedListView mView;
 
@@ -61,20 +63,24 @@
             OfflineContentProvider provider, DeleteController deleteController,
             SelectionDelegate<ListItem> selectionDelegate,
             FilterCoordinator.Observer filterObserver) {
+        // TODO(shaktisahu): Use a real provider/have this provider query the real data source.
+        PrefetchStatusProvider prefetchProvider = new PrefetchStatusProvider();
+
         ListItemModel model = new ListItemModel();
         DecoratedListItemModel decoratedModel = new DecoratedListItemModel(model);
         mView = new DateOrderedListView(context, decoratedModel);
         mMediator = new DateOrderedListMediator(
                 offTheRecord, provider, deleteController, selectionDelegate, model);
 
-        // Hook up the FilterCoordinator with our mediator.
-        mFilterCoordinator = new FilterCoordinator(context, mMediator.getFilterSource());
+        mEmptyCoordinator =
+                new EmptyCoordinator(context, prefetchProvider, mMediator.getEmptySource());
+
+        mFilterCoordinator =
+                new FilterCoordinator(context, prefetchProvider, mMediator.getFilterSource());
         mFilterCoordinator.addObserver(mMediator::onFilterTypeSelected);
         mFilterCoordinator.addObserver(filterObserver);
+        mFilterCoordinator.addObserver(mEmptyCoordinator);
 
-        mEmptyViewCoordinator =
-                new EmptyViewCoordinator(context, decoratedModel, mMediator.getFilterSource());
-        mFilterCoordinator.addObserver(mEmptyViewCoordinator::onFilterTypeSelected);
         decoratedModel.setHeader(new ViewListItem(Long.MAX_VALUE, mFilterCoordinator.getView()));
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
index a689c0d..071c0ad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
@@ -138,6 +138,14 @@
         return mDeleteUndoFilter;
     }
 
+    /**
+     * @return The {@link OfflineItemFilterSource} that should be used to determine whether there
+     * are no items and empty view should be shown.
+     */
+    public OfflineItemFilterSource getEmptySource() {
+        return mTypeFilter;
+    }
+
     private void onDeleteItem(OfflineItem item) {
         onDeleteItems(CollectionUtil.newArrayList(item));
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/EmptyViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/EmptyViewCoordinator.java
deleted file mode 100644
index 53e34ba0..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/EmptyViewCoordinator.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.download.home.list;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.graphics.drawable.VectorDrawableCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.download.home.filter.Filters;
-import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterObserver;
-import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
-import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
-import org.chromium.components.offline_items_collection.OfflineItem;
-
-import java.util.Collection;
-
-/**
- * A class that determines whether an empty view should be shown and inserts into the model.
- */
-class EmptyViewCoordinator implements OfflineItemFilterObserver {
-    private final Context mContext;
-    private final DecoratedListItemModel mDecoratedModel;
-    private final OfflineItemFilterSource mSource;
-    private final Handler mHandler = new Handler();
-
-    private ViewListItem mEmptyViewItem;
-
-    private @Filters.FilterType int mCurrentFilter;
-
-    /** Creates a {@link EmptyViewCoordinator} instance that wraos {@code model}. */
-    public EmptyViewCoordinator(Context context, DecoratedListItemModel decoratedModel,
-            OfflineItemFilterSource source) {
-        mContext = context;
-        mDecoratedModel = decoratedModel;
-        mSource = source;
-        mSource.addObserver(this);
-        mHandler.post(() -> onFilterTypeSelected(mCurrentFilter));
-    }
-
-    public void onFilterTypeSelected(@Filters.FilterType int filter) {
-        boolean hasEmptyView = mEmptyViewItem != null;
-        if (filter == mCurrentFilter && hasEmptyView == isEmpty()) return;
-
-        mCurrentFilter = filter;
-        updateForEmptyView();
-    }
-
-    private boolean isEmpty() {
-        boolean showingPrefetch = mCurrentFilter == Filters.FilterType.PREFETCHED;
-        for (OfflineItem item : mSource.getItems()) {
-            if (showingPrefetch && item.isSuggested) return false;
-            if (!showingPrefetch && !item.isSuggested) return false;
-        }
-
-        return true;
-    }
-
-    private void updateForEmptyView() {
-        mEmptyViewItem = isEmpty() ? createEmptyView() : null;
-        mDecoratedModel.setEmptyView(mEmptyViewItem);
-    }
-
-    private ViewListItem createEmptyView() {
-        boolean showingPrefetch = mCurrentFilter == Filters.FilterType.PREFETCHED;
-        // TODO(shaktisahu): Supply correct value for prefetch settings.
-        boolean prefetchEnabled = true;
-        View emptyView = LayoutInflater.from(mContext).inflate(R.layout.downloads_empty_view, null);
-
-        Drawable emptyDrawable = VectorDrawableCompat.create(mContext.getResources(),
-                showingPrefetch ? R.drawable.ic_library_news_feed : R.drawable.downloads_big,
-                mContext.getTheme());
-
-        TextView emptyTextView = emptyView.findViewById(R.id.empty_view);
-        emptyTextView.setText(showingPrefetch
-                        ? (prefetchEnabled ? R.string.download_manager_prefetch_tab_empty
-                                           : R.string.download_manager_enable_prefetch_message)
-                        : R.string.download_manager_ui_empty);
-        emptyTextView.setCompoundDrawablesWithIntrinsicBounds(null, emptyDrawable, null, null);
-
-        return new ViewListItem(Long.MAX_VALUE - 1, emptyView);
-    }
-
-    // OfflineItemFilterObserver implementation.
-    @Override
-    public void onItemsAdded(Collection<OfflineItem> items) {
-        mHandler.post(() -> onFilterTypeSelected(mCurrentFilter));
-    }
-
-    @Override
-    public void onItemsRemoved(Collection<OfflineItem> items) {
-        mHandler.post(() -> onFilterTypeSelected(mCurrentFilter));
-    }
-
-    @Override
-    public void onItemUpdated(OfflineItem oldItem, OfflineItem item) {
-        mHandler.post(() -> onFilterTypeSelected(mCurrentFilter));
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
index 36c2dd5..5103042 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
@@ -124,6 +124,6 @@
     /** @return A drawable resource id representing an icon for {@code item}. */
     public static @DrawableRes int getIconForItem(OfflineItem item) {
         return DownloadUtils.getIconResId(Filters.offlineItemFilterToDownloadFilter(item.filter),
-                DownloadUtils.ICON_SIZE_24_DP);
+                DownloadUtils.IconSize.DP_24);
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
index 1b85f0697..3ca728ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -246,7 +246,7 @@
         int fileType = item.getFilterType();
 
         // Pick what icon to display for the item.
-        mIconResId = DownloadUtils.getIconResId(fileType, DownloadUtils.ICON_SIZE_24_DP);
+        mIconResId = DownloadUtils.getIconResId(fileType, DownloadUtils.IconSize.DP_24);
 
         // Request a thumbnail for the file to be sent to the ThumbnailCallback. This will happen
         // immediately if the thumbnail is cached or asynchronously if it has to be fetched from a
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index 8ce85868..c7d4da4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -58,6 +58,8 @@
 import org.chromium.ui.widget.ViewRectProvider;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -164,18 +166,19 @@
 
     // Please treat this list as append only and keep it in sync with
     // Android.DownloadManager.Menu.Actions in enums.xml.
-    @IntDef({MENU_ACTION_CLOSE, MENU_ACTION_MULTI_DELETE, MENU_ACTION_MULTI_SHARE,
-            MENU_ACTION_SHOW_INFO, MENU_ACTION_HIDE_INFO, MENU_ACTION_SEARCH})
-    public @interface MenuAction {}
-
-    // TODO(shaktisahu): Move these to new download home and make them private.
-    public static final int MENU_ACTION_CLOSE = 0;
-    public static final int MENU_ACTION_MULTI_DELETE = 1;
-    public static final int MENU_ACTION_MULTI_SHARE = 2;
-    public static final int MENU_ACTION_SHOW_INFO = 3;
-    public static final int MENU_ACTION_HIDE_INFO = 4;
-    public static final int MENU_ACTION_SEARCH = 5;
-    public static final int MENU_ACTION_BOUNDARY = 6;
+    @IntDef({MenuAction.CLOSE, MenuAction.MULTI_DELETE, MenuAction.MULTI_SHARE,
+            MenuAction.SHOW_INFO, MenuAction.HIDE_INFO, MenuAction.SEARCH})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MenuAction {
+        // TODO(shaktisahu): Move these to new download home and make them private.
+        int CLOSE = 0;
+        int MULTI_DELETE = 1;
+        int MULTI_SHARE = 2;
+        int SHOW_INFO = 3;
+        int HIDE_INFO = 4;
+        int SEARCH = 5;
+        int NUM_ENTRIES = 6;
+    }
 
     private static final int PREFETCH_BUNDLE_OPEN_DELAY_MS = 500;
 
@@ -373,7 +376,7 @@
         if ((item.getItemId() == R.id.close_menu_id
                     || item.getItemId() == R.id.with_settings_close_menu_id)
                 && mIsSeparateActivity) {
-            recordMenuActionHistogram(MENU_ACTION_CLOSE);
+            recordMenuActionHistogram(MenuAction.CLOSE);
             mActivity.finish();
             return true;
         } else if (item.getItemId() == R.id.selection_mode_delete_menu_id) {
@@ -381,7 +384,7 @@
                     mBackendProvider.getSelectionDelegate().getSelectedItems();
             mBackendProvider.getSelectionDelegate().clearSelection();
 
-            recordMenuActionHistogram(MENU_ACTION_MULTI_DELETE);
+            recordMenuActionHistogram(MenuAction.MULTI_DELETE);
             RecordHistogram.recordCount100Histogram(
                     "Android.DownloadManager.Menu.Delete.SelectedCount", items.size());
 
@@ -395,7 +398,7 @@
             //                    after receiving an OK response. See crbug.com/638916.
             mBackendProvider.getSelectionDelegate().clearSelection();
 
-            recordMenuActionHistogram(MENU_ACTION_MULTI_SHARE);
+            recordMenuActionHistogram(MenuAction.MULTI_SHARE);
             RecordHistogram.recordCount100Histogram(
                     "Android.DownloadManager.Menu.Share.SelectedCount", items.size());
 
@@ -403,11 +406,11 @@
             return true;
         } else if (item.getItemId() == mInfoMenuId) {
             boolean showInfo = !mHistoryAdapter.shouldShowStorageInfoHeader();
-            recordMenuActionHistogram(showInfo ? MENU_ACTION_SHOW_INFO : MENU_ACTION_HIDE_INFO);
+            recordMenuActionHistogram(showInfo ? MenuAction.SHOW_INFO : MenuAction.HIDE_INFO);
             enableStorageInfoHeader(showInfo);
             return true;
         } else if (item.getItemId() == mSearchMenuId) {
-            recordMenuActionHistogram(MENU_ACTION_SEARCH);
+            recordMenuActionHistogram(MenuAction.SEARCH);
             // The header should be removed as soon as a search is started. It will be added back in
             // DownloadHistoryAdatper#filter() when the search is ended.
             mHistoryAdapter.removeHeader();
@@ -621,7 +624,7 @@
 
     public static void recordMenuActionHistogram(@MenuAction int action) {
         RecordHistogram.recordEnumeratedHistogram(
-                "Android.DownloadManager.Menu.Action", action, MENU_ACTION_BOUNDARY);
+                "Android.DownloadManager.Menu.Action", action, MenuAction.NUM_ENTRIES);
     }
 
     private void shareItems(final List<DownloadHistoryItemWrapper> items) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
index fafe21c..ff7e69c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
@@ -9,6 +9,7 @@
 import android.os.Bundle;
 
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.cards.SignInPromo;
 import org.chromium.chrome.browser.signin.SigninAccessPoint;
 import org.chromium.chrome.browser.signin.SigninFragmentBase;
@@ -30,12 +31,11 @@
         String forceAccountTo =
                 freProperties.getString(AccountFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO);
         if (forceAccountTo == null) {
-            mArguments = createArguments(SigninAccessPoint.START_PAGE, null);
+            mArguments = createArguments(null);
         } else {
             @ChildAccountStatus.Status int childAccountStatus =
                     freProperties.getInt(AccountFirstRunFragment.CHILD_ACCOUNT_STATUS);
-            mArguments = createArgumentsForForcedSigninFlow(
-                    SigninAccessPoint.START_PAGE, forceAccountTo, childAccountStatus);
+            mArguments = createArgumentsForForcedSigninFlow(forceAccountTo, childAccountStatus);
         }
 
         RecordUserAction.record("MobileFre.SignInShown");
@@ -71,4 +71,9 @@
         getPageDelegate().advanceToNextPage();
         callback.run();
     }
+
+    @Override
+    protected int getNegativeButtonTextId() {
+        return R.string.no_thanks;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
index 8ed40f91..be091db1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
@@ -80,7 +80,7 @@
         mModalDialogManager = activity.getModalDialogManager();
         mDialogView = JavascriptModalDialogView.create(this, mTitle, mMessage, mDefaultPromptText,
                 false, mPositiveButtonTextId, mNegativeButtonTextId);
-        mModalDialogManager.showDialog(mDialogView, ModalDialogManager.TAB_MODAL);
+        mModalDialogManager.showDialog(mDialogView, ModalDialogManager.ModalDialogType.TAB);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java
index c3df5d1e..041da74 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java
@@ -13,6 +13,7 @@
 import com.google.android.gms.common.api.ResultCallback;
 
 import org.chromium.base.Log;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaController;
 import org.chromium.chrome.browser.media.router.cast.CastSessionUtil;
 import org.chromium.chrome.browser.media.ui.MediaNotificationInfo;
@@ -25,7 +26,7 @@
 public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpdatedListener,
                                                  RemoteMediaPlayer.OnStatusUpdatedListener,
                                                  ResultCallback<MediaChannelResult>,
-                                                 MediaController {
+                                                 MediaController, FlingingController {
     private static final String TAG = "MediaRemoting";
 
     private final CastDevice mCastDevice;
@@ -208,4 +209,15 @@
                     result.getStatus().getStatusCode());
         }
     }
+
+    // FlingingController implementation
+    @Override
+    public MediaController getMediaController() {
+        return this;
+    }
+
+    @Override
+    public long getApproximateCurrentTime() {
+        return mMediaPlayer.getApproximateStreamPosition();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
index 5256a99f..68d24b89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -394,7 +394,7 @@
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
         if (provider == null) return null;
 
-        MediaController controller = provider.getMediaController(routeId);
+        FlingingController controller = provider.getFlingingController(routeId);
         if (controller == null) return null;
 
         return new FlingingControllerBridge(controller);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingController.java
new file mode 100644
index 0000000..af233eb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingController.java
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.media.router;
+
+/**
+ * Interface that groups all the necessary hooks to control media being flung to a Cast device,
+ * as part of RemotePlayback.
+ * This interface should be the same as media/base/flinging_controller.h.
+ * TODO(tguilbert): add MediaStatusObserver.
+ */
+public interface FlingingController {
+    /**
+     * Gets the media controller through which we can send commands to the Cast device.
+     */
+    public MediaController getMediaController();
+
+    /**
+     * Gets the current media time. Implementers may sacrifice precision in order to avoid a
+     * round-trip query to Cast devices (see gms.cast.RemoteMediaPlayer's
+     * getApproximateStreamPosition() for example).
+     */
+    public long getApproximateCurrentTime();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
index e814562..be38210 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java
@@ -8,40 +8,40 @@
 import org.chromium.base.annotations.JNINamespace;
 
 /**
- * A wrapper around a MediaController that allows the native code to use it, and to subscribe to
- * status changes. See chrome/browser/media/android/remote/flinging_controller_bridge.h for the
+ * A wrapper around a FlingingController that allows the native code to use it
+ * See chrome/browser/media/android/remote/flinging_controller_bridge.h for the
  * corresponding native code.
  */
 @JNINamespace("media_router")
 public class FlingingControllerBridge {
-    private final MediaController mMediaController;
+    private final FlingingController mFlingingController;
 
-    public FlingingControllerBridge(MediaController mediaController) {
-        mMediaController = mediaController;
+    public FlingingControllerBridge(FlingingController flingingController) {
+        mFlingingController = flingingController;
     }
 
     @CalledByNative
     public void play() {
-        mMediaController.play();
+        mFlingingController.getMediaController().play();
     }
 
     @CalledByNative
     public void pause() {
-        mMediaController.pause();
+        mFlingingController.getMediaController().pause();
     }
 
     @CalledByNative
     public void setMute(boolean mute) {
-        mMediaController.setMute(mute);
+        mFlingingController.getMediaController().setMute(mute);
     }
 
     @CalledByNative
     public void setVolume(float volume) {
-        mMediaController.setVolume(volume);
+        mFlingingController.getMediaController().setVolume(volume);
     }
 
     @CalledByNative
     public void seek(long positionInMs) {
-        mMediaController.seek(positionInMs);
+        mFlingingController.getMediaController().seek(positionInMs);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaController.java
index 1e097c9..7855c22c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaController.java
@@ -7,7 +7,6 @@
 /**
  * Generic interface used to control the playback of media content.
  * Changes to the media content status will be propagated via the MediaStatusObserver interface.
- * TODO(tguilbert): Add MediaStatusObserver class to allow client to listen for changes.
  */
 public interface MediaController {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java
index 1809168b..074a3f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java
@@ -87,10 +87,10 @@
     void sendStringMessage(String routeId, String message, int nativeCallbackId);
 
     /**
-     * Returns a MediaController for the given route ID.
-     * Returns null if no MediaController can be retrieved from the given route ID.
+     * Returns a FlingingController for the given route ID.
+     * Returns null if no FlingingController can be retrieved from the given route ID.
      * @param routeId The id of the route.
      */
     @Nullable
-    MediaController getMediaController(String routeId);
+    FlingingController getFlingingController(String routeId);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
index d8d26d9..ba838de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.media.router.ChromeMediaRouter;
 import org.chromium.chrome.browser.media.router.DiscoveryCallback;
 import org.chromium.chrome.browser.media.router.DiscoveryDelegate;
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaRouteManager;
 import org.chromium.chrome.browser.media.router.MediaRouteProvider;
 import org.chromium.chrome.browser.media.router.MediaSink;
@@ -196,7 +196,7 @@
 
     @Override
     @Nullable
-    public MediaController getMediaController(String routeId) {
+    public FlingingController getFlingingController(String routeId) {
         // Not implemented.
         return null;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
index ce7871fb..2b0d7747 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
@@ -11,7 +11,7 @@
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.media.router.DiscoveryCallback;
 import org.chromium.chrome.browser.media.router.DiscoveryDelegate;
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaRoute;
 import org.chromium.chrome.browser.media.router.MediaRouteManager;
 import org.chromium.chrome.browser.media.router.MediaRouteProvider;
@@ -233,7 +233,7 @@
 
     @Override
     @Nullable
-    public MediaController getMediaController(String routeId) {
+    public FlingingController getFlingingController(String routeId) {
         return null;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java
index 8062fcc..0f1007c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java
@@ -7,7 +7,7 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 
 import java.util.Set;
 
@@ -133,5 +133,5 @@
      * Returns a controller for the media content.
      */
     @Nullable
-    MediaController getMediaController();
+    FlingingController getFlingingController();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
index a5010a30d..c75f014 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
@@ -20,7 +20,7 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaSource;
 import org.chromium.chrome.browser.media.ui.MediaNotificationInfo;
 import org.chromium.chrome.browser.media.ui.MediaNotificationListener;
@@ -34,6 +34,8 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.annotation.Nullable;
+
 /**
  * A wrapper around the established Cast application session.
  */
@@ -485,8 +487,9 @@
     }
 
     @Override
-    public MediaController getMediaController() {
-        // MediaController is not used with the CastSessionImpl.
+    @Nullable
+    public FlingingController getFlingingController() {
+        // FlingingController is not used with the CastSessionImpl.
         return null;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java
index a38c25fc..c60ce94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java
@@ -17,7 +17,7 @@
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.media.remote.RemoteMediaPlayerWrapper;
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaSource;
 import org.chromium.chrome.browser.media.router.cast.CastMessageHandler;
 import org.chromium.chrome.browser.media.router.cast.CastSession;
@@ -214,7 +214,7 @@
     public void onMediaSessionAction(int action) {}
 
     @Override
-    public MediaController getMediaController() {
+    public FlingingController getFlingingController() {
         return mMediaPlayerWrapper;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java
index 573cc3f..a86374091 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java
@@ -8,7 +8,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.media.router.ChromeMediaRouter;
-import org.chromium.chrome.browser.media.router.MediaController;
+import org.chromium.chrome.browser.media.router.FlingingController;
 import org.chromium.chrome.browser.media.router.MediaRoute;
 import org.chromium.chrome.browser.media.router.MediaRouteManager;
 import org.chromium.chrome.browser.media.router.MediaRouteProvider;
@@ -145,17 +145,17 @@
 
     @Override
     @Nullable
-    public MediaController getMediaController(String routeId) {
-        // We cannot return a MediaController if we don't have a session.
+    public FlingingController getFlingingController(String routeId) {
+        // We cannot return a FlingingController if we don't have a session.
         if (mSession == null) return null;
 
         // Don't return controllers for stale routes.
-        if (mRoutes.get(routeId) == null) return null;
+        if (!mRoutes.containsKey(routeId)) return null;
 
         // RemotePlayback does not support joining routes, which means we only
         // have a single route active at a time. If we have a a valid CastSession
         // and the route ID is current, this means that the given |mSession|
-        // corresponds to the route ID, and it is ok to return the MediaController.
-        return mSession.getMediaController();
+        // corresponds to the route ID, and it is ok to return the FlingingController.
+        return mSession.getFlingingController();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java
index 924cc1b6..2475ae7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java
@@ -89,16 +89,14 @@
         protected abstract void removeDialogView(View dialogView);
     }
 
+    @IntDef({ModalDialogType.APP, ModalDialogType.TAB})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({APP_MODAL, TAB_MODAL})
-    public @interface ModalDialogType {}
-
-    /**
-     * The integer assigned to each type represents its priority. A smaller number represents a
-     * higher priority type of dialog.
-     */
-    public static final int APP_MODAL = 0;
-    public static final int TAB_MODAL = 1;
+    public @interface ModalDialogType {
+        // The integer assigned to each type represents its priority. A smaller number represents a
+        // higher priority type of dialog.
+        int APP = 0;
+        int TAB = 1;
+    }
 
     /** Mapping of the {@link Presenter}s and the type of dialogs they are showing. */
     private final SparseArray<Presenter> mPresenters = new SparseArray<>();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
index 66bbee0..18b150d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.modaldialog;
 
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
@@ -27,7 +28,7 @@
         @Override
         public void onDestroyed(Tab tab) {
             if (mActiveTab == tab) {
-                mManager.cancelAllDialogs(ModalDialogManager.TAB_MODAL);
+                mManager.cancelAllDialogs(ModalDialogType.TAB);
                 mActiveTab = null;
             }
         }
@@ -47,7 +48,7 @@
     public TabModalLifetimeHandler(ChromeActivity activity, ModalDialogManager manager) {
         mManager = manager;
         mPresenter = new TabModalPresenter(activity);
-        mManager.registerPresenter(mPresenter, ModalDialogManager.TAB_MODAL);
+        mManager.registerPresenter(mPresenter, ModalDialogType.TAB);
         mHasBottomControls = activity.getBottomSheet() != null;
 
         TabModelSelector tabModelSelector = activity.getTabModelSelector();
@@ -57,7 +58,7 @@
                 // Do not use lastId here since it can be the selected tab's ID if model is switched
                 // inside tab switcher.
                 if (tab != mActiveTab) {
-                    mManager.cancelAllDialogs(ModalDialogManager.TAB_MODAL);
+                    mManager.cancelAllDialogs(ModalDialogType.TAB);
                     if (mActiveTab != null) mActiveTab.removeObserver(mTabObserver);
 
                     mActiveTab = tab;
@@ -101,9 +102,9 @@
     private void updateSuspensionState() {
         assert mActiveTab != null;
         if (mActiveTab.isUserInteractable()) {
-            mManager.resumeType(ModalDialogManager.TAB_MODAL);
+            mManager.resumeType(ModalDialogType.TAB);
         } else {
-            mManager.suspendType(ModalDialogManager.TAB_MODAL);
+            mManager.suspendType(ModalDialogType.TAB);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 6913819..de599ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -1776,8 +1776,9 @@
     @Override
     @UrlBar.ScrollType
     public int getScrollType() {
-        return mToolbarDataProvider.shouldDisplaySearchTerms() ? UrlBar.SCROLL_TO_BEGINNING
-                                                               : UrlBar.SCROLL_TO_TLD;
+        return mToolbarDataProvider.shouldDisplaySearchTerms()
+                ? UrlBar.ScrollType.SCROLL_TO_BEGINNING
+                : UrlBar.ScrollType.SCROLL_TO_TLD;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index 135bf38f..288a4e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -156,7 +156,7 @@
     public void finishUrlFocusChange(boolean hasFocus) {
         if (!hasFocus) {
             // Scroll to ensure the TLD is visible, if necessary.
-            if (getScrollType() == UrlBar.SCROLL_TO_TLD) mUrlBar.scrollDisplayText();
+            if (getScrollType() == UrlBar.ScrollType.SCROLL_TO_TLD) mUrlBar.scrollDisplayText();
 
             // The animation rendering may not yet be 100% complete and hiding the keyboard makes
             // the animation quite choppy.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index dd988aa..37fb79e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -43,6 +43,8 @@
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.ui.UiUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.MalformedURLException;
 import java.net.URL;
 
@@ -137,12 +139,13 @@
     private int mOriginEndIndex;
 
     /** What scrolling action should be taken after the URL bar text changes. **/
-    @IntDef({NO_SCROLL, SCROLL_TO_TLD, SCROLL_TO_BEGINNING})
-    public @interface ScrollType {}
-
-    public static final int NO_SCROLL = 0;
-    public static final int SCROLL_TO_TLD = 1;
-    public static final int SCROLL_TO_BEGINNING = 2;
+    @IntDef({ScrollType.NO_SCROLL, ScrollType.SCROLL_TO_TLD, ScrollType.SCROLL_TO_BEGINNING})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScrollType {
+        int NO_SCROLL = 0;
+        int SCROLL_TO_TLD = 1;
+        int SCROLL_TO_BEGINNING = 2;
+    }
 
     /**
      * Implement this to get updates when the direction of the text in the URL bar changes.
@@ -746,7 +749,7 @@
         @ScrollType
         int scrollType = mUrlBarDelegate.getScrollType();
         if (isLayoutRequested()) {
-            mPendingScroll = scrollType != NO_SCROLL;
+            mPendingScroll = scrollType != ScrollType.NO_SCROLL;
             return;
         }
         scrollDisplayTextInternal(scrollType);
@@ -763,10 +766,10 @@
     private void scrollDisplayTextInternal(@ScrollType int scrollType) {
         mPendingScroll = false;
         switch (scrollType) {
-            case SCROLL_TO_TLD:
+            case ScrollType.SCROLL_TO_TLD:
                 scrollToTLD();
                 break;
-            case SCROLL_TO_BEGINNING:
+            case ScrollType.SCROLL_TO_BEGINNING:
                 scrollToBeginning();
                 break;
             default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
index 8481f12..0fa3188 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManager.java
@@ -159,7 +159,7 @@
             CellInfo cellInfo = cellInfos.get(i);
             VisibleCell visibleCell =
                     getVisibleCellPostJellyBeanMr1(cellInfo, elapsedTime, currentTime);
-            if (visibleCell.radioType() != VisibleCell.UNKNOWN_RADIO_TYPE) {
+            if (visibleCell.radioType() != VisibleCell.RadioType.UNKNOWN) {
                 visibleCells.add(visibleCell);
             }
         }
@@ -195,7 +195,7 @@
         long cellTimestamp = currentTime - cellInfoAge;
         if (cellInfo instanceof CellInfoCdma) {
             CellIdentityCdma cellIdentityCdma = ((CellInfoCdma) cellInfo).getCellIdentity();
-            return VisibleCell.builder(VisibleCell.CDMA_RADIO_TYPE)
+            return VisibleCell.builder(VisibleCell.RadioType.CDMA)
                     .setCellId(cellIdentityCdma.getBasestationId())
                     .setLocationAreaCode(cellIdentityCdma.getNetworkId())
                     .setMobileNetworkCode(cellIdentityCdma.getSystemId())
@@ -204,7 +204,7 @@
         }
         if (cellInfo instanceof CellInfoGsm) {
             CellIdentityGsm cellIdentityGsm = ((CellInfoGsm) cellInfo).getCellIdentity();
-            return VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
+            return VisibleCell.builder(VisibleCell.RadioType.GSM)
                     .setCellId(cellIdentityGsm.getCid())
                     .setLocationAreaCode(cellIdentityGsm.getLac())
                     .setMobileCountryCode(cellIdentityGsm.getMcc())
@@ -214,7 +214,7 @@
         }
         if (cellInfo instanceof CellInfoLte) {
             CellIdentityLte cellIdLte = ((CellInfoLte) cellInfo).getCellIdentity();
-            return VisibleCell.builder(VisibleCell.LTE_RADIO_TYPE)
+            return VisibleCell.builder(VisibleCell.RadioType.LTE)
                     .setCellId(cellIdLte.getCi())
                     .setMobileCountryCode(cellIdLte.getMcc())
                     .setMobileNetworkCode(cellIdLte.getMnc())
@@ -227,7 +227,7 @@
                 && cellInfo instanceof CellInfoWcdma) {
             // CellInfoWcdma is only usable JB MR2 upwards.
             CellIdentityWcdma cellIdentityWcdma = ((CellInfoWcdma) cellInfo).getCellIdentity();
-            return VisibleCell.builder(VisibleCell.WCDMA_RADIO_TYPE)
+            return VisibleCell.builder(VisibleCell.RadioType.WCDMA)
                     .setCellId(cellIdentityWcdma.getCid())
                     .setLocationAreaCode(cellIdentityWcdma.getLac())
                     .setMobileCountryCode(cellIdentityWcdma.getMcc())
@@ -297,9 +297,9 @@
         }
         connectedCell = getConnectedCell(context, telephonyManager);
         if (connectedCell != null
-                && (connectedCell.radioType() == VisibleCell.UNKNOWN_RADIO_TYPE
+                && (connectedCell.radioType() == VisibleCell.RadioType.UNKNOWN
                            || connectedCell.radioType()
-                                   == VisibleCell.UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE)) {
+                                   == VisibleCell.RadioType.UNKNOWN_MISSING_LOCATION_PERMISSION)) {
             // If the radio type is unknown, do not use it.
             connectedCell = null;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworks.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworks.java
index 75b1e9c..58679ea1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworks.java
@@ -228,24 +228,26 @@
      */
     static class VisibleCell {
         static final VisibleCell UNKNOWN_VISIBLE_CELL =
-                VisibleCell.builder(VisibleCell.UNKNOWN_RADIO_TYPE).build();
+                VisibleCell.builder(VisibleCell.RadioType.UNKNOWN).build();
         static final VisibleCell UNKNOWN_MISSING_LOCATION_PERMISSION_VISIBLE_CELL =
-                VisibleCell.builder(VisibleCell.UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE)
+                VisibleCell.builder(VisibleCell.RadioType.UNKNOWN_MISSING_LOCATION_PERMISSION)
                         .build();
 
         /**
          * Represents all possible values of radio type that we track.
          */
+        @IntDef({RadioType.UNKNOWN, RadioType.UNKNOWN_MISSING_LOCATION_PERMISSION, RadioType.CDMA,
+                RadioType.GSM, RadioType.LTE, RadioType.WCDMA})
         @Retention(RetentionPolicy.SOURCE)
-        @IntDef({UNKNOWN_RADIO_TYPE, UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE,
-                CDMA_RADIO_TYPE, GSM_RADIO_TYPE, LTE_RADIO_TYPE, WCDMA_RADIO_TYPE})
-        @interface RadioType {}
-        static final int UNKNOWN_RADIO_TYPE = 0;
-        static final int UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE = 1;
-        static final int CDMA_RADIO_TYPE = 2;
-        static final int GSM_RADIO_TYPE = 3;
-        static final int LTE_RADIO_TYPE = 4;
-        static final int WCDMA_RADIO_TYPE = 5;
+        @interface RadioType {
+            int UNKNOWN = 0;
+            int UNKNOWN_MISSING_LOCATION_PERMISSION = 1;
+            int CDMA = 2;
+            int GSM = 3;
+            int LTE = 4;
+            int WCDMA = 5;
+            int NUM_ENTRIES = 6;
+        }
 
         static Builder builder(@RadioType int radioType) {
             return new VisibleCell.Builder().setRadioType(radioType);
@@ -395,20 +397,20 @@
                     PartnerLocationDescriptor.VisibleNetwork.Cell.newBuilder();
 
             switch (radioType()) {
-                case VisibleCell.CDMA_RADIO_TYPE:
+                case VisibleCell.RadioType.CDMA:
                     cellBuilder.setType(PartnerLocationDescriptor.VisibleNetwork.Cell.Type.CDMA);
                     break;
-                case VisibleCell.GSM_RADIO_TYPE:
+                case VisibleCell.RadioType.GSM:
                     cellBuilder.setType(PartnerLocationDescriptor.VisibleNetwork.Cell.Type.GSM);
                     break;
-                case VisibleCell.LTE_RADIO_TYPE:
+                case VisibleCell.RadioType.LTE:
                     cellBuilder.setType(PartnerLocationDescriptor.VisibleNetwork.Cell.Type.LTE);
                     break;
-                case VisibleCell.WCDMA_RADIO_TYPE:
+                case VisibleCell.RadioType.WCDMA:
                     cellBuilder.setType(PartnerLocationDescriptor.VisibleNetwork.Cell.Type.WCDMA);
                     break;
-                case VisibleCell.UNKNOWN_RADIO_TYPE:
-                case VisibleCell.UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE:
+                case VisibleCell.RadioType.UNKNOWN:
+                case VisibleCell.RadioType.UNKNOWN_MISSING_LOCATION_PERMISSION:
                 default:
                     cellBuilder.setType(PartnerLocationDescriptor.VisibleNetwork.Cell.Type.UNKNOWN);
                     break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
index 64eebe53..cd406cd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
@@ -194,7 +194,7 @@
         params.customView = scrollView;
         params.cancelOnTouchOutside = true;
         mDialog = new ModalDialogView(this, params);
-        mModalDialogManager.showDialog(mDialog, ModalDialogManager.APP_MODAL, true);
+        mModalDialogManager.showDialog(mDialog, ModalDialogManager.ModalDialogType.APP, true);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
index 1367cf0..087a437 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
@@ -108,7 +108,7 @@
         if (mIsSheet) {
             mSheetDialog.show();
         } else {
-            mManager.showDialog(mModalDialog, ModalDialogManager.APP_MODAL);
+            mManager.showDialog(mModalDialog, ModalDialogManager.ModalDialogType.APP);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
index 1a35f84..b9aabbd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
@@ -30,7 +30,8 @@
         PasswordGenerationDialogMediator.initializeState(
                 mModel, generatedPassword, saveExplanationText, onPasswordAcceptedOrRejected);
         PasswordGenerationDialogViewBinder.bind(mModel, mViewHolder);
-        mModalDialogManager.showDialog(mViewHolder.getView(), ModalDialogManager.APP_MODAL);
+        mModalDialogManager.showDialog(
+                mViewHolder.getView(), ModalDialogManager.ModalDialogType.APP);
     }
 
     public void dismissDialog() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
index ac22476..315ec7fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
@@ -262,7 +262,8 @@
         if (useAppModalDialogView()) {
             mModalDialogManager = mDialogDelegate.getTab().getActivity().getModalDialogManager();
             mAppModalDialogView = PermissionAppModalDialogView.create(this, mDialogDelegate);
-            mModalDialogManager.showDialog(mAppModalDialogView, ModalDialogManager.APP_MODAL);
+            mModalDialogManager.showDialog(
+                    mAppModalDialogView, ModalDialogManager.ModalDialogType.APP);
         } else {
             mDialogView = new PermissionDialogView(mDialogDelegate);
             mDialogView.createView(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpMoreOptionsFragment.java
similarity index 69%
rename from chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java
rename to chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpMoreOptionsFragment.java
index 616a24d..24b6465f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpMoreOptionsFragment.java
@@ -14,18 +14,18 @@
 import org.chromium.chrome.browser.widget.RadioButtonWithDescription;
 
 /**
- * This fragment implements consent bump screen. This screen lets users to enable or customize
- * personalized and non-personalized services.
+ * This fragment implements the advanced consent bump screen. This screen lets users to enable or
+ * customize personalized and non-personalized services.
  */
-public class ConsentBumpFragment extends Fragment {
-    private static final String TAG = "ConsentBumpFragment";
+public class ConsentBumpMoreOptionsFragment extends Fragment {
+    private static final String TAG = "ConsentBumpMoreOptionsFragment";
 
-    public ConsentBumpFragment() {}
+    public ConsentBumpMoreOptionsFragment() {}
 
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.consent_bump_view, container, false);
+        View view = inflater.inflate(R.layout.consent_bump_more_options_view, container, false);
 
         RadioButtonWithDescription noChanges = view.findViewById(R.id.consent_bump_no_changes);
         noChanges.setDescriptionText(getText(R.string.consent_bump_no_changes_description));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
index a31778f..1475c90 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 
@@ -21,6 +22,7 @@
 public class SigninFragment extends SigninFragmentBase {
     private static final String TAG = "SigninFragment";
 
+    private static final String ARGUMENT_ACCESS_POINT = "SigninFragment.AccessPoint";
     private static final String ARGUMENT_PERSONALIZED_PROMO_ACTION =
             "SigninFragment.PersonalizedPromoAction";
 
@@ -34,6 +36,7 @@
         int NEW_ACCOUNT = 3;
     }
 
+    private @SigninAccessPoint int mSigninAccessPoint;
     private @PromoAction int mPromoAction;
 
     /**
@@ -41,7 +44,9 @@
      * @param accessPoint The access point for starting sign-in flow.
      */
     public static Bundle createArguments(@SigninAccessPoint int accessPoint) {
-        return SigninFragmentBase.createArguments(accessPoint, null);
+        Bundle result = SigninFragmentBase.createArguments(null);
+        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
+        return result;
     }
 
     /**
@@ -51,7 +56,8 @@
      */
     public static Bundle createArgumentsForPromoDefaultFlow(
             @SigninAccessPoint int accessPoint, String accountName) {
-        Bundle result = SigninFragmentBase.createArguments(accessPoint, accountName);
+        Bundle result = SigninFragmentBase.createArguments(accountName);
+        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_PERSONALIZED_PROMO_ACTION, PromoAction.WITH_DEFAULT);
         return result;
     }
@@ -64,8 +70,8 @@
      */
     public static Bundle createArgumentsForPromoChooseAccountFlow(
             @SigninAccessPoint int accessPoint, String accountName) {
-        Bundle result =
-                SigninFragmentBase.createArgumentsForChooseAccountFlow(accessPoint, accountName);
+        Bundle result = SigninFragmentBase.createArgumentsForChooseAccountFlow(accountName);
+        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_PERSONALIZED_PROMO_ACTION, PromoAction.NOT_DEFAULT);
         return result;
     }
@@ -76,7 +82,8 @@
      * @param accessPoint The access point for starting sign-in flow.
      */
     public static Bundle createArgumentsForPromoAddAccountFlow(@SigninAccessPoint int accessPoint) {
-        Bundle result = SigninFragmentBase.createArgumentsForAddAccountFlow(accessPoint);
+        Bundle result = SigninFragmentBase.createArgumentsForAddAccountFlow();
+        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_PERSONALIZED_PROMO_ACTION, PromoAction.NEW_ACCOUNT);
         return result;
     }
@@ -88,10 +95,20 @@
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        int accessPoint = getSigninArguments().getInt(ARGUMENT_ACCESS_POINT, -1);
+        assert accessPoint == SigninAccessPoint.AUTOFILL_DROPDOWN
+                || accessPoint == SigninAccessPoint.BOOKMARK_MANAGER
+                || accessPoint == SigninAccessPoint.NTP_CONTENT_SUGGESTIONS
+                || accessPoint == SigninAccessPoint.RECENT_TABS
+                || accessPoint == SigninAccessPoint.SETTINGS
+                || accessPoint == SigninAccessPoint.SIGNIN_PROMO
+                || accessPoint
+                        == SigninAccessPoint.START_PAGE : "invalid access point: " + accessPoint;
+        mSigninAccessPoint = accessPoint;
         mPromoAction =
                 getSigninArguments().getInt(ARGUMENT_PERSONALIZED_PROMO_ACTION, PromoAction.NONE);
 
-        SigninManager.logSigninStartAccessPoint(getSigninAccessPoint());
+        SigninManager.logSigninStartAccessPoint(mSigninAccessPoint);
         recordSigninStartedHistogramAccountInfo();
         recordSigninStartedUserAction();
     }
@@ -135,6 +152,12 @@
         });
     }
 
+    @Override
+    protected int getNegativeButtonTextId() {
+        return mSigninAccessPoint == SigninAccessPoint.SIGNIN_PROMO ? R.string.no_thanks
+                                                                    : R.string.cancel;
+    }
+
     private void recordSigninCompletedHistogramAccountInfo() {
         final String histogram;
         switch (mPromoAction) {
@@ -155,7 +178,7 @@
         }
 
         RecordHistogram.recordEnumeratedHistogram(
-                histogram, getSigninAccessPoint(), SigninAccessPoint.MAX);
+                histogram, mSigninAccessPoint, SigninAccessPoint.MAX);
     }
 
     private void recordSigninStartedHistogramAccountInfo() {
@@ -178,11 +201,11 @@
         }
 
         RecordHistogram.recordEnumeratedHistogram(
-                histogram, getSigninAccessPoint(), SigninAccessPoint.MAX);
+                histogram, mSigninAccessPoint, SigninAccessPoint.MAX);
     }
 
     private void recordSigninStartedUserAction() {
-        switch (getSigninAccessPoint()) {
+        switch (mSigninAccessPoint) {
             case SigninAccessPoint.AUTOFILL_DROPDOWN:
                 RecordUserAction.record("Signin_Signin_FromAutofillDropdown");
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
index 24ca5ab..b9176fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
@@ -63,7 +63,6 @@
     private static final String SETTINGS_LINK_OPEN = "<LINK1>";
     private static final String SETTINGS_LINK_CLOSE = "</LINK1>";
 
-    private static final String ARGUMENT_ACCESS_POINT = "SigninFragmentBase.AccessPoint";
     private static final String ARGUMENT_ACCOUNT_NAME = "SigninFragmentBase.AccountName";
     private static final String ARGUMENT_CHILD_ACCOUNT_STATUS =
             "SigninFragmentBase.ChildAccountStatus";
@@ -84,13 +83,11 @@
         int ADD_ACCOUNT = 3;
     }
 
-    private @SigninAccessPoint int mSigninAccessPoint;
     private @SigninFlowType int mSigninFlowType;
     private @ChildAccountStatus.Status int mChildAccountStatus;
 
     private SigninView mView;
     private ConsentTextTracker mConsentTextTracker;
-    private @StringRes int mCancelButtonTextId = R.string.cancel;
 
     private boolean mAccountSelectionPending;
     private @Nullable String mRequestedAccountName;
@@ -114,13 +111,10 @@
     /**
      * Creates an argument bundle for the default SigninFragmentBase flow (account selection is
      * enabled, etc.).
-     * @param accessPoint The access point for starting sign-in flow.
      * @param accountName The account to preselect or null to preselect the default account.
      */
-    protected static Bundle createArguments(
-            @SigninAccessPoint int accessPoint, @Nullable String accountName) {
+    protected static Bundle createArguments(@Nullable String accountName) {
         Bundle result = new Bundle();
-        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_SIGNIN_FLOW_TYPE, SigninFlowType.DEFAULT);
         result.putString(ARGUMENT_ACCOUNT_NAME, accountName);
         return result;
@@ -129,13 +123,10 @@
     /**
      * Creates an argument bundle for "Choose account" sign-in flow. Account selection dialog will
      * be shown at the start of the sign-in process.
-     * @param accessPoint The access point for starting sign-in flow.
      * @param accountName The account to preselect or null to preselect the default account.
      */
-    protected static Bundle createArgumentsForChooseAccountFlow(
-            @SigninAccessPoint int accessPoint, @Nullable String accountName) {
+    protected static Bundle createArgumentsForChooseAccountFlow(@Nullable String accountName) {
         Bundle result = new Bundle();
-        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_SIGNIN_FLOW_TYPE, SigninFlowType.CHOOSE_ACCOUNT);
         result.putString(ARGUMENT_ACCOUNT_NAME, accountName);
         return result;
@@ -144,25 +135,21 @@
     /**
      * Creates an argument bundle for "Add account" sign-in flow. Activity to add an account will be
      * shown at the start of the sign-in process.
-     * @param accessPoint The access point for starting sign-in flow.
      */
-    protected static Bundle createArgumentsForAddAccountFlow(@SigninAccessPoint int accessPoint) {
+    protected static Bundle createArgumentsForAddAccountFlow() {
         Bundle result = new Bundle();
-        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_SIGNIN_FLOW_TYPE, SigninFlowType.ADD_ACCOUNT);
         return result;
     }
 
     /**
      * Creates an argument bundle for a custom SigninFragmentBase flow.
-     * @param accessPoint The access point for starting sign-in flow.
      * @param accountName The account to preselect.
      * @param childAccountStatus Whether the selected account is a child one.
      */
-    protected static Bundle createArgumentsForForcedSigninFlow(@SigninAccessPoint int accessPoint,
+    protected static Bundle createArgumentsForForcedSigninFlow(
             String accountName, @ChildAccountStatus.Status int childAccountStatus) {
         Bundle result = new Bundle();
-        result.putInt(ARGUMENT_ACCESS_POINT, accessPoint);
         result.putInt(ARGUMENT_SIGNIN_FLOW_TYPE, SigninFlowType.FORCED);
         result.putString(ARGUMENT_ACCOUNT_NAME, accountName);
         result.putInt(ARGUMENT_CHILD_ACCOUNT_STATUS, childAccountStatus);
@@ -193,10 +180,12 @@
     protected abstract void onSigninAccepted(String accountName, boolean isDefaultAccount,
             boolean settingsClicked, Runnable callback);
 
-    /** Returns the access point that initiated the sign-in flow. */
-    protected @SigninAccessPoint int getSigninAccessPoint() {
-        return mSigninAccessPoint;
-    }
+    /**
+     * Returns the string resource id for the negative button. This is invoked once from
+     * {@link #onCreateView}.
+     */
+    @StringRes
+    protected abstract int getNegativeButtonTextId();
 
     /** Returns whether this fragment is in "force sign-in" mode. */
     protected boolean isForcedSignin() {
@@ -208,7 +197,6 @@
         super.onCreate(savedInstanceState);
 
         Bundle arguments = getSigninArguments();
-        initAccessPoint(arguments.getInt(ARGUMENT_ACCESS_POINT, -1));
         mRequestedAccountName = arguments.getString(ARGUMENT_ACCOUNT_NAME, null);
         mChildAccountStatus =
                 arguments.getInt(ARGUMENT_CHILD_ACCOUNT_STATUS, ChildAccountStatus.NOT_CHILD);
@@ -243,23 +231,6 @@
                 getResources().getDimensionPixelSize(R.dimen.user_picture_size), badgeConfig);
     }
 
-    private void initAccessPoint(@SigninAccessPoint int accessPoint) {
-        assert accessPoint == SigninAccessPoint.AUTOFILL_DROPDOWN
-                || accessPoint == SigninAccessPoint.BOOKMARK_MANAGER
-                || accessPoint == SigninAccessPoint.NTP_CONTENT_SUGGESTIONS
-                || accessPoint == SigninAccessPoint.RECENT_TABS
-                || accessPoint == SigninAccessPoint.SETTINGS
-                || accessPoint == SigninAccessPoint.SIGNIN_PROMO
-                || accessPoint
-                        == SigninAccessPoint.START_PAGE : "invalid access point: " + accessPoint;
-
-        mSigninAccessPoint = accessPoint;
-        if (accessPoint == SigninAccessPoint.START_PAGE
-                || accessPoint == SigninAccessPoint.SIGNIN_PROMO) {
-            mCancelButtonTextId = R.string.no_thanks;
-        }
-    }
-
     @Override
     public void onDestroy() {
         super.onDestroy();
@@ -351,7 +322,7 @@
 
         mConsentTextTracker.setText(mView.getGoogleServicesDescriptionView(),
                 R.string.signin_google_services_description);
-        mConsentTextTracker.setText(mView.getRefuseButton(), mCancelButtonTextId);
+        mConsentTextTracker.setText(mView.getRefuseButton(), getNegativeButtonTextId());
         mConsentTextTracker.setText(mView.getMoreButton(), R.string.more);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
index 7b6a7850..6018825c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -315,7 +315,7 @@
         mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
         mThumbnailView.setBackgroundColor(iconBackgroundColor);
         mThumbnailView.setImageResource(
-                DownloadUtils.getIconResId(fileType, DownloadUtils.ICON_SIZE_36_DP));
+                DownloadUtils.getIconResId(fileType, DownloadUtils.IconSize.DP_36));
         if (!mIsContextual) ((TintedImageView) mThumbnailView).setTint(iconForegroundColorList);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java
index 495d606..c0c17ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java
@@ -24,6 +24,8 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -117,6 +119,7 @@
      */
     @VisibleForTesting
     @IntDef({TileTask.FETCH_DATA, TileTask.SCHEDULE_ICON_FETCH, TileTask.FETCH_ICON})
+    @Retention(RetentionPolicy.SOURCE)
     @interface TileTask {
         /**
          * An event that should result in new data being loaded happened.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index c97eb4e..4534f87 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -740,7 +740,7 @@
     @Override
     @UrlBar.ScrollType
     public int getScrollType() {
-        return UrlBar.SCROLL_TO_TLD;
+        return UrlBar.ScrollType.SCROLL_TO_TLD;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
index 4637d08..ad8bbf1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
@@ -55,7 +55,7 @@
     @Override
     public void show() {
         mModalDialogView = createView();
-        mModalDialogManager.showDialog(mModalDialogView, ModalDialogManager.APP_MODAL);
+        mModalDialogManager.showDialog(mModalDialogView, ModalDialogManager.ModalDialogType.APP);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellImpl.java
index 3ac2c5c..57850bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellImpl.java
@@ -320,7 +320,7 @@
         mNonVrModalDialogManager.cancelAllDialogs();
         mVrModalPresenter = new VrModalPresenter(this);
         mVrModalDialogManager =
-                new ModalDialogManager(mVrModalPresenter, ModalDialogManager.APP_MODAL);
+                new ModalDialogManager(mVrModalPresenter, ModalDialogManager.ModalDialogType.APP);
         mActivity.setModalDialogManager(mVrModalDialogManager);
 
         ViewGroup decor = (ViewGroup) mActivity.getWindow().getDecorView();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
index bf8f6ad1..d2b60ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
@@ -838,6 +838,6 @@
             protected Calendar doInBackground(Void... unused) {
                 return Calendar.getInstance();
             }
-        }.execute();
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a2b76fc8..6718ca2 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -444,6 +444,11 @@
   "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorFactory.java",
   "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java",
   "java/src/org/chromium/chrome/browser/download/home/OfflineItemSource.java",
+  "java/src/org/chromium/chrome/browser/download/home/PrefetchStatusProvider.java",
+  "java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java",
+  "java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java",
+  "java/src/org/chromium/chrome/browser/download/home/empty/EmptyView.java",
+  "java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java",
   "java/src/org/chromium/chrome/browser/download/home/filter/DeleteUndoOfflineItemFilter.java",
   "java/src/org/chromium/chrome/browser/download/home/filter/Filters.java",
   "java/src/org/chromium/chrome/browser/download/home/filter/FilterChipsProvider.java",
@@ -474,7 +479,6 @@
   "java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java",
   "java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListViewAdapter.java",
   "java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java",
-  "java/src/org/chromium/chrome/browser/download/home/list/EmptyViewCoordinator.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ItemUtils.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListItem.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java",
@@ -731,6 +735,7 @@
   "java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java",
   "java/src/org/chromium/chrome/browser/media/router/DiscoveryCallback.java",
   "java/src/org/chromium/chrome/browser/media/router/DiscoveryDelegate.java",
+  "java/src/org/chromium/chrome/browser/media/router/FlingingController.java",
   "java/src/org/chromium/chrome/browser/media/router/FlingingControllerBridge.java",
   "java/src/org/chromium/chrome/browser/media/router/MediaController.java",
   "java/src/org/chromium/chrome/browser/media/router/MediaRoute.java",
@@ -1247,7 +1252,7 @@
   "java/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialog.java",
   "java/src/org/chromium/chrome/browser/signin/ConfirmSyncDataStateMachine.java",
   "java/src/org/chromium/chrome/browser/signin/ConfirmSyncDataStateMachineDelegate.java",
-  "java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java",
+  "java/src/org/chromium/chrome/browser/signin/ConsentBumpMoreOptionsFragment.java",
   "java/src/org/chromium/chrome/browser/signin/ConsentTextTracker.java",
   "java/src/org/chromium/chrome/browser/signin/DisplayableProfileData.java",
   "java/src/org/chromium/chrome/browser/signin/GoogleActivityController.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/router/MockMediaRouteProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/router/MockMediaRouteProvider.java
index 5ad98f68..3d3a4c51d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/router/MockMediaRouteProvider.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/router/MockMediaRouteProvider.java
@@ -11,6 +11,8 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.annotation.Nullable;
+
 /**
  * Mocked {@link MediaRouteProvider}.
  */
@@ -177,7 +179,8 @@
     }
 
     @Override
-    public MediaController getMediaController(String routeId) {
+    @Nullable
+    public FlingingController getFlingingController(String routeId) {
         return null;
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java
index 2e96e9c2..666eea6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -80,7 +81,7 @@
         mActivity.getToolbarManager().getToolbarLayout().getLocationBar().addUrlFocusChangeListener(
                 mTestObserver);
         TabModalPresenter presenter =
-                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogManager.TAB_MODAL);
+                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
         presenter.disableAnimationForTest();
     }
 
@@ -88,25 +89,25 @@
     @SmallTest
     public void testOneDialog() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Show a dialog. The pending list should be empty, and the dialog should be showing.
         // Browser controls should be restricted.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the dialog by clicking positive button.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(not(hasDescendant(withText("0")))));
         checkBrowserControls(false);
@@ -117,8 +118,8 @@
     @SmallTest
     public void testTwoDialogs() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
@@ -126,39 +127,39 @@
         // The pending list should be empty, and the dialog should be showing.
         // The tab modal container shouldn't be in the window hierarchy when an app modal dialog is
         // showing.
-        showDialog(0, ModalDialogManager.APP_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.APP);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("0")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Show the second dialog. It should be added to the pending list, and the first dialog
         // should still be shown.
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(1, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withText("0")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the first dialog by clicking cancel. The second dialog should be removed from
         // pending list and shown immediately after.
         onView(withText(R.string.cancel)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("0")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(
                         allOf(not(hasDescendant(withText("0"))), hasDescendant(withText("1")))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the second dialog by clicking ok. Browser controls should no longer be
         // restricted.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("0")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(
@@ -171,76 +172,76 @@
     @SmallTest
     public void testThreeDialogs() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Show the first dialog.
         // The pending list should be empty, and the dialog should be showing.
         // Browser controls should be restricted.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Show the second dialog. It should be added to the pending list, and the first dialog
         // should still be shown.
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(1, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(
                         allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Show the third dialog (app modal). The first tab modal dialog should be back to the
         // pending list and this app modal dialog should be shown right after.
-        showDialog(2, ModalDialogManager.APP_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        showDialog(2, ModalDialogType.APP);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Simulate dismissing the dialog by non-user action. The second dialog should be removed
         // from pending list without showing.
         dismissDialog(1);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the second dialog twice and verify nothing breaks.
         dismissDialog(1);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the third dialog. The first dialog should be removed from pending list and
         // shown immediately after. The tab modal container shouldn't be in the window hierarchy
         // when an app modal dialog is showing.
         dismissDialog(2);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("2")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("0")),
                         not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the first dialog by clicking OK. Browser controls should no longer be restricted.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("2")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(not(hasDescendant(withText("0"))),
@@ -253,106 +254,106 @@
     @SmallTest
     public void testShow_ShowNext() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Insert two tab modal dialogs and two app modal dialogs for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        showDialog(2, ModalDialogManager.APP_MODAL);
-        showDialog(3, ModalDialogManager.APP_MODAL);
+        showDialog(0, ModalDialogType.TAB);
+        showDialog(1, ModalDialogType.TAB);
+        showDialog(2, ModalDialogType.APP);
+        showDialog(3, ModalDialogType.APP);
 
         // The first inserted app modal dialog is shown.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 1);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        checkPendingSize(ModalDialogType.APP, 1);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the dialog by clicking OK. The second inserted app modal dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withText("3")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the dialog by clicking OK. The first tab modal dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("0")),
                         not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))),
                         not(hasDescendant(withText("3"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the dialog by clicking OK. The second tab modal dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("1")),
                         not(hasDescendant(withText("0"))), not(hasDescendant(withText("2"))),
                         not(hasDescendant(withText("3"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
     }
 
     @Test
     @SmallTest
     public void testShow_ShowDialogAsNext() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Insert a tab modal dialog and a app modal dialog for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        showDialog(2, ModalDialogManager.APP_MODAL);
+        showDialog(0, ModalDialogType.TAB);
+        showDialog(1, ModalDialogType.TAB);
+        showDialog(2, ModalDialogType.APP);
 
         // The first inserted app modal dialog is shown.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Show a tab modal dialog as the next dialog to be shown. Verify that the app modal dialog
         // is still showing.
-        showDialogAsNext(3, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 3);
+        showDialogAsNext(3, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 3);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Dismiss the dialog by clicking OK. The third tab modal dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("3")),
                         not(hasDescendant(withText("0"))), not(hasDescendant(withText("1"))),
                         not(hasDescendant(withText("2"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the dialog by clicking OK. The first tab modal dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("0")),
                         not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))),
                         not(hasDescendant(withText("3"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
     }
 
     @Test
@@ -360,10 +361,10 @@
     @DisabledTest(message = "crbug.com/812066")
     public void testShow_UrlBarFocused() throws Exception {
         // Show a dialog. The dialog should be shown on top of the toolbar.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
+        showDialog(0, ModalDialogType.TAB);
 
         TabModalPresenter presenter =
-                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogManager.TAB_MODAL);
+                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
         final View dialogContainer = presenter.getDialogContainerForTest();
         final View controlContainer = mActivity.findViewById(R.id.control_container);
         final ViewGroup containerParent = presenter.getContainerParentForTest();
@@ -401,26 +402,26 @@
     @DisabledTest(message = "crbug.com/804858")
     public void testSuspend_ToggleOverview() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add two dialogs available for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(0, ModalDialogType.TAB);
+        showDialog(1, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(
                         allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         //  Tab modal dialogs should be suspended on entering tab switcher.
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(
                         not(hasDescendant(withText("0"))), not(hasDescendant(withText("1"))))));
@@ -429,23 +430,23 @@
 
         // Exit overview mode. The first dialog should be showing again.
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(
                         allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dismiss the first dialog. The second dialog should be shown.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(
                         allOf(not(hasDescendant(withText("0"))), hasDescendant(withText("1")))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
     }
 
     @Test
@@ -454,46 +455,46 @@
     @DisabledTest(message = "crbug.com/804858")
     public void testSuspend_ShowNext() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Tab modal dialogs should be suspended on entering tab switcher. App modal dialog should
         // be showing.
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
 
         // An app modal dialog can be shown in tab switcher.
-        showDialog(1, ModalDialogManager.APP_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(1, ModalDialogType.APP);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
         onView(withText("1")).check(matches(isDisplayed()));
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Close the app modal dialog and exit overview mode. The first dialog should be showing
         // again.
         onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
     }
 
     @Test
@@ -506,31 +507,31 @@
         }
 
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Tab modal dialogs should be suspended on entering tab switcher.
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Close the only tab in the tab switcher.
         ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
     }
@@ -542,47 +543,47 @@
         mActivityTestRule.loadUrlInNewTab("about:blank");
 
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Tab modal dialogs should be suspended on entering tab switcher.
         onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Close current tab in the tab switcher.
         ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Show a new tab modal dialog, and it should be suspended in tab switcher.
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(1, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Show an app modal dialog. The app modal dialog should be shown.
-        showDialog(2, ModalDialogManager.APP_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        showDialog(2, ModalDialogType.APP);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withText("2")).check(matches(isDisplayed()));
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
     }
 
     @Test
@@ -593,84 +594,84 @@
         ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
 
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(0, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("0"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Dialog should be dismissed after switching to a different tab.
         ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Open a tab modal dialog in the current tab. The dialog should be shown.
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        showDialog(1, ModalDialogType.TAB);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(hasDescendant(withText("1"))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
     }
 
     @Test
     @SmallTest
     public void testDismiss_BackPressed() throws Exception {
         // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         checkBrowserControls(false);
         checkCurrentPresenter(null);
 
         // Add three dialogs available for showing. The app modal dialog should be shown first.
-        showDialog(0, ModalDialogManager.TAB_MODAL);
-        showDialog(1, ModalDialogManager.TAB_MODAL);
-        showDialog(2, ModalDialogManager.APP_MODAL);
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 2);
+        showDialog(0, ModalDialogType.TAB);
+        showDialog(1, ModalDialogType.TAB);
+        showDialog(2, ModalDialogType.APP);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 2);
         onView(withText("2")).check(matches(isDisplayed()));
         onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogManager.APP_MODAL);
+        checkCurrentPresenter(ModalDialogType.APP);
 
         // Perform back press. The app modal dialog should be dismissed.
         // The first tab modal dialog should be shown. The tab modal container shouldn't be in the
         // window hierarchy when an app modal dialog is showing.
         Espresso.pressBack();
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 1);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 1);
         onView(withText("2")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(hasDescendant(withText("0")),
                         not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Perform a second back press. The first tab modal dialog should be dismissed.
         Espresso.pressBack();
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("2")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(not(hasDescendant(withText("0"))),
                         hasDescendant(withText("1")), not(hasDescendant(withText("2"))))));
         checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogManager.TAB_MODAL);
+        checkCurrentPresenter(ModalDialogType.TAB);
 
         // Perform a third back press. The second tab modal dialog should be dismissed.
         Espresso.pressBack();
-        checkPendingSize(ModalDialogManager.APP_MODAL, 0);
-        checkPendingSize(ModalDialogManager.TAB_MODAL, 0);
+        checkPendingSize(ModalDialogType.APP, 0);
+        checkPendingSize(ModalDialogType.TAB, 0);
         onView(withText("2")).check(doesNotExist());
         onView(withId(R.id.tab_modal_dialog_container))
                 .check(matches(allOf(not(hasDescendant(withText("0"))),
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterTest.java
index 654c45e..c0f2fa9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/OfflineItemFilterTest.java
@@ -105,6 +105,23 @@
     }
 
     @Test
+    public void testItemsAvailable() {
+        when(mSource.areItemsAvailable()).thenReturn(false);
+
+        OfflineItemFilterImpl filter = new OfflineItemFilterImpl(mSource);
+        filter.addObserver(mObserver);
+        verify(mSource, times(1)).addObserver(filter);
+        verify(mSource, times(1)).getItems();
+
+        Assert.assertFalse(filter.areItemsAvailable());
+
+        when(mSource.areItemsAvailable()).thenReturn(true);
+        filter.onItemsAvailable();
+        verify(mObserver, times(1)).onItemsAvailable();
+        Assert.assertTrue(filter.areItemsAvailable());
+    }
+
+    @Test
     public void testFiltering() {
         OfflineItem item1 = new OfflineItem();
         OfflineItem item2 = new OfflineItem();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java
index 053f1631..668fdf3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java
@@ -60,19 +60,6 @@
 
     private FeedImageLoader mImageLoader;
 
-    private class FakeConsumer implements Consumer<Drawable> {
-        public Drawable mResponse = null;
-
-        @Override
-        public void accept(Drawable value) {
-            mResponse = value;
-        }
-
-        public void clearResponse() {
-            mResponse = null;
-        }
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderUnitTest.java
index 8a01223..e0f5624 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderUnitTest.java
@@ -72,15 +72,14 @@
             VisibleWifi.create("ssid1_nomap", "11:11:11:11:11:11", -1, 10L);
     private static final VisibleWifi VISIBLE_WIFI_OPTOUT =
             VisibleWifi.create("ssid1_optout", "11:11:11:11:11:11", -1, 10L);
-    private static final VisibleCell VISIBLE_CELL1 =
-            VisibleCell.builder(VisibleCell.CDMA_RADIO_TYPE)
-                    .setCellId(10)
-                    .setLocationAreaCode(11)
-                    .setMobileCountryCode(12)
-                    .setMobileNetworkCode(13)
-                    .setTimestamp(10L)
-                    .build();
-    private static final VisibleCell VISIBLE_CELL2 = VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
+    private static final VisibleCell VISIBLE_CELL1 = VisibleCell.builder(VisibleCell.RadioType.CDMA)
+                                                             .setCellId(10)
+                                                             .setLocationAreaCode(11)
+                                                             .setMobileCountryCode(12)
+                                                             .setMobileNetworkCode(13)
+                                                             .setTimestamp(10L)
+                                                             .build();
+    private static final VisibleCell VISIBLE_CELL2 = VisibleCell.builder(VisibleCell.RadioType.GSM)
                                                              .setCellId(20)
                                                              .setLocationAreaCode(21)
                                                              .setMobileCountryCode(22)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
index ec69d461..b29dc95 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/PlatformNetworksManagerTest.java
@@ -45,6 +45,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell;
+import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell.RadioType;
 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleWifi;
 
 import java.util.Arrays;
@@ -66,13 +67,13 @@
     private static final long CONNECTED_WIFI_AGE = 1000L;
     private static final long NOT_CONNECTED_WIFI_AGE = 2000L;
 
-    private static final VisibleCell CDMA_CELL = VisibleCell.builder(VisibleCell.CDMA_RADIO_TYPE)
+    private static final VisibleCell CDMA_CELL = VisibleCell.builder(RadioType.CDMA)
                                                          .setCellId(40)
                                                          .setLocationAreaCode(41)
                                                          .setMobileNetworkCode(43)
                                                          .setTimestamp(47L)
                                                          .build();
-    private static final VisibleCell LTE_CELL = VisibleCell.builder(VisibleCell.LTE_RADIO_TYPE)
+    private static final VisibleCell LTE_CELL = VisibleCell.builder(RadioType.LTE)
                                                         .setCellId(50)
                                                         .setMobileCountryCode(52)
                                                         .setMobileNetworkCode(53)
@@ -80,7 +81,7 @@
                                                         .setTrackingAreaCode(56)
                                                         .setTimestamp(57L)
                                                         .build();
-    private static final VisibleCell WCDMA_CELL = VisibleCell.builder(VisibleCell.WCDMA_RADIO_TYPE)
+    private static final VisibleCell WCDMA_CELL = VisibleCell.builder(RadioType.WCDMA)
                                                           .setCellId(60)
                                                           .setLocationAreaCode(61)
                                                           .setMobileCountryCode(62)
@@ -88,7 +89,7 @@
                                                           .setPrimaryScramblingCode(64)
                                                           .setTimestamp(67L)
                                                           .build();
-    private static final VisibleCell GSM_CELL = VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
+    private static final VisibleCell GSM_CELL = VisibleCell.builder(RadioType.GSM)
                                                         .setCellId(70)
                                                         .setLocationAreaCode(71)
                                                         .setMobileCountryCode(72)
@@ -96,9 +97,9 @@
                                                         .setTimestamp(77L)
                                                         .build();
     private static final VisibleCell UNKNOWN_VISIBLE_CELL =
-            VisibleCell.builder(VisibleCell.UNKNOWN_RADIO_TYPE).build();
+            VisibleCell.builder(RadioType.UNKNOWN).build();
     private static final VisibleCell UNKNOWN_MISSING_LOCATION_PERMISSION_VISIBLE_CELL =
-            VisibleCell.builder(VisibleCell.UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE).build();
+            VisibleCell.builder(RadioType.UNKNOWN_MISSING_LOCATION_PERMISSION).build();
     private static final VisibleWifi UNKNOWN_VISIBLE_WIFI =
             VisibleWifi.create(null, null, null, null);
     private static final long LTE_CELL_AGE = 3000L;
@@ -273,22 +274,22 @@
         assertEquals(4, visibleCells.size());
         for (VisibleCell visibleCell : visibleCells) {
             switch (visibleCell.radioType()) {
-                case VisibleCell.LTE_RADIO_TYPE:
+                case RadioType.LTE:
                     assertEquals(LTE_CELL, visibleCell);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - LTE_CELL_AGE),
                             visibleCell.timestampMs());
                     break;
-                case VisibleCell.WCDMA_RADIO_TYPE:
+                case RadioType.WCDMA:
                     assertEquals(visibleCell, WCDMA_CELL);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - WCDMA_CELL_AGE),
                             visibleCell.timestampMs());
                     break;
-                case VisibleCell.GSM_RADIO_TYPE:
+                case RadioType.GSM:
                     assertEquals(visibleCell, GSM_CELL);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - GSM_CELL_AGE),
                             visibleCell.timestampMs());
                     break;
-                case VisibleCell.CDMA_RADIO_TYPE:
+                case RadioType.CDMA:
                     assertEquals(visibleCell, CDMA_CELL);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - CDMA_CELL_AGE),
                             visibleCell.timestampMs());
@@ -309,17 +310,17 @@
         assertEquals(visibleCells.size(), 3);
         for (VisibleCell visibleCell : visibleCells) {
             switch (visibleCell.radioType()) {
-                case VisibleCell.LTE_RADIO_TYPE:
+                case RadioType.LTE:
                     assertEquals(LTE_CELL, visibleCell);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - LTE_CELL_AGE),
                             visibleCell.timestampMs());
                     break;
-                case VisibleCell.GSM_RADIO_TYPE:
+                case RadioType.GSM:
                     assertEquals(visibleCell, GSM_CELL);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - GSM_CELL_AGE),
                             visibleCell.timestampMs());
                     break;
-                case VisibleCell.CDMA_RADIO_TYPE:
+                case RadioType.CDMA:
                     assertEquals(visibleCell, CDMA_CELL);
                     assertEquals(Long.valueOf(CURRENT_TIME_MS - CDMA_CELL_AGE),
                             visibleCell.timestampMs());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java
index 5b96b6d5..4dd3de76 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java
@@ -38,19 +38,14 @@
     private static final String BSSID2 = "11:11:11:11:11:12";
     private static final Integer LEVEL2 = -2;
     private static final Long TIMESTAMP2 = 20L;
-    @RadioType
-    private static final int[] RADIO_TYPES = {VisibleCell.UNKNOWN_RADIO_TYPE,
-            VisibleCell.UNKNOWN_MISSING_LOCATION_PERMISSION_RADIO_TYPE, VisibleCell.CDMA_RADIO_TYPE,
-            VisibleCell.GSM_RADIO_TYPE, VisibleCell.LTE_RADIO_TYPE, VisibleCell.WCDMA_RADIO_TYPE};
 
     private final VisibleWifi mVisibleWifi1 = VisibleWifi.create(SSID1, BSSID1, LEVEL1, TIMESTAMP1);
     private final VisibleWifi mVisibleWifi2 = VisibleWifi.create(SSID2, BSSID2, LEVEL2, TIMESTAMP2);
-    private final VisibleCell.Builder mVisibleCellCommunBuilder =
-            VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
-                    .setCellId(10)
-                    .setLocationAreaCode(11)
-                    .setMobileCountryCode(12)
-                    .setMobileNetworkCode(13);
+    private final VisibleCell.Builder mVisibleCellCommunBuilder = VisibleCell.builder(RadioType.GSM)
+                                                                          .setCellId(10)
+                                                                          .setLocationAreaCode(11)
+                                                                          .setMobileCountryCode(12)
+                                                                          .setMobileNetworkCode(13);
     private final VisibleCell mVisibleCell1 = mVisibleCellCommunBuilder.setPhysicalCellId(14)
                                                       .setPrimaryScramblingCode(15)
                                                       .setTrackingAreaCode(16)
@@ -58,15 +53,14 @@
                                                       .build();
     private final VisibleCell mVisibleCell1DifferentTimestamp =
             mVisibleCellCommunBuilder.setTimestamp(20L).build();
-    private final VisibleCell mVisibleCell2 = VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
+    private final VisibleCell mVisibleCell2 = VisibleCell.builder(RadioType.GSM)
                                                       .setCellId(30)
                                                       .setLocationAreaCode(31)
                                                       .setMobileCountryCode(32)
                                                       .setMobileNetworkCode(33)
                                                       .setTimestamp(30L)
                                                       .build();
-    private final VisibleCell mEmptyCell =
-            VisibleCell.builder(VisibleCell.UNKNOWN_RADIO_TYPE).build();
+    private final VisibleCell mEmptyCell = VisibleCell.builder(RadioType.UNKNOWN).build();
     private final VisibleWifi mEmptyWifi = VisibleWifi.create(null, null, null, null);
 
     private final Set<VisibleCell> mAllVisibleCells =
@@ -163,7 +157,8 @@
 
     @Test
     public void testVisibleCellBuilder() {
-        for (@RadioType int radioType : RADIO_TYPES) {
+        for (@RadioType int radioType = RadioType.UNKNOWN; radioType < RadioType.NUM_ENTRIES;
+                radioType++) {
             VisibleCell visibleCell = VisibleCell.builder(radioType).build();
             assertEquals(radioType, visibleCell.radioType());
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java
index 2270ee63..c5285f0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java
@@ -43,16 +43,15 @@
             VisibleWifi.create("ssid1", "11:11:11:11:11:11", 1, 10L);
     private static final VisibleWifi VISIBLE_WIFI_2 =
             VisibleWifi.create("ssid2", "11:11:11:11:11:12", 2, 20L);
-    private static final VisibleCell VISIBLE_CELL_1 =
-            VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
-                    .setCellId(30)
-                    .setLocationAreaCode(31)
-                    .setMobileCountryCode(32)
-                    .setMobileNetworkCode(33)
-                    .setTimestamp(30L)
-                    .build();
+    private static final VisibleCell VISIBLE_CELL_1 = VisibleCell.builder(VisibleCell.RadioType.GSM)
+                                                              .setCellId(30)
+                                                              .setLocationAreaCode(31)
+                                                              .setMobileCountryCode(32)
+                                                              .setMobileNetworkCode(33)
+                                                              .setTimestamp(30L)
+                                                              .build();
     private static final VisibleCell VISIBLE_CELL_2 =
-            VisibleCell.builder(VisibleCell.CDMA_RADIO_TYPE)
+            VisibleCell.builder(VisibleCell.RadioType.CDMA)
                     .setCellId(40)
                     .setLocationAreaCode(41)
                     .setMobileCountryCode(42)
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index ee3fbdb4..c1a3b76 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3568,13 +3568,10 @@
   <message name="IDS_SETTINGS_PASSWORDS_CHECKBOX_LABEL" desc="Label for the checkbox which enables or disables syncing passwords between multiple browser instances.">
     Passwords
   </message>
+  <!-- TODO(https://crbug.com/854562): Remove this string once Autofill Home is fully launched. -->
   <message name="IDS_SETTINGS_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL" desc="Label for the checkbox that controls the Autofill/Payments integration feature.">
     Credit cards and addresses using Google Pay.
   </message>
-  <!-- TODO(https://crbug.com/854562): Rename this to "IDS_SETTINGS_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL" once Autofill Home is fully launched. -->
-  <message name="IDS_SETTINGS_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL_AUTOFILL_HOME" desc="Label for the checkbox that controls the Autofill/Payments integration feature used for Autofill Home.">
-    Payment methods and addresses using Google Pay.
-  </message>
   <message name="IDS_SETTINGS_OPEN_TABS_CHECKBOX_LABEL" desc="Label for the checkbox which enables or disables syncing open tabs between multiple browser instances.">
     Open Tabs
   </message>
diff --git a/chrome/browser/android/feed/feed_image_loader_bridge.cc b/chrome/browser/android/feed/feed_image_loader_bridge.cc
index 7aafff28..4e94e69 100644
--- a/chrome/browser/android/feed/feed_image_loader_bridge.cc
+++ b/chrome/browser/android/feed/feed_image_loader_bridge.cc
@@ -24,6 +24,7 @@
 
 using base::android::JavaIntArrayToIntVector;
 using base::android::JavaParamRef;
+using base::android::JavaRef;
 using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -50,17 +51,17 @@
 FeedImageLoaderBridge::~FeedImageLoaderBridge() = default;
 
 void FeedImageLoaderBridge::Destroy(JNIEnv* env,
-                                    const JavaParamRef<jobject>& j_this) {
+                                    const JavaRef<jobject>& j_this) {
   delete this;
 }
 
-void FeedImageLoaderBridge::FetchImage(
-    JNIEnv* j_env,
-    const JavaParamRef<jobject>& j_this,
-    const JavaParamRef<jobjectArray>& j_urls,
-    const JavaParamRef<jobject>& j_callback) {
+void FeedImageLoaderBridge::FetchImage(JNIEnv* j_env,
+                                       const JavaRef<jobject>& j_this,
+                                       const JavaRef<jobjectArray>& j_urls,
+                                       const JavaRef<jobject>& j_callback) {
   std::vector<std::string> urls;
-  base::android::AppendJavaStringArrayToStringVector(j_env, j_urls, &urls);
+  base::android::AppendJavaStringArrayToStringVector(j_env, j_urls.obj(),
+                                                     &urls);
 
   ScopedJavaGlobalRef<jobject> callback(j_callback);
   feed_image_manager_->FetchImage(
diff --git a/chrome/browser/android/feed/feed_image_loader_bridge.h b/chrome/browser/android/feed/feed_image_loader_bridge.h
index 9c14bbc..631f208 100644
--- a/chrome/browser/android/feed/feed_image_loader_bridge.h
+++ b/chrome/browser/android/feed/feed_image_loader_bridge.h
@@ -23,13 +23,12 @@
   explicit FeedImageLoaderBridge(FeedImageManager* feed_image_manager);
   ~FeedImageLoaderBridge();
 
-  void Destroy(JNIEnv* j_env,
-               const base::android::JavaParamRef<jobject>& j_this);
+  void Destroy(JNIEnv* j_env, const base::android::JavaRef<jobject>& j_this);
 
   void FetchImage(JNIEnv* j_env,
-                  const base::android::JavaParamRef<jobject>& j_this,
-                  const base::android::JavaParamRef<jobjectArray>& j_urls,
-                  const base::android::JavaParamRef<jobject>& j_callback);
+                  const base::android::JavaRef<jobject>& j_this,
+                  const base::android::JavaRef<jobjectArray>& j_urls,
+                  const base::android::JavaRef<jobject>& j_callback);
 
  private:
   void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> callback,
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h
index 0e587283..b322024 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.h
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -30,7 +30,6 @@
 #include "ui/base/ime/chromeos/input_method_manager.h"
 
 class Browser;
-class DictationChromeos;
 class Profile;
 
 namespace gfx {
@@ -40,6 +39,7 @@
 namespace chromeos {
 
 class AccessibilityExtensionLoader;
+class DictationChromeos;
 class SelectToSpeakEventHandler;
 class SwitchAccessEventHandler;
 
@@ -438,6 +438,7 @@
 
   base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_;
 
+  friend class DictationTest;
   DISALLOW_COPY_AND_ASSIGN(AccessibilityManager);
 };
 
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
index 811d21c..e574962 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
@@ -19,6 +19,8 @@
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_input_context_handler_interface.h"
 
+namespace chromeos {
+
 namespace {
 
 const char kDefaultProfileLocale[] = "en-US";
@@ -59,8 +61,13 @@
 
 void DictationChromeos::OnSpeechResult(const base::string16& query,
                                        bool is_final) {
+  composition_->text = query;
+
   if (!is_final) {
-    composition_->text = query;
+    // If ChromeVox is enabled, we don't want to show intermediate results
+    if (AccessibilityManager::Get()->IsSpokenFeedbackEnabled())
+      return;
+
     if (input_context_)
       input_context_->UpdateCompositionText(*composition_, 0, true);
     return;
@@ -109,3 +116,5 @@
       details);
   speech_recognizer_.reset();
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos.h b/chrome/browser/chromeos/accessibility/dictation_chromeos.h
index fe32a9d9..f2bb284 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos.h
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos.h
@@ -21,6 +21,8 @@
 class Profile;
 class SpeechRecognizer;
 
+namespace chromeos {
+
 // Provides global dictation (type what you speak) on Chrome OS.
 class DictationChromeos : public SpeechRecognizerDelegate,
                           ui::IMEBridgeObserver {
@@ -43,7 +45,7 @@
   // IMEBridgeObserver
   void OnRequestSwitchEngine() override;
 
-  // Saves current dictation state and stops listening.
+  // Saves current dictation result and stops listening.
   void DictationOff();
 
   std::unique_ptr<SpeechRecognizer> speech_recognizer_;
@@ -54,7 +56,10 @@
 
   base::WeakPtrFactory<DictationChromeos> weak_ptr_factory_;
 
+  friend class DictationTest;
   DISALLOW_COPY_AND_ASSIGN(DictationChromeos);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_DICTATION_CHROMEOS_H_
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc b/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc
new file mode 100644
index 0000000..d9344df
--- /dev/null
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos_browsertest.cc
@@ -0,0 +1,125 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/accessibility/dictation_chromeos.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/base/ime/ime_bridge.h"
+#include "ui/base/ime/mock_ime_input_context_handler.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kFirstSpeechResult[] = "help";
+const char kSecondSpeechResult[] = "help oh";
+const char kFinalSpeechResult[] = "hello world";
+
+}  // namespace
+
+class DictationTest : public InProcessBrowserTest {
+ protected:
+  DictationTest() {
+    input_context_handler_.reset(new ui::MockIMEInputContextHandler());
+    empty_composition_text_ =
+        ui::MockIMEInputContextHandler::UpdateCompositionTextArg()
+            .composition_text;
+  }
+  ~DictationTest() override = default;
+
+  void SetUpOnMainThread() override {
+    ui::IMEBridge::Get()->SetInputContextHandler(input_context_handler_.get());
+  }
+
+  AccessibilityManager* GetManager() { return AccessibilityManager::Get(); }
+
+  void EnableChromeVox() { GetManager()->EnableSpokenFeedback(true); }
+
+  void SendSpeechResult(const char* result, bool is_final) {
+    GetManager()->dictation_->OnSpeechResult(base::ASCIIToUTF16(result),
+                                             is_final);
+  }
+
+  ui::CompositionText GetLastCompositionText() {
+    return input_context_handler_->last_update_composition_arg()
+        .composition_text;
+  }
+
+  std::unique_ptr<ui::MockIMEInputContextHandler> input_context_handler_;
+  ui::CompositionText empty_composition_text_;
+};
+
+IN_PROC_BROWSER_TEST_F(DictationTest, RecognitionEnds) {
+  AccessibilityManager* manager = GetManager();
+
+  manager->ToggleDictation();
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kFirstSpeechResult, false /* is_final */);
+  EXPECT_EQ(base::ASCIIToUTF16(kFirstSpeechResult),
+            GetLastCompositionText().text);
+
+  SendSpeechResult(kSecondSpeechResult, false /* is_final */);
+  EXPECT_EQ(base::ASCIIToUTF16(kSecondSpeechResult),
+            GetLastCompositionText().text);
+
+  SendSpeechResult(kFinalSpeechResult, true /* is_final */);
+  EXPECT_EQ(1, input_context_handler_->commit_text_call_count());
+  EXPECT_EQ(kFinalSpeechResult, input_context_handler_->last_commit_text());
+}
+
+IN_PROC_BROWSER_TEST_F(DictationTest, RecognitionEndsWithChromeVoxEnabled) {
+  AccessibilityManager* manager = GetManager();
+
+  EnableChromeVox();
+  EXPECT_TRUE(manager->IsSpokenFeedbackEnabled());
+
+  manager->ToggleDictation();
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kFirstSpeechResult, false /* is_final */);
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kSecondSpeechResult, false /* is_final */);
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kFinalSpeechResult, true /* is_final */);
+  EXPECT_EQ(1, input_context_handler_->commit_text_call_count());
+  EXPECT_EQ(kFinalSpeechResult, input_context_handler_->last_commit_text());
+}
+
+IN_PROC_BROWSER_TEST_F(DictationTest, UserEndsDictation) {
+  AccessibilityManager* manager = GetManager();
+
+  manager->ToggleDictation();
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kFinalSpeechResult, false /* is_final */);
+  EXPECT_EQ(base::ASCIIToUTF16(kFinalSpeechResult),
+            GetLastCompositionText().text);
+
+  manager->ToggleDictation();
+  EXPECT_EQ(1, input_context_handler_->commit_text_call_count());
+  EXPECT_EQ(kFinalSpeechResult, input_context_handler_->last_commit_text());
+}
+
+IN_PROC_BROWSER_TEST_F(DictationTest, UserEndsDictationWhenChromeVoxEnabled) {
+  AccessibilityManager* manager = GetManager();
+
+  EnableChromeVox();
+  EXPECT_TRUE(manager->IsSpokenFeedbackEnabled());
+
+  manager->ToggleDictation();
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  SendSpeechResult(kFinalSpeechResult, false /* is_final */);
+  EXPECT_EQ(GetLastCompositionText().text, empty_composition_text_.text);
+
+  manager->ToggleDictation();
+  EXPECT_EQ(1, input_context_handler_->commit_text_call_count());
+  EXPECT_EQ(kFinalSpeechResult, input_context_handler_->last_commit_text());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index 473f9c3d..f37e168 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -69,7 +69,7 @@
                        PreferenceChange) {
   // TODO(penghuang): Re-enable once the EXO+Viz work is done and Arc can be
   // supported. https://crbug.com/807465
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 
   ASSERT_EQ(mojom::AccessibilityFilterType::OFF,
diff --git a/chrome/browser/chromeos/extensions/info_private_api.cc b/chrome/browser/chromeos/extensions/info_private_api.cc
index 3699132..0b537e42 100644
--- a/chrome/browser/chromeos/extensions/info_private_api.cc
+++ b/chrome/browser/chromeos/extensions/info_private_api.cc
@@ -178,9 +178,13 @@
 const char kStylusStatusUnsupported[] = "unsupported";
 
 // Value to which stylusStatus property is set when the device supports stylus
-// input.
+// input, but no stylus has been seen before.
 const char kStylusStatusSupported[] = "supported";
 
+// Value to which stylusStatus property is set when the device has a built-in
+// stylus or a stylus has been seen before.
+const char kStylusStatusSeen[] = "seen";
+
 const struct {
   const char* api_name;
   const char* preference_name;
@@ -352,8 +356,10 @@
       return std::make_unique<base::Value>(kStylusStatusUnsupported);
     }
 
-    // TODO(michaelpg): Return "seen" if stylus has been used.
-    return std::make_unique<base::Value>(kStylusStatusSupported);
+    bool seen = g_browser_process->local_state()->HasPrefPath(
+        ash::prefs::kHasSeenStylus);
+    return std::make_unique<base::Value>(seen ? kStylusStatusSeen
+                                              : kStylusStatusSupported);
   }
 
   if (property_name == kPropertyClientId) {
diff --git a/chrome/browser/chromeos/extensions/info_private_apitest.cc b/chrome/browser/chromeos/extensions/info_private_apitest.cc
index fc5bd9ee..8df7daae 100644
--- a/chrome/browser/chromeos/extensions/info_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/info_private_apitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/stylus_utils.h"
 #include "base/sys_info.h"
 #include "base/values.h"
@@ -11,11 +12,16 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_install_attributes.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/arc/arc_util.h"
 #include "components/prefs/pref_service.h"
+#include "services/ui/public/cpp/input_devices/input_device_client_test_api.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/touchscreen_device.h"
+#include "ui/events/test/event_generator.h"
 
 namespace {
 
@@ -138,12 +144,61 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeOSInfoPrivateTest, StylusSupported) {
-  ash::stylus_utils::SetHasStylusInputForTesting();
+  ui::InputDeviceClientTestApi test_api;
+  ui::TouchscreenDevice touchscreen(1,
+                                    ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
+                                    "Touchscreen", gfx::Size(1024, 768), 0);
+  touchscreen.has_stylus = true;
+  test_api.SetTouchscreenDevices({touchscreen});
+
   ASSERT_TRUE(RunPlatformAppTestWithArg("chromeos_info_private/extended",
                                         "stylus supported"))
       << message_;
 }
 
+// TODO(https://crbug.com/814675): Excluded from Mash because pointer events
+// aren't seen.
+IN_PROC_BROWSER_TEST_F(ChromeOSInfoPrivateTest, StylusSeen) {
+  ui::InputDeviceClientTestApi test_api;
+  ui::TouchscreenDevice touchscreen(1,
+                                    ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
+                                    "Touchscreen", gfx::Size(1024, 768), 0);
+  touchscreen.has_stylus = true;
+  test_api.SetTouchscreenDevices({touchscreen});
+
+  ui::test::EventGenerator generator(browser()->window()->GetNativeWindow());
+  generator.EnterPenPointerMode();
+  generator.PressTouch();
+  generator.ReleaseTouch();
+  generator.ExitPenPointerMode();
+
+  ASSERT_TRUE(RunPlatformAppTestWithArg("chromeos_info_private/extended",
+                                        "stylus seen"))
+      << message_;
+}
+
+class ChromeOSInfoPrivateInternalStylusTest : public ChromeOSInfoPrivateTest {
+ public:
+  ChromeOSInfoPrivateInternalStylusTest() = default;
+  ~ChromeOSInfoPrivateInternalStylusTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ChromeOSInfoPrivateTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(ash::switches::kHasInternalStylus);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeOSInfoPrivateInternalStylusTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ChromeOSInfoPrivateInternalStylusTest,
+                       StylusSeenInternal) {
+  ash::stylus_utils::SetHasStylusInputForTesting();
+  ASSERT_TRUE(RunPlatformAppTestWithArg("chromeos_info_private/extended",
+                                        "stylus seen"))
+      << message_;
+}
+
 class ChromeOSArcInfoPrivateTest : public ChromeOSInfoPrivateTest {
  public:
   ChromeOSArcInfoPrivateTest() = default;
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util.cc b/chrome/browser/chromeos/fileapi/external_file_url_util.cc
index ed206334..3cbd637 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_util.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_util.cc
@@ -44,11 +44,8 @@
 base::FilePath ExternalFileURLToVirtualPath(const GURL& url) {
   if (!url.is_valid() || url.scheme() != content::kExternalFileScheme)
     return base::FilePath();
-  const std::string path_string = net::UnescapeURLComponent(
-      url.path(),
-      net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
-          net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
-          net::UnescapeRule::NONASCII_SPACES);
+  const std::string path_string =
+      net::UnescapeBinaryURLComponent(url.path(), net::UnescapeRule::NORMAL);
   return base::FilePath::FromUTF8Unsafe(path_string);
 }
 
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc b/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
index 17074cf..99951e3 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
@@ -103,6 +103,14 @@
   // Path containing some ASCII characters that are escaped by URL enconding.
   ExpectVirtualPathRoundtrip(FILE_PATH_LITERAL("foo/bar \"#<>?`{}.txt"),
                              "foo/bar%20%22%23%3C%3E%3F%60%7B%7D.txt");
+
+  // (U+3000) IDEOGRAPHIC SPACE and (U+1F512) LOCK are examples of characters
+  // potentially used for URL spoofing. Those are blacklisted from unescaping
+  // when a URL is displayed, but this should not prevent it from being
+  // unescaped when converting a URL to a virtual file path. See
+  // crbug.com/585422 for detail.
+  ExpectVirtualPathRoundtrip(FILE_PATH_LITERAL("foo/bar/space\u3000lock🔒.zip"),
+                             "foo/bar/space%E3%80%80lock%F0%9F%94%92.zip");
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index af73590..50a9d17 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -685,27 +685,16 @@
       }
     }
 
-    if (container.staging_percent_of_fleet_per_week_size()) {
-      auto staging_percent_of_fleet_per_week_policy =
-          std::make_unique<base::ListValue>();
+    if (container.has_staging_schedule()) {
+      std::unique_ptr<base::Value> staging_percent_of_fleet_per_week_policy =
+          DecodeJsonStringAndDropUnknownBySchema(
+              container.staging_schedule(), key::kDeviceUpdateStagingSchedule);
 
-      bool error_decoding = false;
-      for (const auto& entry : container.staging_percent_of_fleet_per_week()) {
-        std::unique_ptr<base::Value> value = DecodeIntegerValue(entry);
-        if (value) {
-          staging_percent_of_fleet_per_week_policy->Append(std::move(value));
-        } else {
-          error_decoding = true;
-          LOG(ERROR)
-              << "Could not decode integer value for staging percentage.";
-          break;
-        }
-      }
-      if (!error_decoding) {
-        policies->Set(
-            key::kDeviceUpdateStagingPercentOfFleetPerWeek,
-            POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
-            std::move(staging_percent_of_fleet_per_week_policy), nullptr);
+      if (staging_percent_of_fleet_per_week_policy) {
+        policies->Set(key::kDeviceUpdateStagingSchedule, POLICY_LEVEL_MANDATORY,
+                      POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+                      std::move(staging_percent_of_fleet_per_week_policy),
+                      nullptr);
       }
     }
   }
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.cc b/chrome/browser/chromeos/smb_client/smb_file_system.cc
index 71cdc20..5687d88c 100644
--- a/chrome/browser/chromeos/smb_client/smb_file_system.cc
+++ b/chrome/browser/chromeos/smb_client/smb_file_system.cc
@@ -23,6 +23,8 @@
 
 namespace {
 
+using file_system_provider::ProvidedFileSystemInterface;
+
 // This is a bogus data URI.
 // The Files app will attempt to download a whole image to create a thumbnail
 // any time you visit a folder. A bug (crbug.com/548050) tracks not doing that
@@ -38,7 +40,12 @@
 // Maximum number of entries to send at a time for read directory,
 constexpr uint32_t kReadDirectoryMaxBatchSize = 2048;
 
-using file_system_provider::ProvidedFileSystemInterface;
+constexpr ProvidedFileSystemInterface::MetadataFieldMask kRequestableFields =
+    ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_IS_DIRECTORY |
+    ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_NAME |
+    ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_SIZE |
+    ProvidedFileSystemInterface::MetadataField::
+        METADATA_FIELD_MODIFICATION_TIME;
 
 bool RequestedIsDirectory(
     ProvidedFileSystemInterface::MetadataFieldMask fields) {
@@ -67,6 +74,12 @@
          ProvidedFileSystemInterface::MetadataField::METADATA_FIELD_THUMBNAIL;
 }
 
+bool IsRedundantRequest(ProvidedFileSystemInterface::MetadataFieldMask fields) {
+  // If there isn't at least 1 requestable field there is no point doing a
+  // network request.
+  return (fields & kRequestableFields) == 0;
+}
+
 // Metrics recording.
 void RecordReadDirectoryCount(int count) {
   UMA_HISTOGRAM_COUNTS_100000("NativeSmbFileShare.ReadDirectoryCount", count);
@@ -170,6 +183,10 @@
     const base::FilePath& entry_path,
     ProvidedFileSystemInterface::MetadataFieldMask fields,
     ProvidedFileSystemInterface::GetMetadataCallback callback) {
+  if (IsRedundantRequest(fields)) {
+    return HandleSyncRedundantGetMetadata(fields, std::move(callback));
+  }
+
   auto reply =
       base::BindOnce(&SmbFileSystem::HandleRequestGetMetadataEntryCallback,
                      AsWeakPtr(), fields, callback);
@@ -601,6 +618,22 @@
   std::move(callback).Run(TranslateToFileError(error));
 }
 
+AbortCallback SmbFileSystem::HandleSyncRedundantGetMetadata(
+    ProvidedFileSystemInterface::MetadataFieldMask fields,
+    ProvidedFileSystemInterface::GetMetadataCallback callback) {
+  auto metadata = std::make_unique<file_system_provider::EntryMetadata>();
+
+  // The fields could be empty or have one or both of thumbnail and metadata.
+  // We completely ignore metadata, but populate the bogus URI for the
+  // thumbnail.
+  if (RequestedThumbnail(fields)) {
+    metadata->thumbnail = std::make_unique<std::string>(kUnknownImageDataUri);
+  }
+
+  std::move(callback).Run(std::move(metadata), base::File::FILE_OK);
+  return CreateAbortCallback();
+}
+
 void SmbFileSystem::HandleRequestGetMetadataEntryCallback(
     ProvidedFileSystemInterface::MetadataFieldMask fields,
     ProvidedFileSystemInterface::GetMetadataCallback callback,
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.h b/chrome/browser/chromeos/smb_client/smb_file_system.h
index 0fc1ddcb..ce435e5 100644
--- a/chrome/browser/chromeos/smb_client/smb_file_system.h
+++ b/chrome/browser/chromeos/smb_client/smb_file_system.h
@@ -215,6 +215,10 @@
       smbprovider::ErrorType error,
       const smbprovider::DirectoryEntryListProto& entries) const;
 
+  file_system_provider::AbortCallback HandleSyncRedundantGetMetadata(
+      ProvidedFileSystemInterface::MetadataFieldMask fields,
+      ProvidedFileSystemInterface::GetMetadataCallback callback);
+
   void HandleRequestGetMetadataEntryCallback(
       ProvidedFileSystemInterface::MetadataFieldMask fields,
       ProvidedFileSystemInterface::GetMetadataCallback callback,
diff --git a/chrome/browser/conflicts/incompatible_applications_browsertest.cc b/chrome/browser/conflicts/incompatible_applications_browsertest.cc
index 12666d3d..9915277d 100644
--- a/chrome/browser/conflicts/incompatible_applications_browsertest.cc
+++ b/chrome/browser/conflicts/incompatible_applications_browsertest.cc
@@ -86,7 +86,7 @@
     // TODO(crbug.com/850517): Don't do test-specific setup if the test isn't
     // going to do anything. It seems to conflict with the VizDisplayCompositor
     // feature.
-    if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+    if (!features::IsVizDisplayCompositorEnabled()) {
       ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
 
       ASSERT_NO_FATAL_FAILURE(
@@ -199,7 +199,7 @@
     return;
 
   // TODO(crbug.com/850517) This fails in viz_browser_tests in official builds.
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 
   ModuleDatabase* module_database = ModuleDatabase::GetInstance();
diff --git a/chrome/browser/devtools/device/devtools_device_discovery.cc b/chrome/browser/devtools/device/devtools_device_discovery.cc
index fbada04..ed5e08a2 100644
--- a/chrome/browser/devtools/device/devtools_device_discovery.cc
+++ b/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -365,11 +365,15 @@
     : public base::RefCountedThreadSafe<DiscoveryRequest,
                                         BrowserThread::DeleteOnUIThread> {
  public:
-  DiscoveryRequest(AndroidDeviceManager* device_manager,
-                   const DevToolsDeviceDiscovery::DeviceListCallback& callback);
+  static void Start(
+      AndroidDeviceManager* device_manager,
+      const DevToolsDeviceDiscovery::DeviceListCallback& callback);
+
  private:
   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
   friend class base::DeleteHelper<DiscoveryRequest>;
+  explicit DiscoveryRequest(
+      const DevToolsDeviceDiscovery::DeviceListCallback& callback);
   virtual ~DiscoveryRequest();
 
   void ReceivedDevices(const AndroidDeviceManager::Devices& devices);
@@ -387,13 +391,20 @@
   DevToolsDeviceDiscovery::CompleteDevices complete_devices_;
 };
 
-DevToolsDeviceDiscovery::DiscoveryRequest::DiscoveryRequest(
+// static
+void DevToolsDeviceDiscovery::DiscoveryRequest::Start(
     AndroidDeviceManager* device_manager,
+    const DevToolsDeviceDiscovery::DeviceListCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto request = base::WrapRefCounted(new DiscoveryRequest(callback));
+  device_manager->QueryDevices(
+      base::Bind(&DiscoveryRequest::ReceivedDevices, request));
+}
+
+DevToolsDeviceDiscovery::DiscoveryRequest::DiscoveryRequest(
     const DevToolsDeviceDiscovery::DeviceListCallback& callback)
     : callback_(callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  device_manager->QueryDevices(
-      base::Bind(&DiscoveryRequest::ReceivedDevices, this));
 }
 
 DevToolsDeviceDiscovery::DiscoveryRequest::~DiscoveryRequest() {
@@ -599,10 +610,9 @@
 
 void DevToolsDeviceDiscovery::RequestDeviceList() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  new DiscoveryRequest(
-      device_manager_,
-      base::Bind(&DevToolsDeviceDiscovery::ReceivedDeviceList,
-                 weak_factory_.GetWeakPtr()));
+  DiscoveryRequest::Start(
+      device_manager_, base::Bind(&DevToolsDeviceDiscovery::ReceivedDeviceList,
+                                  weak_factory_.GetWeakPtr()));
 }
 
 void DevToolsDeviceDiscovery::ReceivedDeviceList(
diff --git a/chrome/browser/devtools/devtools_eye_dropper.cc b/chrome/browser/devtools/devtools_eye_dropper.cc
index b82f19a..45f8146 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.cc
+++ b/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -34,7 +34,7 @@
       last_cursor_y_(-1),
       host_(nullptr),
       use_video_capture_api_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+          features::IsVizDisplayCompositorEnabled() ||
           base::FeatureList::IsEnabled(
               features::kUseVideoCaptureApiForDevToolsSnapshots)),
       weak_factory_(this) {
diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc
index 38e61611..3c4ca44 100644
--- a/chrome/browser/net/predictor.cc
+++ b/chrome/browser/net/predictor.cc
@@ -740,14 +740,11 @@
 };
 
 void Predictor::PreconnectUrlOnIOThread(
-    const GURL& original_url,
+    const GURL& url,
     const GURL& site_for_cookies,
     UrlInfo::ResolutionMotivation motivation,
     bool allow_credentials,
     int count) {
-  // Skip the HSTS redirect.
-  GURL url = GetHSTSRedirectOnIOThread(original_url);
-
   // TODO(csharrison): The observer should only be notified after the null check
   // for the URLRequestContextGetter. The predictor tests should be fixed to
   // allow for this, as they currently expect a callback with no getter.
diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc
index 2545174b..915c663 100644
--- a/chrome/browser/net/predictor_unittest.cc
+++ b/chrome/browser/net/predictor_unittest.cc
@@ -384,87 +384,6 @@
   std::vector<GURL> preconnected_urls_;
 };
 
-// Tests that preconnects apply the HSTS list.
-TEST_F(PredictorTest, HSTSRedirect) {
-  const GURL kHttpUrl("http://example.com");
-  const GURL kHttpsUrl("https://example.com");
-
-  const base::Time expiry =
-      base::Time::Now() + base::TimeDelta::FromSeconds(1000);
-  net::TransportSecurityState state;
-  state.AddHSTS(kHttpUrl.host(), expiry, false);
-
-  Predictor predictor(true);
-  TestPredictorObserver observer;
-  predictor.SetObserver(&observer);
-  predictor.SetTransportSecurityState(&state);
-
-  predictor.PreconnectUrl(kHttpUrl, GURL(), UrlInfo::OMNIBOX_MOTIVATED, true,
-                          2);
-  ASSERT_EQ(1u, observer.preconnected_urls_.size());
-  EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]);
-
-  predictor.Shutdown();
-}
-
-// Tests that preconnecting a URL on the HSTS list preconnects the subresources
-// for the SSL version.
-TEST_F(PredictorTest, HSTSRedirectSubresources) {
-  const GURL kHttpUrl("http://example.com");
-  const GURL kHttpsUrl("https://example.com");
-  const GURL kSubresourceUrl("https://images.example.com");
-  const double kUseRate = 23.4;
-
-  const base::Time expiry =
-      base::Time::Now() + base::TimeDelta::FromSeconds(1000);
-  net::TransportSecurityState state;
-  state.AddHSTS(kHttpUrl.host(), expiry, false);
-
-  SimplePredictor predictor(true);
-  TestPredictorObserver observer;
-  predictor.SetObserver(&observer);
-  predictor.SetTransportSecurityState(&state);
-
-  std::unique_ptr<base::ListValue> referral_list(NewEmptySerializationList());
-  AddToSerializedList(
-      kHttpsUrl, kSubresourceUrl, kUseRate, referral_list.get());
-  predictor.DeserializeReferrers(*referral_list.get());
-
-  predictor.PreconnectUrlAndSubresources(kHttpUrl, GURL());
-  ASSERT_EQ(2u, observer.preconnected_urls_.size());
-  EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]);
-  EXPECT_EQ(kSubresourceUrl, observer.preconnected_urls_[1]);
-
-  predictor.Shutdown();
-}
-
-TEST_F(PredictorTest, HSTSRedirectLearnedSubresource) {
-  const GURL kHttpUrl("http://example.com");
-  const GURL kHttpsUrl("https://example.com");
-  const GURL kSubresourceUrl("https://images.example.com");
-
-  const base::Time expiry =
-      base::Time::Now() + base::TimeDelta::FromSeconds(1000);
-  net::TransportSecurityState state;
-  state.AddHSTS(kHttpUrl.host(), expiry, false);
-
-  SimplePredictor predictor(true);
-  TestPredictorObserver observer;
-  predictor.SetObserver(&observer);
-  predictor.SetTransportSecurityState(&state);
-
-  // Note that the predictor would also learn the HSTS redirect from kHttpUrl to
-  // kHttpsUrl during the navigation.
-  predictor.LearnFromNavigation(kHttpUrl, kSubresourceUrl);
-
-  predictor.PreconnectUrlAndSubresources(kHttpUrl, GURL());
-  ASSERT_EQ(2u, observer.preconnected_urls_.size());
-  EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]);
-  EXPECT_EQ(kSubresourceUrl, observer.preconnected_urls_[1]);
-
-  predictor.Shutdown();
-}
-
 TEST_F(PredictorTest, NoProxyService) {
   // Don't actually try to resolve names.
   Predictor::set_max_parallel_resolves(0);
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 6c61af53..477df9b 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -3348,13 +3348,6 @@
  public:
   PasswordManagerDialogBrowserTest() = default;
 
-  // SupportsTestUi:
-  void SetUp() override {
-    // Secondary UI needs to be enabled before ShowUi for the test to work.
-    UseMdOnly();
-    SupportsTestUi::SetUp();
-  }
-
   void SetUpCommandLine(base::CommandLine* command_line) override {
     SupportsTestDialog<PasswordManagerBrowserTestBase>::SetUpCommandLine(
         command_line);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index af730ba1..dca0890 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -197,11 +197,11 @@
 #include "components/ntp_tiles/popular_sites_impl.h"
 #if BUILDFLAG(ENABLE_FEED_IN_CHROME)
 #include "components/feed/core/feed_scheduler_host.h"
+#include "components/feed/core/refresh_throttler.h"
 #include "components/feed/core/user_classifier.h"
 #endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
 #else
 #include "chrome/browser/gcm/gcm_product_util.h"
-#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/metrics/tab_stats_tracker.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/signin/signin_promo.h"
@@ -619,6 +619,7 @@
   RecentTabsPagePrefs::RegisterProfilePrefs(registry);
 #if BUILDFLAG(ENABLE_FEED_IN_CHROME)
   feed::FeedSchedulerHost::RegisterProfilePrefs(registry);
+  feed::RefreshThrottler::RegisterProfilePrefs(registry);
   feed::UserClassifier::RegisterProfilePrefs(registry);
 #endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
 #else
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 12c384a..d36e89e 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -48,6 +48,11 @@
          state == LifecycleUnitState::PENDING_DISCARD;
 }
 
+bool IsFrozenOrPendingFreeze(LifecycleUnitState state) {
+  return state == LifecycleUnitState::FROZEN ||
+         state == LifecycleUnitState::PENDING_FREEZE;
+}
+
 // Returns true if it is valid to transition from |from| to |to| for |reason|.
 bool IsValidStateChange(LifecycleUnitState from,
                         LifecycleUnitState to,
@@ -89,14 +94,17 @@
         // The renderer notifies the browser that the page has been frozen.
         case LifecycleUnitState::FROZEN:
           return reason == StateChangeReason::RENDERER_INITIATED;
+        // Unfreeze() is called.
+        case LifecycleUnitState::PENDING_UNFREEZE:
+          return reason == StateChangeReason::BROWSER_INITIATED;
         default:
           return false;
       }
     }
     case LifecycleUnitState::FROZEN: {
       switch (to) {
-        // The renderer notifies the browser that the page was unfrozen after it
-        // became visible.
+        // The renderer notifies the browser that the page was unfrozen after
+        // it became visible.
         case LifecycleUnitState::ACTIVE: {
           return reason == StateChangeReason::RENDERER_INITIATED;
         }
@@ -131,7 +139,7 @@
     }
     case LifecycleUnitState::DISCARDED: {
       switch (to) {
-        // The WebContents is focused.
+        // The WebContents is focused or reloaded.
         case LifecycleUnitState::ACTIVE:
           return reason == StateChangeReason::USER_INITIATED;
         default:
@@ -317,12 +325,24 @@
     mojom::LifecycleState state) {
   switch (state) {
     case mojom::LifecycleState::kFrozen: {
-      if (GetState() == LifecycleUnitState::PENDING_DISCARD) {
-        freeze_timeout_timer_->Stop();
-        FinishDiscard(discard_reason_);
-      } else {
-        SetState(LifecycleUnitState::FROZEN,
-                 StateChangeReason::RENDERER_INITIATED);
+      switch (GetState()) {
+        case LifecycleUnitState::PENDING_DISCARD: {
+          freeze_timeout_timer_->Stop();
+          FinishDiscard(discard_reason_);
+          break;
+        }
+        case LifecycleUnitState::PENDING_UNFREEZE: {
+          // By the time the kFrozen state message arrive the tab might have
+          // been reloaded by the user (by right-clicking on the tab) and might
+          // have been switched to the PENDING_UNFREEZE state. It's safe to just
+          // ignore this message here as an unfreeze signal has been sent to the
+          // tab and so a |kRunning| transition will follow this one.
+          break;
+        }
+        default: {
+          SetState(LifecycleUnitState::FROZEN,
+                   StateChangeReason::RENDERER_INITIATED);
+        }
       }
       break;
     }
@@ -866,6 +886,13 @@
     // This happens when a discarded tab is explicitly reloaded without being
     // focused first (right-click > Reload).
     SetState(LifecycleUnitState::ACTIVE, StateChangeReason::USER_INITIATED);
+  } else if (IsFrozenOrPendingFreeze(GetState())) {
+    // This happens when a frozen tab is explicitly reloaded without being
+    // focused first (right-click > Reload).
+    Unfreeze();
+
+    if (freeze_timeout_timer_)
+      freeze_timeout_timer_->Stop();
   }
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 9acdad99..7be8ecf1 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -737,4 +737,18 @@
   EXPECT_TRUE(decision_details.IsPositive());
 }
 
+TEST_F(TabLifecycleUnitTest, ReloadingAFrozenTabUnfreezeIt) {
+  TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
+                                      usage_clock_.get(), web_contents_,
+                                      tab_strip_model_.get());
+  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
+                                                   LoadingState::LOADED);
+  DecisionDetails decision_details;
+  EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
+
+  tab_lifecycle_unit.Freeze();
+  web_contents_->GetController().Reload(content::ReloadType::NORMAL, false);
+  EXPECT_NE(LifecycleUnitState::FROZEN, tab_lifecycle_unit.GetState());
+}
+
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index db2b3eda..a6c206a 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -1012,14 +1012,10 @@
 // Verifies the following state transitions for a tab:
 // - Initial state: ACTIVE
 // - Freeze(): ACTIVE->PENDING_FREEZE
-// - Unfreeze(): Disallowed. Transition to PENDING_FREEZE->FROZEN should happen
-//     once the freeze happens in renderer.
+// - Unfreeze(): PENDING_FREEZE->FROZEN
 IN_PROC_BROWSER_TEST_F(TabManagerTest, TabPendingFreezeAndUnfreeze) {
   TestTransitionFromActiveToPendingFreeze();
 
-  // Unfreezing a PENDING_FREEZE tab is not allowed. The tab must be fully
-  // frozen before it is unfrozen.
-  EXPECT_FALSE(GetLifecycleUnitAt(1)->Unfreeze());
   EXPECT_EQ(LifecycleUnitState::PENDING_FREEZE,
             GetLifecycleUnitAt(1)->GetState());
 
@@ -1431,6 +1427,19 @@
   EXPECT_TRUE(IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(1)));
 }
 
+IN_PROC_BROWSER_TEST_F(TabManagerTest, UnfreezeTabOnNavigationEvent) {
+  TestTransitionFromActiveToPendingFreeze();
+
+  browser()->tab_strip_model()->GetWebContentsAt(1)->GetController().Reload(
+      content::ReloadType::NORMAL, false);
+
+  ExpectStateTransitionObserver expect_state_transition(
+      GetLifecycleUnitAt(1), LifecycleUnitState::ACTIVE);
+  expect_state_transition.AllowState(LifecycleUnitState::PENDING_UNFREEZE);
+  expect_state_transition.AllowState(LifecycleUnitState::FROZEN);
+  expect_state_transition.Wait();
+}
+
 }  // namespace resource_coordinator
 
 #endif  // OS_WIN || OS_MAXOSX || OS_LINUX || defined(OS_CHROMEOS)
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.css b/chrome/browser/resources/local_ntp/custom_backgrounds.css
index c26b9906..e9c2cff 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.css
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.css
@@ -499,9 +499,11 @@
   bottom: 16px;
   color: white;
   font-family: 'Roboto', arial, sans-serif;
+  font-weight: 500;
   left: 16px;
   padding: 8px 8px 8px 8px;
   position: fixed;
+  text-shadow: 0 0 16px rgba(0, 0, 0, 0.3);
 }
 
 html[dir=rtl] #custom-bg-attr {
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.js b/chrome/browser/resources/local_ntp/custom_backgrounds.js
index 593f861..cf9efb1 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.js
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.js
@@ -33,8 +33,6 @@
   ATTRIBUTIONS: 'custom-bg-attr',
   BACK: 'bg-sel-back',
   CANCEL: 'bg-sel-footer-cancel',
-  CONNECT_GOOGLE_PHOTOS: 'edit-bg-google-photos',
-  CONNECT_GOOGLE_PHOTOS_TEXT: 'edit-bg-google-photos-text',
   CUSTOM_LINKS_RESTORE_DEFAULT: 'custom-links-restore-default',
   CUSTOM_LINKS_RESTORE_DEFAULT_TEXT: 'custom-links-restore-default-text',
   DEFAULT_WALLPAPERS: 'edit-bg-default-wallpapers',
@@ -105,11 +103,10 @@
  * @const
  */
 customBackgrounds.MENU_ENTRIES = {
-  GOOGLE_PHOTOS: 0,
-  CHROME_BACKGROUNDS: 1,
-  UPLOAD_IMAGE: 2,
-  RESTORE_DEFAULT: 3,
-  RESTORE_DEFAULT_CUSTOM_LINKS: 4,
+  CHROME_BACKGROUNDS: 0,
+  UPLOAD_IMAGE: 1,
+  RESTORE_DEFAULT: 2,
+  CUSTOM_LINKS_RESTORE_DEFAULT: 3,
 };
 
 customBackgrounds.CUSTOM_BACKGROUND_OVERLAY =
@@ -640,11 +637,10 @@
   // Create array corresponding to the menu. Important that this is in the same
   // order as the MENU_ENTRIES enum, so we can index into it.
   var entries = [];
-  entries.push($(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS));
   entries.push($(customBackgrounds.IDS.DEFAULT_WALLPAPERS));
   entries.push($(customBackgrounds.IDS.UPLOAD_IMAGE));
   entries.push($(customBackgrounds.IDS.RESTORE_DEFAULT));
-  entries.push($(customBackgrounds.IDS.RESTORE_DEFAULT_CUSTOM_LINKS));
+  entries.push($(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT));
 
   var idx = current_index;
   do {
@@ -663,7 +659,6 @@
  * @param {bool} online The current state of the network
  */
 customBackgrounds.networkStateChanged = function(online) {
-  $(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS).hidden = !online;
   $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).hidden = !online;
 };
 
@@ -770,12 +765,12 @@
     else if (event.keyCode === customBackgrounds.KEYCODES.UP) {
       customBackgrounds
           .getNextOption(
-              customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT_CUSTOM_LINKS, -1)
+              customBackgrounds.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, -1)
           .focus();
     } else if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
       customBackgrounds
           .getNextOption(
-              customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT_CUSTOM_LINKS, 1)
+              customBackgrounds.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, 1)
           .focus();
     }
   };
@@ -789,8 +784,6 @@
   var editDialog = $(customBackgrounds.IDS.EDIT_BG_DIALOG);
   var menu = $(customBackgrounds.IDS.MENU);
 
-  $(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS_TEXT).textContent =
-      configData.translatedStrings.connectGooglePhotos;
   $(customBackgrounds.IDS.DEFAULT_WALLPAPERS_TEXT).textContent =
       configData.translatedStrings.defaultWallpapers;
   $(customBackgrounds.IDS.UPLOAD_IMAGE_TEXT).textContent =
@@ -909,52 +902,6 @@
     }
   };
 
-  // Interactions with the Google Photos option.
-  var googlePhotosInteraction = function(event) {
-    customBackgrounds.loadGooglePhotosAlbums();
-    $('ntp-album-loader').onload = function() {
-      if (typeof albums != 'undefined' && !albums_errors.auth_error &&
-          !albums_errors.net_error && !albums_errors.service_error) {
-        editDialog.close();
-        customBackgrounds.showCollectionSelectionDialog(
-            customBackgrounds.SOURCES.GOOGLE_PHOTOS);
-      } else {
-        // If an auth error occurs leave the dialog open and redirect the
-        // user to sign-in again. Then they can return to the same place in
-        // the customization flow.
-        if (!albums_errors.auth_error) {
-          editDialog.close();
-        }
-
-        customBackgrounds.handleError(albums_errors);
-      }
-    };
-  };
-  $(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS).onclick = function(event) {
-    $(customBackgrounds.IDS.MENU)
-        .classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
-    googlePhotosInteraction(event);
-  };
-  $(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS).onkeyup = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
-      $(customBackgrounds.IDS.MENU)
-          .classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
-      googlePhotosInteraction(event);
-    }
-
-    // Handle arrow key navigation.
-    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.GOOGLE_PHOTOS, -1)
-          .focus();
-    }
-    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.GOOGLE_PHOTOS, 1)
-          .focus();
-    }
-  };
-
   // Escape and Backspace handling for the background picker dialog.
   menu.onkeydown = function(event) {
     if (event.keyCode === customBackgrounds.KEYCODES.ESC ||
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 5ef8a501..b3b183b 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -8,6 +8,10 @@
   <link rel="stylesheet" href="chrome-search://local-ntp/local-ntp.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/voice.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/custom-backgrounds.css"></link>
+  <meta http-equiv="Content-Security-Policy"
+      content="{{CONTENT_SECURITY_POLICY}}">
+  <script src="chrome-search://local-ntp/config.js"
+      {{CONFIG_DATA_INTEGRITY}}></script>
   <script src="chrome-search://local-ntp/custom-backgrounds.js"
       {{LOCAL_NTP_CUSTOM_BG_INTEGRITY}}></script>
   <script src="chrome-search://local-ntp/local-ntp.js"
@@ -88,10 +92,6 @@
 
   <dialog div id="edit-bg-dialog">
     <div id="edit-bg-title"></div>
-    <div id="edit-bg-google-photos" class="bg-option" tabindex="0" hidden>
-      <div class="bg-option-img"></div>
-      <div id="edit-bg-google-photos-text" class="bg-option-text"></div>
-    </div>
     <div id="edit-bg-default-wallpapers" class="bg-option" tabindex="0">
       <div class="bg-option-img"></div>
       <div id="edit-bg-default-wallpapers-text" class="bg-option-text">
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 2170e54..ad5b710f 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -177,6 +177,23 @@
 
 
 /**
+ * The maximum number of tiles to show in the Most Visited section.
+ * @type {number}
+ * @const
+ */
+const MAX_NUM_TILES_MOST_VISITED = 8;
+
+
+/**
+ * The maximum number of tiles to show in the Most Visited section if custom
+ * links is enabled.
+ * @type {number}
+ * @const
+ */
+const MAX_NUM_TILES_CUSTOM_LINKS = 10;
+
+
+/**
  * Background colors considered "white". Used to determine if it is possible
  * to display a Google Doodle, or if the notifier should be used instead.
  * @type {Array<string>}
@@ -236,10 +253,6 @@
 var ntpApiHandle;
 
 
-/** @type {number} @const */
-var MAX_NUM_TILES_TO_SHOW = 8;
-
-
 /**
  * Returns theme background info, first checking for history.state.notheme. If
  * the page has notheme set, returns a fallback light-colored theme.
@@ -495,7 +508,10 @@
 
   var pages = ntpApiHandle.mostVisited;
   var cmds = [];
-  for (var i = 0; i < Math.min(MAX_NUM_TILES_TO_SHOW, pages.length); ++i) {
+  let maxNumTiles = configData.isCustomLinksEnabled ?
+      MAX_NUM_TILES_CUSTOM_LINKS :
+      MAX_NUM_TILES_MOST_VISITED;
+  for (var i = 0; i < Math.min(maxNumTiles, pages.length); ++i) {
     cmds.push({cmd: 'tile', rid: pages[i].rid});
   }
   cmds.push({cmd: 'show'});
@@ -879,7 +895,6 @@
       }
     }
 
-
     // Set up the fakebox (which only exists on the Google NTP).
     ntpApiHandle.oninputstart = onInputStart;
     ntpApiHandle.oninputcancel = onInputCancel;
@@ -1077,20 +1092,11 @@
 }
 
 
-function loadConfig() {
-  var configScript = document.createElement('script');
-  configScript.type = 'text/javascript';
-  configScript.src = 'chrome-search://local-ntp/config.js';
-  configScript.onload = init;
-  document.head.appendChild(configScript);
-}
-
-
 /**
  * Binds event listeners.
  */
 function listen() {
-  document.addEventListener('DOMContentLoaded', loadConfig);
+  document.addEventListener('DOMContentLoaded', init);
 }
 
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index bd743f89..e26bbc46 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -78,22 +78,6 @@
   THUMBNAIL_FAILED: 8,
 };
 
-
-/**
- * Maximum number of MostVisited tiles to show at any time. If the host page
- * doesn't send enough tiles, we fill them blank.
- * @const {number}
- */
-var MAX_NUM_TILES = 8;
-
-
-/**
- * Maximum number of custom link tiles to show at any time.
- * @const {number}
- */
-var MAX_NUM_CUSTOM_LINKS = 10;
-
-
 /**
  * Number of tiles per row for Material Design.
  * @const {number}
@@ -133,6 +117,16 @@
 
 
 /**
+ * Maximum number of MostVisited tiles to show at any time. If the host page
+ * doesn't send enough tiles and custom links is not enabled, we fill them blank
+ * tiles. This can be changed depending on what feature is enabled. Set by the
+ * host page, while 8 is default.
+ * @type {number}
+ */
+let maxNumTiles = 8;
+
+
+/**
  * List of parameters passed by query args.
  * @type {Object}
  */
@@ -170,7 +164,7 @@
 
 /**
  * Log impression of an NTP tile.
- * @param {number} tileIndex Position of the tile, >= 0 and < MAX_NUM_TILES.
+ * @param {number} tileIndex Position of the tile, >= 0 and < |maxNumTiles|.
  * @param {number} tileTitleSource The source of the tile's title as received
  *                 from getMostVisitedItemData.
  * @param {number} tileSource The tile's source as received from
@@ -187,7 +181,7 @@
 
 /**
  * Log click on an NTP tile.
- * @param {number} tileIndex Position of the tile, >= 0 and < MAX_NUM_TILES.
+ * @param {number} tileIndex Position of the tile, >= 0 and < |maxNumTiles|.
  * @param {number} tileTitleSource The source of the tile's title as received
  *                 from getMostVisitedItemData.
  * @param {number} tileSource The tile's source as received from
@@ -307,8 +301,8 @@
   var cur = tiles;
 
   // Add an "add new custom link" button if we haven't reached the maximum
-  // number of links.
-  if (isCustomLinksEnabled && cur.childNodes.length < MAX_NUM_CUSTOM_LINKS) {
+  // number of tiles.
+  if (isCustomLinksEnabled && cur.childNodes.length < maxNumTiles) {
     let data = {
       'tid': -1,
       'title': 'Add shortcut',  // TODO(851293): Use translated strings.
@@ -318,10 +312,10 @@
     tiles.appendChild(renderMaterialDesignTile(data));
   }
 
-  // Create empty tiles until we have MAX_NUM_TILES. This is not required for
+  // Create empty tiles until we have |maxNumTiles|. This is not required for
   // the Material Design style tiles.
   if (!isMDEnabled) {
-    while (cur.childNodes.length < MAX_NUM_TILES) {
+    while (cur.childNodes.length < maxNumTiles) {
       addTile({});
     }
   }
@@ -373,7 +367,7 @@
 /**
  * Handler for the 'show' message from the host page, called when it wants to
  * add a suggestion tile.
- * It's also used to fill up our tiles to MAX_NUM_TILES if necessary.
+ * It's also used to fill up our tiles to |maxNumTiles| if necessary.
  * @param {object} args Data for the tile to be rendered.
  */
 var addTile = function(args) {
@@ -814,6 +808,11 @@
     isCustomLinksEnabled = true;
   }
 
+  // Set the maximum number of tiles to show.
+  if (isCustomLinksEnabled) {
+    maxNumTiles = 10;
+  }
+
   window.addEventListener('message', handlePostMessage);
 };
 
diff --git a/chrome/browser/resources/optimize_webui.gni b/chrome/browser/resources/optimize_webui.gni
index 70dabca..6dde71e 100644
--- a/chrome/browser/resources/optimize_webui.gni
+++ b/chrome/browser/resources/optimize_webui.gni
@@ -61,6 +61,10 @@
 
     args += [ "--html_in_files" ] + invoker.html_in_files
     args += [ "--html_out_files" ] + invoker.html_out_files
+
+    if (defined(invoker.html_out_files_polymer2)) {
+      args += [ "--html_out_files_polymer2" ] + invoker.html_out_files_polymer2
+    }
     args += [ "--js_out_files" ] + invoker.js_out_files
 
     if (defined(invoker.excludes)) {
diff --git a/chrome/browser/resources/optimize_webui.py b/chrome/browser/resources/optimize_webui.py
index 9ea1987..124f7481 100755
--- a/chrome/browser/resources/optimize_webui.py
+++ b/chrome/browser/resources/optimize_webui.py
@@ -202,17 +202,29 @@
 
     # Run polymer-css-build and write the output HTML files to their final
     # destination.
-    pcb_html_out_paths = [
-        os.path.join(out_path, f) for f in args.html_out_files]
-    node.RunNode([node_modules.PathToPolymerCssBuild()] +
-                 ['--polymer-version', '1'] +
-                 ['--no-inline-includes', '-f'] +
-                 crisper_html_out_paths + ['-o'] + pcb_html_out_paths)
+    # TODO(dpapad): Remove this when Polymer 2 migration has completed.
+    _polymer_css_build(out_path, crisper_html_out_paths, args.html_out_files, 1)
+
+    if args.html_out_files_polymer2:
+      # Run polymer-css-build again with --polymer-version=2. This is
+      # necessary so that the runtime --enable-features=WebUIPolymer2 works
+      # with optimized builds.
+      _polymer_css_build(out_path, crisper_html_out_paths,
+                         args.html_out_files_polymer2, 2)
   finally:
     shutil.rmtree(tmp_out_dir)
   return manifest_out_path
 
 
+def _polymer_css_build(out_path, html_in_paths, html_out_files, version):
+  html_out_paths = [
+      os.path.join(out_path, f) for f in html_out_files]
+  node.RunNode([node_modules.PathToPolymerCssBuild()] +
+               ['--polymer-version', str(version)] +
+               ['--no-inline-includes', '-f'] +
+               html_in_paths + ['-o'] + html_out_paths)
+
+
 def main(argv):
   parser = argparse.ArgumentParser()
   parser.add_argument('--depfile', required=True)
@@ -220,6 +232,7 @@
   parser.add_argument('--host', required=True)
   parser.add_argument('--html_in_files', nargs='*', required=True)
   parser.add_argument('--html_out_files', nargs='*', required=True)
+  parser.add_argument('--html_out_files_polymer2', nargs='*')
   parser.add_argument('--input', required=True)
   parser.add_argument('--insert_in_head')
   parser.add_argument('--js_out_files', nargs='*', required=True)
diff --git a/chrome/browser/resources/optimize_webui_test.py b/chrome/browser/resources/optimize_webui_test.py
index 5a9c271..6ce29d3 100755
--- a/chrome/browser/resources/optimize_webui_test.py
+++ b/chrome/browser/resources/optimize_webui_test.py
@@ -44,17 +44,19 @@
     assert self._out_folder
     return open(os.path.join(self._out_folder, file_name), 'r').read()
 
-  def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file):
+  def _run_optimize(self, depfile, html_in_file, html_out_file,
+                    html_out_file_polymer2, js_out_file):
     # TODO(dbeam): make it possible to _run_optimize twice? Is that useful?
     assert not self._out_folder
     self._out_folder = self._create_tmp_dir()
     optimize_webui.main([
       '--depfile', os.path.join(self._out_folder,'depfile.d'),
-      '--html_in_file', html_in_file,
-      '--html_out_file', html_out_file,
+      '--html_in_files', html_in_file,
+      '--html_out_files', html_out_file,
+      '--html_out_files_polymer2', html_out_file_polymer2,
       '--host', 'fake-host',
       '--input', self._tmp_src_dir,
-      '--js_out_file', js_out_file,
+      '--js_out_files', js_out_file,
       '--out_folder', self._out_folder,
     ])
 
@@ -75,6 +77,7 @@
     self._run_optimize(depfile='depfile.d',
                        html_in_file='ui.html',
                        html_out_file='fast.html',
+                       html_out_file_polymer2='fast.p2.html',
                        js_out_file='fast.js')
 
     fast_html = self._read_out_file('fast.html')
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 29044636..5359806 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/search/instant_service_observer.h"
 #include "chrome/browser/search/local_ntp_source.h"
 #include "chrome/browser/search/most_visited_iframe_source.h"
+#include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/ntp_icon_source.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search/thumbnail_source.h"
@@ -115,8 +116,13 @@
                  content::NotificationService::AllSources());
 
   most_visited_sites_ = ChromeMostVisitedSitesFactory::NewForProfile(profile_);
-  if (most_visited_sites_)
-    most_visited_sites_->SetMostVisitedURLsObserver(this, 8);
+  if (most_visited_sites_) {
+    // 9 tiles are required for the custom links feature in order to balance the
+    // Most Visited rows (this is due to an additional "Add" button). Otherwise,
+    // Most Visited should return the regular 8 tiles.
+    most_visited_sites_->SetMostVisitedURLsObserver(
+        this, features::IsCustomLinksEnabled() ? 9 : 8);
+  }
 
   if (profile_ && profile_->GetResourceContext()) {
     content::BrowserThread::PostTask(
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 021ee14..29c415f 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -56,6 +56,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/browser_thread.h"
+#include "crypto/sha2.h"
 #include "net/base/url_util.h"
 #include "net/url_request/url_request.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -247,43 +248,6 @@
   return translated_strings;
 }
 
-// Returns a JS dictionary of configuration data for the local NTP.
-std::string GetConfigData(bool is_google, const GURL& google_base_url) {
-  base::DictionaryValue config_data;
-  config_data.Set("translatedStrings", GetTranslatedStrings(is_google));
-  config_data.SetBoolean("isGooglePage", is_google);
-  config_data.SetString("googleBaseUrl", google_base_url.spec());
-  config_data.SetBoolean(
-      "isAccessibleBrowser",
-      content::BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser());
-
-  bool is_voice_search_enabled =
-      base::FeatureList::IsEnabled(features::kVoiceSearchOnLocalNtp);
-  config_data.SetBoolean("isVoiceSearchEnabled", is_voice_search_enabled);
-
-  config_data.SetBoolean("isMDUIEnabled", features::IsMDUIEnabled());
-
-  config_data.SetBoolean("isMDIconsEnabled", features::IsMDIconsEnabled());
-
-  if (is_google) {
-    config_data.SetBoolean("isCustomLinksEnabled",
-                           features::IsCustomLinksEnabled());
-    config_data.SetBoolean("isCustomBackgroundsEnabled",
-                           features::IsCustomBackgroundsEnabled());
-  }
-
-  // Serialize the dictionary.
-  std::string js_text;
-  JSONStringValueSerializer serializer(&js_text);
-  serializer.Serialize(config_data);
-
-  std::string config_data_js;
-  config_data_js.append("var configData = ");
-  config_data_js.append(js_text);
-  config_data_js.append(";");
-  return config_data_js;
-}
-
 std::string GetThemeCSS(Profile* profile) {
   SkColor background_color =
       ThemeService::GetThemeProviderForProfile(profile)
@@ -480,29 +444,6 @@
   return false;
 }
 
-std::string GetContentSecurityPolicyScriptSrcIOThread() {
-#if !defined(GOOGLE_CHROME_BUILD)
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kLocalNtpReload)) {
-    // While live-editing the local NTP files, turn off CSP.
-    return "script-src * 'unsafe-inline';";
-  }
-#endif  // !defined(GOOGLE_CHROME_BUILD)
-
-  return base::StringPrintf(
-      "script-src 'strict-dynamic' 'sha256-%s' 'sha256-%s' 'sha256-%s';",
-      LOCAL_NTP_JS_INTEGRITY, VOICE_JS_INTEGRITY,
-      CUSTOM_BACKGROUNDS_JS_INTEGRITY);
-}
-
-std::string GetContentSecurityPolicyChildSrcIOThread() {
-  // Allow embedding of the most visited iframe, as well as the account
-  // switcher and the notifications dropdown from the One Google Bar, and/or
-  // the iframe for interactive Doodles.
-  return base::StringPrintf("child-src %s https://*.google.com/;",
-                            chrome::kChromeSearchMostVisitedUrl);
-}
-
 std::string GetErrorDict(const ErrorInfo& error) {
   base::DictionaryValue error_info;
   error_info.SetBoolean("auth_error",
@@ -521,30 +462,74 @@
 
 }  // namespace
 
-class LocalNtpSource::GoogleSearchProviderTracker
+// Keeps the search engine configuration data to be included on the Local NTP,
+// and will also keep track of any changes in search engine provider to
+// recompute this data.
+class LocalNtpSource::SearchConfigurationProvider
     : public TemplateURLServiceObserver {
  public:
-  explicit GoogleSearchProviderTracker(TemplateURLService* service)
-      : service_(service), is_google_(false) {
+  explicit SearchConfigurationProvider(TemplateURLService* service)
+      : service_(service) {
     DCHECK(service_);
     service_->AddObserver(this);
-    is_google_ = search::DefaultSearchProviderIsGoogle(service_);
-    google_base_url_ = GURL(service_->search_terms_data().GoogleBaseURLValue());
+    UpdateConfigData();
   }
 
-  ~GoogleSearchProviderTracker() override {
+  ~SearchConfigurationProvider() override {
     if (service_)
       service_->RemoveObserver(this);
   }
 
-  bool DefaultSearchProviderIsGoogle() const { return is_google_; }
-
-  const GURL& GetGoogleBaseUrl() const { return google_base_url_; }
+  const std::string& config_data_js() const { return config_data_js_; }
+  const std::string& config_data_integrity() const {
+    return config_data_integrity_;
+  }
 
  private:
+  // Updates the configuration data for the local NTP.
+  void UpdateConfigData() {
+    bool is_google = search::DefaultSearchProviderIsGoogle(service_);
+    const GURL google_base_url =
+        GURL(service_->search_terms_data().GoogleBaseURLValue());
+    base::DictionaryValue config_data;
+    config_data.Set("translatedStrings", GetTranslatedStrings(is_google));
+    config_data.SetBoolean("isGooglePage", is_google);
+    config_data.SetString("googleBaseUrl", google_base_url.spec());
+    config_data.SetBoolean("isAccessibleBrowser",
+                           content::BrowserAccessibilityState::GetInstance()
+                               ->IsAccessibleBrowser());
+
+    bool is_voice_search_enabled =
+        base::FeatureList::IsEnabled(features::kVoiceSearchOnLocalNtp);
+    config_data.SetBoolean("isVoiceSearchEnabled", is_voice_search_enabled);
+
+    config_data.SetBoolean("isMDUIEnabled", features::IsMDUIEnabled());
+
+    config_data.SetBoolean("isMDIconsEnabled", features::IsMDIconsEnabled());
+
+    if (is_google) {
+      config_data.SetBoolean("isCustomLinksEnabled",
+                             features::IsCustomLinksEnabled());
+      config_data.SetBoolean("isCustomBackgroundsEnabled",
+                             features::IsCustomBackgroundsEnabled());
+    }
+
+    // Serialize the dictionary.
+    std::string js_text;
+    JSONStringValueSerializer serializer(&js_text);
+    serializer.Serialize(config_data);
+
+    config_data_js_ = "var configData = ";
+    config_data_js_.append(js_text);
+    config_data_js_.append(";");
+
+    std::string config_sha256 = crypto::SHA256HashString(config_data_js_);
+    base::Base64Encode(config_sha256, &config_data_integrity_);
+  }
+
   void OnTemplateURLServiceChanged() override {
-    is_google_ = search::DefaultSearchProviderIsGoogle(service_);
-    google_base_url_ = GURL(service_->search_terms_data().GoogleBaseURLValue());
+    // The search provider may have changed, keep the config data valid.
+    UpdateConfigData();
   }
 
   void OnTemplateURLServiceShuttingDown() override {
@@ -554,8 +539,8 @@
 
   TemplateURLService* service_;
 
-  bool is_google_;
-  GURL google_base_url_;
+  std::string config_data_js_;
+  std::string config_data_integrity_;
 };
 
 class LocalNtpSource::DesktopLogoObserver {
@@ -697,8 +682,8 @@
   TemplateURLService* template_url_service =
       TemplateURLServiceFactory::GetForProfile(profile_);
   if (template_url_service) {
-    google_tracker_ =
-        std::make_unique<GoogleSearchProviderTracker>(template_url_service);
+    search_config_provider_ =
+        std::make_unique<SearchConfigurationProvider>(template_url_service);
   }
 }
 
@@ -716,9 +701,7 @@
 
   std::string stripped_path = StripParameters(path);
   if (stripped_path == kConfigDataFilename) {
-    std::string config_data_js =
-        GetConfigData(google_tracker_->DefaultSearchProviderIsGoogle(),
-                      google_tracker_->GetGoogleBaseUrl());
+    std::string config_data_js = search_config_provider_->config_data_js();
     callback.Run(base::RefCountedString::TakeString(&config_data_js));
     return;
   }
@@ -851,6 +834,7 @@
     std::string html = ui::ResourceBundle::GetSharedInstance()
                            .GetRawDataResource(IDR_LOCAL_NTP_HTML)
                            .as_string();
+
     std::string local_ntp_integrity =
         base::StringPrintf(kIntegrityFormat, LOCAL_NTP_JS_INTEGRITY);
     base::ReplaceFirstSubstringAfterOffset(&html, 0, "{{LOCAL_NTP_INTEGRITY}}",
@@ -866,6 +850,16 @@
     base::ReplaceFirstSubstringAfterOffset(&html, 0,
                                            "{{LOCAL_NTP_CUSTOM_BG_INTEGRITY}}",
                                            local_ntp_custom_bg_integrity);
+
+    std::string config_data_integrity = base::StringPrintf(
+        kIntegrityFormat,
+        search_config_provider_->config_data_integrity().c_str());
+    base::ReplaceFirstSubstringAfterOffset(
+        &html, 0, "{{CONFIG_DATA_INTEGRITY}}", config_data_integrity);
+
+    base::ReplaceFirstSubstringAfterOffset(
+        &html, 0, "{{CONTENT_SECURITY_POLICY}}", GetContentSecurityPolicy());
+
     callback.Run(base::RefCountedString::TakeString(&html));
     return;
   }
@@ -915,16 +909,42 @@
   return ShouldServiceRequestIOThread(url, resource_context, render_process_id);
 }
 
-std::string LocalNtpSource::GetContentSecurityPolicyScriptSrc() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  return GetContentSecurityPolicyScriptSrcIOThread();
+bool LocalNtpSource::ShouldAddContentSecurityPolicy() const {
+  // The Content Security Policy is served as a meta tag in local NTP html.
+  // We disable the HTTP Header version here to avoid a conflicting policy. See
+  // GetContentSecurityPolicy.
+  return false;
 }
 
-std::string LocalNtpSource::GetContentSecurityPolicyChildSrc() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+std::string LocalNtpSource::GetContentSecurityPolicy() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+#if !defined(GOOGLE_CHROME_BUILD)
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kLocalNtpReload)) {
+    // While live-editing the local NTP files, turn off CSP.
+    return "script-src * 'unsafe-inline';";
+  }
+#endif  // !defined(GOOGLE_CHROME_BUILD)
 
-  return GetContentSecurityPolicyChildSrcIOThread();
+  // Allow embedding of the most visited iframe, as well as the account
+  // switcher and the notifications dropdown from the One Google Bar, and/or
+  // the iframe for interactive Doodles.
+  std::string child_src_csp =
+      base::StringPrintf("child-src %s https://*.google.com/;",
+                         chrome::kChromeSearchMostVisitedUrl);
+
+  // Restrict scripts in the main page to those listed here. However,
+  // 'strict-dynamic' allows those scripts to load dependencies not listed here.
+  std::string script_src_csp = base::StringPrintf(
+      "script-src 'strict-dynamic' 'sha256-%s' 'sha256-%s' 'sha256-%s' "
+      "'sha256-%s';",
+      LOCAL_NTP_JS_INTEGRITY, VOICE_JS_INTEGRITY,
+      CUSTOM_BACKGROUNDS_JS_INTEGRITY,
+      search_config_provider_->config_data_integrity().c_str());
+
+  return GetContentSecurityPolicyObjectSrc() +
+         GetContentSecurityPolicyStyleSrc() + GetContentSecurityPolicyImgSrc() +
+         child_src_csp + script_src_csp;
 }
 
 void LocalNtpSource::OnCollectionInfoAvailable() {
diff --git a/chrome/browser/search/local_ntp_source.h b/chrome/browser/search/local_ntp_source.h
index 11246b5f..27cad62 100644
--- a/chrome/browser/search/local_ntp_source.h
+++ b/chrome/browser/search/local_ntp_source.h
@@ -47,7 +47,7 @@
   explicit LocalNtpSource(Profile* profile);
 
  private:
-  class GoogleSearchProviderTracker;
+  class SearchConfigurationProvider;
   class DesktopLogoObserver;
 
   struct NtpBackgroundRequest {
@@ -85,8 +85,10 @@
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
                             int render_process_id) const override;
-  std::string GetContentSecurityPolicyScriptSrc() const override;
-  std::string GetContentSecurityPolicyChildSrc() const override;
+  bool ShouldAddContentSecurityPolicy() const override;
+
+  // The Content Security Policy for the Local NTP.
+  std::string GetContentSecurityPolicy() const;
 
   // Overridden from NtpBackgroundServiceObserver:
   void OnCollectionInfoAvailable() override;
@@ -123,7 +125,7 @@
   search_provider_logos::LogoService* logo_service_;
   std::unique_ptr<DesktopLogoObserver> logo_observer_;
 
-  std::unique_ptr<GoogleSearchProviderTracker> google_tracker_;
+  std::unique_ptr<SearchConfigurationProvider> search_config_provider_;
 
   base::WeakPtrFactory<LocalNtpSource> weak_ptr_factory_;
 
diff --git a/chrome/browser/sessions/session_restore_stats_collector.cc b/chrome/browser/sessions/session_restore_stats_collector.cc
index cf34d36f6..0dddbcac 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector.cc
@@ -27,8 +27,6 @@
 using content::RenderWidgetHostView;
 using content::Source;
 using content::WebContents;
-using resource_coordinator::TabLoadTracker;
-using LoadingState = resource_coordinator::TabLoadTracker::LoadingState;
 
 // The enumeration values stored in the "SessionRestore.Actions" histogram.
 enum SessionRestoreActionsUma {
@@ -126,16 +124,15 @@
       tick_clock_(new base::DefaultTickClock()),
       reporting_delegate_(std::move(reporting_delegate)) {
   this_retainer_ = this;
-  TabLoadTracker::Get()->AddObserver(this);
 }
 
 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() {
-  TabLoadTracker::Get()->RemoveObserver(this);
 }
 
 void SessionRestoreStatsCollector::TrackTabs(
     const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) {
-  DCHECK(!done_tracking_non_deferred_tabs_);
+  // Anytime new tabs are added, they are immediately "non deferred".
+  done_tracking_non_deferred_tabs_ = false;
 
   // If this is the first call to TrackTabs then start observing events.
   if (tab_loader_stats_.tab_count == 0) {
@@ -171,10 +168,8 @@
 
     TabState* tab_state = RegisterForNotifications(controller);
     // The tab might already be loading if it is active in a visible window.
-    if (TabLoadTracker::Get()->GetLoadingState(tab.contents()) ==
-        LoadingState::LOADING) {
+    if (!controller->NeedsReload())
       MarkTabAsLoading(tab_state);
-    }
   }
 }
 
@@ -214,111 +209,140 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  if (type !=
-      content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES) {
-    return;
-  }
-
-  // This notification is across all tabs in the browser so notifications
-  // will arrive for tabs that the collector is not explicitly tracking.
-
-  // Only process this event if first paint hasn't been seen and this is a
-  // paint of a visible tab.
-  RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr();
-  if (got_first_paint_ || !render_widget_host->GetView() ||
-      !render_widget_host->GetView()->IsShowing()) {
-    return;
-  }
-
-  got_first_paint_ = true;
-  TabState* tab_state = GetTabState(render_widget_host);
-  if (tab_state) {
-    // This is a paint for a tab that is explicitly being tracked so
-    // update the statistics. Otherwise the host is for a tab that's not
-    // being tracked thus some other tab has visibility and has rendered
-    // and there's no point in tracking the time to first paint. This can
-    // happen because the user opened a different tab or restored tabs
-    // to an already existing browser and an existing tab was in the
-    // foreground.
-    base::TimeDelta time_to_paint = tick_clock_->NowTicks() - restore_started_;
-    DCHECK(!done_tracking_non_deferred_tabs_);
-    tab_loader_stats_.foreground_tab_first_paint = time_to_paint;
-  }
-
-  // Once first paint has been observed the entire to-paint tracking
-  // mechanism is no longer needed.
-  registrar_.Remove(
-      this,
-      content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES,
-      content::NotificationService::AllSources());
-
-  // Remove any tabs that have loaded. These were only being kept around
-  // while waiting for a paint event.
-  std::vector<NavigationController*> loaded_tabs;
-  for (auto& map_entry : tabs_tracked_) {
-    TabState& tab_state = map_entry.second;
-    if (tab_state.loading_state == TAB_IS_LOADED)
-      loaded_tabs.push_back(tab_state.controller);
-  }
-  for (auto* tab : loaded_tabs)
-    RemoveTab(tab);
-
-  ReleaseIfDoneTracking();
-}
-
-void SessionRestoreStatsCollector::OnLoadingStateChange(
-    WebContents* contents,
-    LoadingState old_loading_state,
-    LoadingState new_loading_state) {
-  // This notification is across all tabs in the browser so notifications
-  // will arrive for tabs that the collector is not explicitly tracking.
-
-  NavigationController* tab = &contents->GetController();
-  TabState* tab_state = GetTabState(tab);
-  if (!tab_state)
-    return;
-
-  switch (new_loading_state) {
-    case LoadingState::LOADING: {
+  switch (type) {
+    case content::NOTIFICATION_LOAD_START: {
       // This occurs when a tab has started to load. This can be because of
       // the tab loader (only for non-deferred tabs) or because the user clicked
       // on the tab.
+      NavigationController* tab = Source<NavigationController>(source).ptr();
+      TabState* tab_state = GetTabState(tab);
       MarkTabAsLoading(tab_state);
-    } break;
+      break;
+    }
+    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
+      // This happens when a tab has been closed. A tab can be in any state
+      // when this occurs. Simply stop tracking the tab.
+      WebContents* web_contents = Source<WebContents>(source).ptr();
+      NavigationController* tab = &web_contents->GetController();
+      RemoveTab(tab);
+      break;
+    }
+    case content::NOTIFICATION_LOAD_STOP: {
+      // This occurs to loading tabs when they have finished loading. The tab
+      // may or may not already have painted at this point.
 
-    // A tab that transitions here means that loading was aborted or errored
-    // out. Either way, we consider it "loaded" from our point of view.
-    case LoadingState::UNLOADED:
-    // A tab that completes loading successfully will transition to this state.
-    case LoadingState::LOADED: {
-      MarkTabAsLoaded(tab_state);
-    } break;
+      // Update the tab state and any global state as necessary.
+      NavigationController* tab = Source<NavigationController>(source).ptr();
+      TabState* tab_state = GetTabState(tab);
+      DCHECK(tab_state);
+      tab_state->loading_state = TAB_IS_LOADED;
+      DCHECK_LT(0u, loading_tab_count_);
+      --loading_tab_count_;
+      if (!tab_state->is_deferred) {
+        DCHECK_LT(0u, waiting_for_load_tab_count_);
+        --waiting_for_load_tab_count_;
+      }
+
+      if (tab_state->is_deferred) {
+        reporting_delegate_->ReportDeferredTabLoaded();
+      } else {
+        DCHECK(!done_tracking_non_deferred_tabs_);
+        ++tab_loader_stats_.tabs_loaded;
+      }
+
+      // Update statistics for foreground tabs.
+      base::TimeDelta time_to_load = tick_clock_->NowTicks() - restore_started_;
+      if (!got_first_foreground_load_ && IsShowing(tab_state->controller)) {
+        got_first_foreground_load_ = true;
+        DCHECK(!done_tracking_non_deferred_tabs_);
+        tab_loader_stats_.foreground_tab_first_loaded = time_to_load;
+      }
+
+      // Update statistics for all tabs, if this wasn't a deferred tab. This is
+      // done here and not in ReleaseIfDoneTracking because it is possible to
+      // wait for a paint long after all loads have completed.
+      if (!done_tracking_non_deferred_tabs_ && !tab_state->is_deferred)
+        tab_loader_stats_.non_deferred_tabs_loaded = time_to_load;
+
+      // By default tabs transition to being tracked for paint events after the
+      // load event has been seen. However, if the first paint event has already
+      // been seen then this is not necessary and the tab can be removed.
+      if (got_first_paint_)
+        RemoveTab(tab);
+
+      break;
+    }
+    case content::
+        NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES: {
+      // This notification is across all tabs in the browser so notifications
+      // will arrive for tabs that the collector is not explicitly tracking.
+
+      // Only process this event if first paint hasn't been seen and this is a
+      // paint of a visible tab.
+      RenderWidgetHost* render_widget_host =
+          Source<RenderWidgetHost>(source).ptr();
+      if (!got_first_paint_ && render_widget_host->GetView() &&
+          render_widget_host->GetView()->IsShowing()) {
+        got_first_paint_ = true;
+        TabState* tab_state = GetTabState(render_widget_host);
+        if (tab_state) {
+          // This is a paint for a tab that is explicitly being tracked so
+          // update the statistics. Otherwise the host is for a tab that's not
+          // being tracked thus some other tab has visibility and has rendered
+          // and there's no point in tracking the time to first paint. This can
+          // happen because the user opened a different tab or restored tabs
+          // to an already existing browser and an existing tab was in the
+          // foreground.
+          base::TimeDelta time_to_paint =
+              tick_clock_->NowTicks() - restore_started_;
+          DCHECK(!done_tracking_non_deferred_tabs_);
+          tab_loader_stats_.foreground_tab_first_paint = time_to_paint;
+        }
+
+        // Once first paint has been observed the entire to-paint tracking
+        // mechanism is no longer needed.
+        registrar_.Remove(
+            this,
+            content::
+                NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES,
+            content::NotificationService::AllSources());
+
+        // Remove any tabs that have loaded. These were only being kept around
+        // while waiting for a paint event.
+        std::vector<NavigationController*> loaded_tabs;
+        for (auto& map_entry : tabs_tracked_) {
+          TabState& tab_state = map_entry.second;
+          if (tab_state.loading_state == TAB_IS_LOADED)
+            loaded_tabs.push_back(tab_state.controller);
+        }
+        for (auto* tab : loaded_tabs)
+          RemoveTab(tab);
+      }
+      break;
+    }
+    default:
+      NOTREACHED() << "Unknown notification received:" << type;
+      break;
   }
 
   ReleaseIfDoneTracking();
 }
 
-void SessionRestoreStatsCollector::OnStopTracking(WebContents* contents,
-                                                  LoadingState loading_state) {
-  // This notification is across all tabs in the browser so notifications
-  // will arrive for tabs that the collector is not explicitly tracking.
-
-  // This happens when a tab has been closed. A tab can be in any state
-  // when this occurs. Simply stop tracking the tab.
-  NavigationController* tab = &contents->GetController();
-  RemoveTab(tab);
-  ReleaseIfDoneTracking();
-}
-
 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) {
-  // This can be called for any tab in the browser, not just those that are
-  // being tracked.
+  // Stop observing this tab.
+  registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+                    Source<WebContents>(tab->GetWebContents()));
+  registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP,
+                    Source<NavigationController>(tab));
+  registrar_.Remove(this, content::NOTIFICATION_LOAD_START,
+                    Source<NavigationController>(tab));
+
   auto tab_it = tabs_tracked_.find(tab);
-  if (tab_it == tabs_tracked_.end())
-    return;
+  DCHECK(tab_it != tabs_tracked_.end());
   TabState& tab_state = tab_it->second;
 
-  // If this tab was in the process of loading then update the loading counts.
+  // If this tab was waiting for a NOTIFICATION_LOAD_STOP event then update
+  // the loading counts.
   if (tab_state.loading_state == TAB_IS_LOADING) {
     DCHECK_LT(0u, loading_tab_count_);
     --loading_tab_count_;
@@ -327,7 +351,7 @@
   // Only non-deferred not-loading/not-loaded tabs are waiting to be loaded.
   if (tab_state.loading_state != TAB_IS_LOADED && !tab_state.is_deferred) {
     DCHECK_LT(0u, waiting_for_load_tab_count_);
-    // It's possible for |waiting_for_load_tab_count_| to reach zero here. This
+    // It's possible for waiting_for_load_tab_count_ to reach zero here. This
     // function is only called from 'Observe', so the transition will be
     // noticed there.
     --waiting_for_load_tab_count_;
@@ -341,7 +365,7 @@
 
   // It is possible for all restored contents to be destroyed or forcibly
   // renavigated before a first paint has arrived. This can be detected by
-  // |tabs_tracked_| containing only deferred tabs. At this point the paint
+  // tabs_tracked_ containing only deferred tabs. At this point the paint
   // mechanism can be disabled and stats collection will stop.
   if (tabs_tracked_.size() == deferred_tab_count_ && !got_first_paint_) {
     got_first_paint_ = true;
@@ -350,17 +374,17 @@
         content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES,
         content::NotificationService::AllSources());
   }
-
-  // Ditto for first load. In this case it no longer needs to be tracked.
-  if (tabs_tracked_.size() == deferred_tab_count_ &&
-      !got_first_foreground_load_) {
-    got_first_foreground_load_ = true;
-  }
 }
 
 SessionRestoreStatsCollector::TabState*
 SessionRestoreStatsCollector::RegisterForNotifications(
     NavigationController* tab) {
+  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+                 Source<WebContents>(tab->GetWebContents()));
+  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
+                 Source<NavigationController>(tab));
+  registrar_.Add(this, content::NOTIFICATION_LOAD_START,
+                 Source<NavigationController>(tab));
   auto result = tabs_tracked_.insert(std::make_pair(tab, TabState(tab)));
   DCHECK(result.second);
   TabState* tab_state = &result.first->second;
@@ -408,50 +432,11 @@
     ++tab_loader_stats_.tabs_load_started;
 }
 
-void SessionRestoreStatsCollector::MarkTabAsLoaded(TabState* tab_state) {
-  // Note that the tab may or may not already have painted at this point.
-  tab_state->loading_state = TAB_IS_LOADED;
-  DCHECK_LT(0u, loading_tab_count_);
-  --loading_tab_count_;
-  if (!tab_state->is_deferred) {
-    DCHECK_LT(0u, waiting_for_load_tab_count_);
-    --waiting_for_load_tab_count_;
-  }
-
-  if (tab_state->is_deferred) {
-    reporting_delegate_->ReportDeferredTabLoaded();
-  } else {
-    DCHECK(!done_tracking_non_deferred_tabs_);
-    ++tab_loader_stats_.tabs_loaded;
-  }
-
-  // Update statistics for foreground tabs.
-  base::TimeDelta time_to_load = tick_clock_->NowTicks() - restore_started_;
-  if (!got_first_foreground_load_ && IsShowing(tab_state->controller)) {
-    got_first_foreground_load_ = true;
-    DCHECK(!done_tracking_non_deferred_tabs_);
-    tab_loader_stats_.foreground_tab_first_loaded = time_to_load;
-  }
-
-  // Update statistics for all tabs, if this wasn't a deferred tab. This is
-  // done here and not in ReleaseIfDoneTracking because it is possible to
-  // wait for a paint long after all loads have completed.
-  if (!done_tracking_non_deferred_tabs_ && !tab_state->is_deferred)
-    tab_loader_stats_.non_deferred_tabs_loaded = time_to_load;
-
-  // By default tabs transition to being tracked for paint events after the
-  // load event has been seen. However, if the first paint event has already
-  // been seen then this is not necessary and the tab can be removed.
-  if (got_first_paint_)
-    RemoveTab(tab_state->controller);
-}
-
 void SessionRestoreStatsCollector::ReleaseIfDoneTracking() {
   // If non-deferred tabs are no longer being tracked then report tab loader
   // statistics.
   if (!done_tracking_non_deferred_tabs_ && got_first_paint_ &&
       waiting_for_load_tab_count_ == 0) {
-    DCHECK(got_first_foreground_load_);
     done_tracking_non_deferred_tabs_ = true;
     reporting_delegate_->ReportTabLoaderStats(tab_loader_stats_);
   }
diff --git a/chrome/browser/sessions/session_restore_stats_collector.h b/chrome/browser/sessions/session_restore_stats_collector.h
index 8bf4940a..555315d 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.h
+++ b/chrome/browser/sessions/session_restore_stats_collector.h
@@ -12,7 +12,6 @@
 #include "base/callback_list.h"
 #include "base/macros.h"
 #include "base/time/tick_clock.h"
-#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/sessions/session_restore_delegate.h"
 #include "content/public/browser/notification_observer.h"
@@ -48,9 +47,8 @@
 // presence of an unavailable network, or when tabs are closed during loading.
 // Rethink the collection in these cases.
 class SessionRestoreStatsCollector
-    : public base::RefCounted<SessionRestoreStatsCollector>,
-      public content::NotificationObserver,
-      public resource_coordinator::TabLoadTracker::Observer {
+    : public content::NotificationObserver,
+      public base::RefCounted<SessionRestoreStatsCollector> {
  public:
   // Houses all of the statistics gathered by the SessionRestoreStatsCollector
   // while the underlying TabLoader is active. These statistics are all reported
@@ -162,24 +160,19 @@
 
   ~SessionRestoreStatsCollector() override;
 
-  // NotificationObserver method. Used for detecting first paint.
+  // NotificationObserver method. This is the workhorse of the class and drives
+  // all state transitions.
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // resource_coordinator::TabLoadTracker::Observer implementation:
-  void OnLoadingStateChange(content::WebContents* contents,
-                            LoadingState old_loading_state,
-                            LoadingState new_loading_state) override;
-  void OnStopTracking(content::WebContents* contents,
-                      LoadingState loading_state) override;
-
-  // Called when a tab is no longer tracked. Takes care of unregistering all
-  // observers and removing the tab from all internal data structures.
+  // Called when a tab is no longer tracked. This is called by the 'Observe'
+  // notification callback. Takes care of unregistering all observers and
+  // removing the tab from all internal data structures.
   void RemoveTab(content::NavigationController* tab);
 
   // Registers for relevant notifications for a tab and inserts the tab into
-  // the |tabs_tracked_| map. Return a pointer to the newly created TabState.
+  // to tabs_tracked_ map. Return a pointer to the newly created TabState.
   TabState* RegisterForNotifications(content::NavigationController* tab);
 
   // Returns the tab state, nullptr if not found.
@@ -189,9 +182,6 @@
   // Marks a tab as loading.
   void MarkTabAsLoading(TabState* tab_state);
 
-  // Marks a tab as loaded.
-  void MarkTabAsLoaded(TabState* tab_state);
-
   // Checks to see if the SessionRestoreStatsCollector has finished collecting,
   // and if so, releases the self reference to the shared pointer.
   void ReleaseIfDoneTracking();
diff --git a/chrome/browser/sessions/session_restore_stats_collector_unittest.cc b/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
index 185acd1..a57ec4b4 100644
--- a/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector_unittest.cc
@@ -10,7 +10,6 @@
 
 #include "base/macros.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -26,10 +25,6 @@
 
 namespace {
 
-using resource_coordinator::TabLoadTracker;
-using resource_coordinator::ResourceCoordinatorTabHelper;
-
-using LoadingState = resource_coordinator::TabLoadTracker::LoadingState;
 using TabLoaderStats = SessionRestoreStatsCollector::TabLoaderStats;
 using StatsReportingDelegate =
     SessionRestoreStatsCollector::StatsReportingDelegate;
@@ -283,32 +278,36 @@
     // Create a last active time in the past.
     content::WebContentsTester::For(contents)->SetLastActiveTime(
         base::TimeTicks::Now() - base::TimeDelta::FromMinutes(1));
-    // TabLoadTracker needs the resource_coordinator WebContentsData to be
-    // initialized.
-    ResourceCoordinatorTabHelper::CreateForWebContents(contents);
     restored_tabs_.push_back(RestoredTab(contents, is_active, false, false));
     if (is_active)
       Show(restored_tabs_.size() - 1);
   }
 
+  // Helper function for various notification generation.
+  void GenerateControllerNotification(size_t tab_index, int type) {
+    content::WebContents* contents = restored_tabs_[tab_index].contents();
+    content::NavigationController* controller = &contents->GetController();
+    stats_collector_->Observe(
+        type, content::Source<content::NavigationController>(controller),
+        content::NotificationService::NoDetails());
+  }
+
   // Generates a load start notification for the given tab.
   void GenerateLoadStart(size_t tab_index) {
-    content::WebContents* contents = restored_tabs_[tab_index].contents();
-    TabLoadTracker::Get()->TransitionStateForTesting(contents,
-                                                     LoadingState::LOADING);
+    GenerateControllerNotification(tab_index, content::NOTIFICATION_LOAD_START);
   }
 
   // Generates a load stop notification for the given tab.
   void GenerateLoadStop(size_t tab_index) {
-    content::WebContents* contents = restored_tabs_[tab_index].contents();
-    TabLoadTracker::Get()->TransitionStateForTesting(contents,
-                                                     LoadingState::LOADED);
+    GenerateControllerNotification(tab_index, content::NOTIFICATION_LOAD_STOP);
   }
 
   // Generates a web contents destroyed notification for the given tab.
   void GenerateWebContentsDestroyed(size_t tab_index) {
     content::WebContents* contents = restored_tabs_[tab_index].contents();
-    test_web_contents_factory_->DestroyWebContents(contents);
+    stats_collector_->Observe(content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+                              content::Source<content::WebContents>(contents),
+                              content::NotificationService::NoDetails());
   }
 
   // Generates a paint notification for the given tab.
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 35e292b..4fda1352 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -685,7 +685,7 @@
   receiver.WaitForActivationDecision();
   receiver.ExpectReceivedOnce(ActivationState(ActivationLevel::DISABLED));
   RulesetVerificationStatus dealer_status = GetRulesetVerification();
-  EXPECT_EQ(RulesetVerificationStatus::CORRUPT, dealer_status);
+  EXPECT_EQ(RulesetVerificationStatus::kCorrupt, dealer_status);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, LazyRulesetValidation) {
@@ -695,7 +695,7 @@
   feature_list.InitAndDisableFeature(subresource_filter::kAdTagging);
   SetRulesetToDisallowURLsWithPathSuffix("included_script.js");
   RulesetVerificationStatus dealer_status = GetRulesetVerification();
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED, dealer_status);
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified, dealer_status);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
@@ -707,7 +707,7 @@
 
   SetRulesetToDisallowURLsWithPathSuffix("included_script.js");
   RulesetVerificationStatus dealer_status = GetRulesetVerification();
-  EXPECT_EQ(RulesetVerificationStatus::INTACT, dealer_status);
+  EXPECT_EQ(RulesetVerificationStatus::kIntact, dealer_status);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
diff --git a/chrome/browser/translate/translate_browsertest.cc b/chrome/browser/translate/translate_browsertest.cc
deleted file mode 100644
index 9a1af87..0000000
--- a/chrome/browser/translate/translate_browsertest.cc
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/ui_base_features.h"
-
-// This entire test suite relies on the translate infobar which has been removed
-// from Aura. The file should be ported to use the bubble.
-#if !defined(USE_AURA) && !BUILDFLAG(MAC_VIEWS_BROWSER)
-
-#include <stddef.h>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/infobars/infobar_observer.h"
-#include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/translate/translate_service.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/test_switches.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/infobars/core/infobar.h"
-#include "components/translate/core/browser/translate_infobar_delegate.h"
-#include "components/translate/core/browser/translate_manager.h"
-#include "components/translate/core/browser/translate_script.h"
-#include "components/translate/core/common/translate_switches.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/http/http_status_code.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-namespace {
-
-const base::FilePath::CharType kTranslateRoot[] =
-    FILE_PATH_LITERAL("chrome/test/data/translate");
-const char kFrenchTestPath[] = "/fr_test.html";
-const char kBasicFrenchTestPath[] = "/basic_fr_test.html";
-const char kRefreshMetaTagTestPath[] = "/refresh_meta_tag.html";
-const char kRefreshMetaTagLongDelayTestPath[] = "/refresh_meta_tag_long.html";
-const char kRefreshMetaTagCaseInsensitiveTestPath[] =
-    "/refresh_meta_tag_casei.html";
-const char kRefreshMetaTagAtOnloadTestPath[] =
-    "/refresh_meta_tag_at_onload.html";
-const char kUpdateLocationTestPath[] = "/update_location.html";
-const char kUpdateLocationAtOnloadTestPath[] =
-    "/update_location_at_onload.html";
-const char kMainScriptPath[] = "/pseudo_main.js";
-const char kElementMainScriptPath[] = "/pseudo_element_main.js";
-
-};  // namespace
-
-// Basic translate browser test with an embedded non-secure test server.
-class TranslateBaseBrowserTest : public InProcessBrowserTest {
- public:
-  TranslateBaseBrowserTest() {}
-
-  void SetUp() override {
-    // --secondary-ui-md forces the bubble to be enabled, so explicitly
-    // disable it since this test deals with the infobar.
-    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
-    set_open_about_blank_on_browser_launch(false);
-    translate::TranslateManager::SetIgnoreMissingKeyForTesting(true);
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    net::EmbeddedTestServer* test_server = embedded_test_server();
-    test_server->ServeFilesFromSourceDirectory(kTranslateRoot);
-    ASSERT_TRUE(test_server->Start());
-  }
-
- protected:
-  GURL GetNonSecureURL(const std::string& path) const {
-    return embedded_test_server()->GetURL(path);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  DISALLOW_COPY_AND_ASSIGN(TranslateBaseBrowserTest);
-};
-
-// Translate browser test for loading a page and checking infobar UI.
-class TranslateBrowserTest : public TranslateBaseBrowserTest {
- public:
-  TranslateBrowserTest() : infobar_service_(NULL) {}
-
- protected:
-  translate::TranslateInfoBarDelegate* GetExistingTranslateInfoBarDelegate() {
-    if (!infobar_service_) {
-      content::WebContents* web_contents =
-          browser()->tab_strip_model()->GetActiveWebContents();
-      if (web_contents)
-        infobar_service_ = InfoBarService::FromWebContents(web_contents);
-    }
-    if (!infobar_service_) {
-      ADD_FAILURE() << "infobar service is not available";
-      return NULL;
-    }
-
-    translate::TranslateInfoBarDelegate* delegate = NULL;
-    for (size_t i = 0; i < infobar_service_->infobar_count(); ++i) {
-      // Check if the shown infobar is a confirm infobar coming from the
-      // |kTranslateSecurityOrigin| flag specified in SetUpCommandLine().
-      // This infobar appears in all tests of TranslateBrowserTest and can be
-      // ignored here.
-      if (infobar_service_->infobar_at(i)->delegate()->
-          AsConfirmInfoBarDelegate()) {
-        continue;
-      }
-
-      translate::TranslateInfoBarDelegate* translate =
-          infobar_service_->infobar_at(i)->delegate()->
-          AsTranslateInfoBarDelegate();
-      if (translate) {
-        EXPECT_FALSE(delegate) << "multiple infobars are shown unexpectedly";
-        delegate = translate;
-        continue;
-      }
-
-      // Other infobar should not be shown.
-      EXPECT_TRUE(delegate);
-    }
-    return delegate;
-  }
-
-  void LoadPageWithInfobar(const std::string& path) {
-    // Setup infobar observer.
-    InfoBarObserver observer(infobar_service_,
-                             InfoBarObserver::Type::kInfoBarAdded);
-
-    // Visit non-secure page which is going to be translated.
-    ui_test_utils::NavigateToURL(browser(), GetNonSecureURL(path));
-
-    // Wait for Chrome Translate infobar.
-    observer.Wait();
-  }
-
-  void LoadPageWithoutInfobar(const std::string& path) {
-    // If infobar won't be triggered, we need to use another observer.
-    // Setup page title observer.
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    ASSERT_TRUE(web_contents);
-    content::TitleWatcher watcher(web_contents, base::ASCIIToUTF16("PASS"));
-    watcher.AlsoWaitForTitle(base::ASCIIToUTF16("FAIL"));
-
-    // Visit non-secure page which is going to be translated.
-    ui_test_utils::NavigateToURL(browser(), GetNonSecureURL(path));
-
-    // Wait for the page title is changed after the test finished.
-    const base::string16 result = watcher.WaitAndGetTitle();
-    EXPECT_EQ("PASS", base::UTF16ToASCII(result));
-  }
-
-  void CheckForTranslateUI(const std::string& path, bool should_trigger_UI) {
-    ASSERT_FALSE(TranslateService::IsTranslateBubbleEnabled());
-    // Check if there is no Translate infobar.
-    translate::TranslateInfoBarDelegate* delegate =
-        GetExistingTranslateInfoBarDelegate();
-    EXPECT_FALSE(delegate);
-
-    if (should_trigger_UI) {
-      ASSERT_NO_FATAL_FAILURE(LoadPageWithInfobar(path));
-    } else {
-      ASSERT_NO_FATAL_FAILURE(LoadPageWithoutInfobar(path));
-    }
-
-    // Check whether the translate infobar showed up.
-    delegate = GetExistingTranslateInfoBarDelegate();
-    if (should_trigger_UI) {
-      // Check if there is a translate infobar.
-      EXPECT_TRUE(delegate);
-    } else {
-      // Check if there is no translate infobar.
-      EXPECT_FALSE(delegate);
-    }
-  }
-
-  void Translate() {
-    translate::TranslateInfoBarDelegate* delegate =
-        GetExistingTranslateInfoBarDelegate();
-    ASSERT_TRUE(delegate);
-    delegate->Translate();
-  }
-
- private:
-  InfoBarService* infobar_service_;
-};
-
-// Translate browser test with a seperate secure server setup.
-class TranslateWithSecureServerBrowserTest : public TranslateBrowserTest {
- public:
-  TranslateWithSecureServerBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    https_server_.ServeFilesFromSourceDirectory(kTranslateRoot);
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    ASSERT_TRUE(https_server_.Start());
-    // Setup alternate security origin for testing in order to allow XHR against
-    // local test server. Note that this flag shows a confirm infobar in tests.
-    GURL base_url = GetSecureURL("/");
-    command_line->AppendSwitchASCII(
-        translate::switches::kTranslateSecurityOrigin,
-        base_url.GetOrigin().spec());
-    TranslateBrowserTest::SetUpCommandLine(command_line);
-  }
-
- protected:
-  GURL GetSecureURL(const std::string& path) const {
-    return https_server_.GetURL(path);
-  }
-
- private:
-  net::EmbeddedTestServer https_server_;
-
-  DISALLOW_COPY_AND_ASSIGN(TranslateWithSecureServerBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(TranslateWithSecureServerBrowserTest,
-                       TranslateInIsolatedWorld) {
-  net::TestURLFetcherFactory factory;
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(kFrenchTestPath, true));
-
-  // Setup page title observer.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(web_contents);
-  content::TitleWatcher watcher(web_contents, base::ASCIIToUTF16("PASS"));
-  watcher.AlsoWaitForTitle(base::ASCIIToUTF16("FAIL"));
-
-  // Perform translate.
-  ASSERT_NO_FATAL_FAILURE(Translate());
-
-  // Hook URLFetcher for element.js.
-  GURL script1_url = GetSecureURL(kMainScriptPath);
-  GURL script2_url = GetSecureURL(kElementMainScriptPath);
-  std::string element_js = "main_script_url = '" + script1_url.spec() + "';\n";
-  element_js += "element_main_script_url = '" + script2_url.spec() + "';\n";
-  element_js +=
-    "google = { 'translate' : { 'TranslateService' : function() { return {\n"
-    "  isAvailable: function() {\n"
-    "    cr.googleTranslate.onLoadJavascript(main_script_url);\n"
-    "    return true;\n"
-    "  },\n"
-    "  translatePage: function(sl, tl, cb) {\n"
-    "    cb(1, true);\n"
-    "  }\n"
-    "} } } };\n"
-    "cr.googleTranslate.onTranslateElementLoad();\n";
-  net::TestURLFetcher* fetcher =
-      factory.GetFetcherByID(translate::TranslateScript::kFetcherId);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_url(fetcher->GetOriginalURL());
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(element_js);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-
-  // Wait for the page title is changed after the test finished.
-  const base::string16 result = watcher.WaitAndGetTitle();
-  EXPECT_EQ("PASS", base::UTF16ToASCII(result));
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, BasicTranslation) {
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(kBasicFrenchTestPath, true));
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, IgnoreRefreshMetaTag) {
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(
-      kRefreshMetaTagTestPath, false));
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslateRefreshMetaTagLongDelay) {
-  ASSERT_NO_FATAL_FAILURE(
-      CheckForTranslateUI(kRefreshMetaTagLongDelayTestPath, true));
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest,
-                       IgnoreRefreshMetaTagInCaseInsensitive) {
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(
-      kRefreshMetaTagCaseInsensitiveTestPath, false));
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, IgnoreRefreshMetaTagAtOnload) {
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(
-      kRefreshMetaTagAtOnloadTestPath, false));
-}
-
-// TODO(toyoshim, creis): The infobar should be dismissed on client redirects.
-// See https://crbug.com/781879.
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, DISABLED_UpdateLocation) {
-  ASSERT_NO_FATAL_FAILURE(CheckForTranslateUI(kUpdateLocationTestPath, false));
-}
-
-// TODO(toyoshim, creis): The infobar should be dismissed on client redirects.
-// See https://crbug.com/781879.
-IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, DISABLED_UpdateLocationAtOnload) {
-  ASSERT_NO_FATAL_FAILURE(
-      CheckForTranslateUI(kUpdateLocationAtOnloadTestPath, false));
-}
-#endif  // !defined(USE_AURA)
diff --git a/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc b/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
index d58e48f5..88d1944 100644
--- a/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
+++ b/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
@@ -8,26 +8,14 @@
 #include "ash/components/shortcut_viewer/views/keyboard_shortcut_view.h"
 #include "ash/public/cpp/ash_features.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace keyboard_shortcut_viewer_util {
-namespace {
-
-// Keyboard shortcut viewer app is incompatible with some a11y features.
-bool IsUsingA11yIncompatibleWithApp() {
-  chromeos::AccessibilityManager* a11y = chromeos::AccessibilityManager::Get();
-  return a11y->IsFocusHighlightEnabled() || a11y->IsSelectToSpeakEnabled() ||
-         a11y->IsSwitchAccessEnabled();
-}
-
-}  // namespace
 
 void ShowKeyboardShortcutViewer() {
   base::TimeTicks user_gesture_time = base::TimeTicks::Now();
-  if (ash::features::IsKeyboardShortcutViewerAppEnabled() &&
-      !IsUsingA11yIncompatibleWithApp()) {
+  if (ash::features::IsKeyboardShortcutViewerAppEnabled()) {
     shortcut_viewer::mojom::ShortcutViewerPtr shortcut_viewer_ptr;
     service_manager::Connector* connector =
         content::ServiceManagerConnection::GetForProcess()->GetConnector();
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index 644fb5b..617acbf 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -255,10 +255,26 @@
   // Convert point to local coordinates of the hit window.
   aura::Window::ConvertPointToTarget(root_window, window, &action.target_point);
 
+  // Check for a AX node tree in a remote process (e.g. renderer, mojo app).
+  ui::AXTreeIDRegistry::AXTreeID child_ax_tree_id;
+  if (ash::Shell::HasRemoteClient(window)) {
+    // For remote mojo apps, the |window| is a DesktopNativeWidgetAura, so the
+    // parent is the widget and the widget's contents view has the child tree.
+    CHECK(window->parent());
+    views::Widget* widget =
+        views::Widget::GetWidgetForNativeWindow(window->parent());
+    CHECK(widget);
+    ui::AXNodeData node_data;
+    widget->GetContentsView()->GetAccessibleNodeData(&node_data);
+    child_ax_tree_id =
+        node_data.GetIntAttribute(ax::mojom::IntAttribute::kChildTreeId);
+  } else {
+    // For normal windows the (optional) child tree is an aura window property.
+    child_ax_tree_id = window->GetProperty(ui::kChildAXTreeID);
+  }
+
   // If the window has a child AX tree ID, forward the action to the
   // associated AXHostDelegate or RenderFrameHost.
-  ui::AXTreeIDRegistry::AXTreeID child_ax_tree_id =
-      window->GetProperty(ui::kChildAXTreeID);
   if (child_ax_tree_id != ui::AXTreeIDRegistry::kNoAXTreeID) {
     ui::AXTreeIDRegistry* registry = ui::AXTreeIDRegistry::GetInstance();
     ui::AXHostDelegate* delegate = registry->GetHostDelegate(child_ax_tree_id);
diff --git a/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm b/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm
index b4a45fa..40292a9c 100644
--- a/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm
+++ b/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm
@@ -7,10 +7,18 @@
 #include "chrome/browser/ui/cocoa/download/download_util_mac.h"
 #include "components/download/public/common/download_item.h"
 #include "ui/gfx/image/image.h"
+#include "ui/views/widget/widget.h"
 
 void DragDownloadItem(const download::DownloadItem* download,
                       gfx::Image* icon,
                       gfx::NativeView view) {
+  // If this drag was initiated from a views::Widget, that widget may have
+  // mouse capture. Drags via View::DoDrag() usually release it. The code below
+  // bypasses that, so release manually. See https://crbug.com/863377.
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(view);
+  if (widget)
+    widget->ReleaseCapture();
+
   DCHECK_EQ(download::DownloadItem::COMPLETE, download->GetState());
   NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
   download_util::AddFileToPasteboard(pasteboard, download->GetTargetFilePath());
diff --git a/chrome/browser/ui/collected_cookies_browsertest.cc b/chrome/browser/ui/collected_cookies_browsertest.cc
index 66e801f..5e2ce059 100644
--- a/chrome/browser/ui/collected_cookies_browsertest.cc
+++ b/chrome/browser/ui/collected_cookies_browsertest.cc
@@ -5,7 +5,6 @@
 #include <string>
 
 #include "base/command_line.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -17,7 +16,6 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/base/ui_base_features.h"
 
 class CollectedCookiesTest : public DialogBrowserTest {
  public:
@@ -44,26 +42,8 @@
   DISALLOW_COPY_AND_ASSIGN(CollectedCookiesTest);
 };
 
-// Runs with --secondary-ui-md. Users of this can switch to CollectedCookiesTest
-// when that is the default.
-class CollectedCookiesTestMd : public CollectedCookiesTest {
- public:
-  CollectedCookiesTestMd() {}
-
-  // CollectedCookiesTest:
-  void SetUp() override {
-    UseMdOnly();
-    CollectedCookiesTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(CollectedCookiesTestMd);
-};
-
 // Test that calls ShowUi("default").
-IN_PROC_BROWSER_TEST_F(CollectedCookiesTestMd, InvokeUi_default) {
+IN_PROC_BROWSER_TEST_F(CollectedCookiesTest, InvokeUi_default) {
   ShowAndVerifyUi();
 }
 
diff --git a/chrome/browser/ui/test/test_browser_dialog.cc b/chrome/browser/ui/test/test_browser_dialog.cc
index 9cc73d48..e7a02b1 100644
--- a/chrome/browser/ui/test/test_browser_dialog.cc
+++ b/chrome/browser/ui/test/test_browser_dialog.cc
@@ -59,18 +59,6 @@
 TestBrowserDialog::~TestBrowserDialog() = default;
 
 void TestBrowserDialog::PreShow() {
-// The rest of this class assumes the child dialog is toolkit-views. So, for
-// Mac, it will only work when MD for secondary UI is enabled. Without this, a
-// Cocoa dialog will be created, which TestBrowserDialog doesn't support.
-// Force kSecondaryUiMd on Mac to get coverage on the bots. Leave it optional
-// elsewhere so that the non-MD dialog can be invoked to compare.
-#if defined(OS_MACOSX)
-  // Note that since SetUp() has already been called, some parts of the toolkit
-  // may already be initialized without MD - this is just to ensure Cocoa
-  // dialogs are not selected.
-  UseMdOnly();
-#endif
-
   UpdateWidgets();
 }
 
diff --git a/chrome/browser/ui/test/test_browser_ui.cc b/chrome/browser/ui/test/test_browser_ui.cc
index 44ee016..40bf6ea5 100644
--- a/chrome/browser/ui/test/test_browser_ui.cc
+++ b/chrome/browser/ui/test/test_browser_ui.cc
@@ -6,11 +6,8 @@
 
 #include "base/command_line.h"
 #include "base/test/gtest_util.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/test_switches.h"
 #include "build/build_config.h"
-#include "chrome/common/chrome_features.h"
-#include "ui/base/ui_base_features.h"
 
 namespace {
 
@@ -39,17 +36,3 @@
   else
     DismissUi();
 }
-
-void TestBrowserUi::UseMdOnly() {
-  if (enable_md_)
-    return;
-
-  enable_md_ = std::make_unique<base::test::ScopedFeatureList>();
-  enable_md_->InitWithFeatures(
-#if defined(OS_MACOSX)
-      {features::kSecondaryUiMd, features::kShowAllDialogsWithViewsToolkit},
-#else
-      {features::kSecondaryUiMd},
-#endif
-      {});
-}
diff --git a/chrome/browser/ui/test/test_browser_ui.h b/chrome/browser/ui/test/test_browser_ui.h
index e414780..7da77cc 100644
--- a/chrome/browser/ui/test/test_browser_ui.h
+++ b/chrome/browser/ui/test/test_browser_ui.h
@@ -11,12 +11,6 @@
 #include "base/macros.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
-namespace base {
-namespace test {
-class ScopedFeatureList;
-}  // namespace test
-}  // namespace base
-
 // TestBrowserUi provides a way to register an InProcessBrowserTest testing
 // harness with a framework that invokes Chrome browser UI in a consistent way.
 // It optionally provides a way to invoke UI "interactively". This allows
@@ -101,14 +95,7 @@
   // with no other code.
   void ShowAndVerifyUi();
 
-  // Convenience method to force-enable features::kSecondaryUiMd for this test
-  // on all platforms. This should be called in an override of SetUp().
-  void UseMdOnly();
-
  private:
-  // If non-null, forces secondary UI to MD.
-  std::unique_ptr<base::test::ScopedFeatureList> enable_md_;
-
   DISALLOW_COPY_AND_ASSIGN(TestBrowserUi);
 };
 
diff --git a/chrome/browser/ui/update_chrome_dialog_browsertest.cc b/chrome/browser/ui/update_chrome_dialog_browsertest.cc
index 29db4592..ff5573b 100644
--- a/chrome/browser/ui/update_chrome_dialog_browsertest.cc
+++ b/chrome/browser/ui/update_chrome_dialog_browsertest.cc
@@ -10,12 +10,6 @@
  public:
   UpdateRecommendedDialogTest() {}
 
-  // DialogBrowserTest:
-  void SetUp() override {
-    UseMdOnly();
-    DialogBrowserTest::SetUp();
-  }
-
   void ShowUi(const std::string& name) override {
     InProcessBrowserTest::browser()->window()->ShowUpdateChromeDialog();
   }
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc
index f1121db..5684ee0 100644
--- a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc
@@ -48,9 +48,6 @@
   ExtensionMessageBubbleViewBrowserTest() {}
   ~ExtensionMessageBubbleViewBrowserTest() override {}
 
-  // ExtensionMessageBubbleBrowserTest:
-  void SetUp() override;
-
   // TestBrowserDialog:
   void ShowUi(const std::string& name) override;
 
@@ -71,14 +68,6 @@
   DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleViewBrowserTest);
 };
 
-void ExtensionMessageBubbleViewBrowserTest::SetUp() {
-  // MD is required on Mac to get a Views bubble. On other platforms, it should
-  // not affect the behavior of the bubble (just the appearance), so enable for
-  // all platforms.
-  UseMdOnly();
-  SupportsTestUi::SetUp();
-}
-
 void ExtensionMessageBubbleViewBrowserTest::ShowUi(const std::string& name) {
   // When invoked this way, the dialog test harness must close the bubble.
   base::AutoReset<bool> guard(&block_close_, true);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index aa30c75..4bbcc0a 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -151,7 +151,7 @@
                            V1BackButton);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
                            ToggleTabletModeOnMinimizedWindow);
-  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
+  FRIEND_TEST_ALL_PREFIXES(HostedAppNonClientFrameViewAshTest,
                            ActiveStateOfButtonMatchesWidget);
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
                            RestoreMinimizedBrowserUpdatesCaption);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 6a2595d..f6e05ef5 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -77,6 +77,7 @@
 #include "ui/aura/test/env_test_helper.h"
 #include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/base_event_utils.h"
@@ -478,19 +479,6 @@
                test.size_button()->icon_definition_for_test()->name);
 }
 
-// Regression test for https://crbug.com/839955
-IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewAshTest,
-                       ActiveStateOfButtonMatchesWidget) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-
-  ash::FrameCaptionButtonContainerView::TestApi test(
-      GetFrameViewAsh(browser_view)->caption_button_container_);
-  EXPECT_TRUE(test.size_button()->paint_as_active());
-
-  browser_view->GetWidget()->Deactivate();
-  EXPECT_FALSE(test.size_button()->paint_as_active());
-}
-
 // This is a regression test that session restore minimized browser should
 // update caption buttons (https://crbug.com/827444).
 IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewAshTest,
@@ -807,14 +795,19 @@
     app_menu_button_ = hosted_app_button_container_->app_menu_button_;
   }
 
-  AppMenu* GetAppMenu(HostedAppButtonContainer* button_container) {
-    return button_container->app_menu_button_->app_menu_for_testing();
+  AppMenu* GetAppMenu() {
+    return hosted_app_button_container_->app_menu_button_
+        ->app_menu_for_testing();
   }
 
-  SkColor GetActiveColor(HostedAppButtonContainer* button_container) {
+  SkColor GetActiveColor() {
     return hosted_app_button_container_->active_color_;
   }
 
+  bool GetPaintingAsActive() {
+    return hosted_app_button_container_->paint_as_active_;
+  }
+
   PageActionIconView* GetPageActionIcon(PageActionIconType type) {
     return browser_view_->toolbar_button_provider()
         ->GetPageActionIconContainerView()
@@ -875,7 +868,7 @@
   aura::Window* window = browser_view_->GetWidget()->GetNativeWindow();
   EXPECT_EQ(GetThemeColor(),window->GetProperty(ash::kFrameActiveColorKey));
   EXPECT_EQ(GetThemeColor(), window->GetProperty(ash::kFrameInactiveColorKey));
-  EXPECT_EQ(SK_ColorWHITE, GetActiveColor(hosted_app_button_container_));
+  EXPECT_EQ(SK_ColorWHITE, GetActiveColor());
 }
 
 // Make sure that for hosted apps, the height of the frame doesn't exceed the
@@ -954,9 +947,9 @@
 // Tests that the show app menu command opens the app menu for web app windows.
 IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest,
                        BrowserCommandShowAppMenu) {
-  EXPECT_EQ(nullptr, GetAppMenu(hosted_app_button_container_));
+  EXPECT_EQ(nullptr, GetAppMenu());
   chrome::ExecuteCommand(app_browser_, IDC_SHOW_APP_MENU);
-  EXPECT_NE(nullptr, GetAppMenu(hosted_app_button_container_));
+  EXPECT_NE(nullptr, GetAppMenu());
 }
 
 // Tests that the focus next pane command focuses the app menu for web app
@@ -1018,7 +1011,7 @@
   SimulateClickOnView(app_menu_button_);
 
   // All extension actions should always be showing in the menu.
-  EXPECT_EQ(3u, GetAppMenu(hosted_app_button_container_)
+  EXPECT_EQ(3u, GetAppMenu()
                     ->extension_toolbar_for_testing()
                     ->container_for_testing()
                     ->VisibleBrowserActions());
@@ -1029,6 +1022,27 @@
   EXPECT_EQ(1u, browser_actions_container_->VisibleBrowserActions());
 }
 
+// Regression test for https://crbug.com/839955
+IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest,
+                       ActiveStateOfButtonMatchesWidget) {
+  // The caption button part of this test is covered for OopAsh by
+  // CustomFrameViewAshTest::ActiveStateOfButtonMatchesWidget.
+  if (features::IsAshInBrowserProcess()) {
+    ash::FrameCaptionButtonContainerView::TestApi test(
+        GetFrameViewAsh(browser_view_)->caption_button_container_);
+    EXPECT_TRUE(test.size_button()->paint_as_active());
+  }
+  EXPECT_TRUE(GetPaintingAsActive());
+
+  browser_view_->GetWidget()->Deactivate();
+  if (features::IsAshInBrowserProcess()) {
+    ash::FrameCaptionButtonContainerView::TestApi test(
+        GetFrameViewAsh(browser_view_)->caption_button_container_);
+    EXPECT_FALSE(test.size_button()->paint_as_active());
+  }
+  EXPECT_FALSE(GetPaintingAsActive());
+}
+
 namespace {
 
 class BrowserNonClientFrameViewAshBackButtonTest
diff --git a/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc b/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
index 619830e..7feb285c5 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
@@ -44,14 +44,6 @@
     }
   }
 
-  // SupportsTestUi:
-  void SetUp() override {
-#if defined(OS_MACOSX)
-    UseMdOnly();  // This needs to be called during SetUp() on Mac.
-#endif
-    SupportsTestUi::SetUp();
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(PasswordBubbleBrowserTest);
 };
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
index 3e9d2b7..ef36e98 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
@@ -13,11 +13,6 @@
  protected:
   RelaunchRecommendedBubbleViewDialogTest() = default;
 
-  void SetUp() override {
-    UseMdOnly();
-    DialogBrowserTest::SetUp();
-  }
-
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
     base::TimeTicks detection_time =
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
index f9d3826..0e6c37a 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
@@ -13,11 +13,6 @@
  protected:
   RelaunchRequiredDialogViewDialogTest() = default;
 
-  void SetUp() override {
-    UseMdOnly();
-    DialogBrowserTest::SetUp();
-  }
-
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
     base::TimeTicks deadline =
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc
index 48b8eade..9e0c329d 100644
--- a/chrome/browser/ui/views/status_bubble_views.cc
+++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -159,10 +159,10 @@
   void SetStyle(BubbleStyle style);
 
   // Show the bubble instantly.
-  void Show();
+  void ShowInstantly();
 
   // Hide the bubble instantly.
-  void Hide();
+  void HideInstantly();
 
   // Resets any timers we have. Typically called when the user moves a
   // mouse.
@@ -251,20 +251,32 @@
   }
 }
 
-void StatusBubbleViews::StatusView::Show() {
+void StatusBubbleViews::StatusView::ShowInstantly() {
   animation_->Stop();
   CancelTimer();
   SetOpacity(1.0);
+#if defined(OS_MACOSX)
+  // Don't order an already-visible window on Mac, since that may trigger a
+  // space switch. The window stacking is guaranteed by its child window status.
+  if (!popup_->IsVisible())
+    popup_->ShowInactive();
+#else
   popup_->ShowInactive();
+#endif
   state_ = BUBBLE_SHOWN;
 }
 
-void StatusBubbleViews::StatusView::Hide() {
+void StatusBubbleViews::StatusView::HideInstantly() {
   animation_->Stop();
   CancelTimer();
   SetOpacity(0.0);
   text_.clear();
+#if !defined(OS_MACOSX)
+  // Don't orderOut: the window on macOS. Doing so for a child window requires
+  // it to be detached/reattached, which may trigger a space switch. Instead,
+  // just leave the window fully transparent and unclickable.
   popup_->Hide();
+#endif
   state_ = BUBBLE_HIDDEN;
 }
 
@@ -322,9 +334,8 @@
     state_ = BUBBLE_HIDING_TIMER;
     StartTimer(base::TimeDelta::FromMilliseconds(kHideDelay));
   } else if (state_ == BUBBLE_SHOWING_TIMER) {
-    state_ = BUBBLE_HIDDEN;
-    popup_->Hide();
-    CancelTimer();
+    HideInstantly();
+    DCHECK_EQ(BUBBLE_HIDDEN, state_);
   } else if (state_ == BUBBLE_SHOWING_FADE) {
     state_ = BUBBLE_HIDING_FADE;
     // Figure out where we are in the current fade.
@@ -372,8 +383,8 @@
 
 void StatusBubbleViews::StatusView::OnAnimationEnded() {
   if (state_ == BUBBLE_HIDING_FADE) {
-    state_ = BUBBLE_HIDDEN;
-    popup_->Hide();
+    HideInstantly();
+    DCHECK_EQ(BUBBLE_HIDDEN, state_);
   } else if (state_ == BUBBLE_SHOWING_FADE) {
     state_ = BUBBLE_SHOWN;
   }
@@ -733,7 +744,7 @@
   status_text_ = status_text;
   if (!status_text_.empty()) {
     view_->SetText(status_text, true);
-    view_->Show();
+    view_->ShowInstantly();
   } else if (!url_text_.empty()) {
     view_->SetText(url_text_, true);
   } else {
@@ -797,7 +808,7 @@
   status_text_ = base::string16();
   url_text_ = base::string16();
   if (view_)
-    view_->Hide();
+    view_->HideInstantly();
 }
 
 void StatusBubbleViews::MouseMoved(bool left_content) {
diff --git a/chrome/browser/ui/views/status_bubble_views.h b/chrome/browser/ui/views/status_bubble_views.h
index b3d0bb68..ab56539 100644
--- a/chrome/browser/ui/views/status_bubble_views.h
+++ b/chrome/browser/ui/views/status_bubble_views.h
@@ -69,6 +69,8 @@
   void MouseMovedAt(const gfx::Point& location, bool left_content);
 
  private:
+  friend class StatusBubbleViewsTest;
+
   class StatusView;
   class StatusViewAnimation;
   class StatusViewExpander;
diff --git a/chrome/browser/ui/views/status_bubble_views_browsertest.cc b/chrome/browser/ui/views/status_bubble_views_browsertest.cc
new file mode 100644
index 0000000..30eb1ac
--- /dev/null
+++ b/chrome/browser/ui/views/status_bubble_views_browsertest.cc
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/status_bubble_views.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/status_bubble_views_browsertest_mac.h"
+#include "chrome/browser/ui/views_mode_controller.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/views/scoped_macviews_browser_mode.h"
+#include "ui/views/widget/widget.h"
+
+class StatusBubbleViewsTest : public InProcessBrowserTest {
+ public:
+  StatusBubble* GetBubble() { return browser()->window()->GetStatusBubble(); }
+  views::Widget* GetWidget() {
+    return static_cast<StatusBubbleViews*>(GetBubble())->popup();
+  }
+
+ private:
+  test::ScopedMacViewsBrowserMode views_mode_{true};
+};
+
+// Ensure the status bubble does not hide itself on Mac. Doing so can trigger
+// unwanted space switches due to rdar://9037452. See https://crbug.com/866760.
+IN_PROC_BROWSER_TEST_F(StatusBubbleViewsTest, NeverHideOnMac) {
+  StatusBubble* bubble = GetBubble();
+  ASSERT_TRUE(bubble);
+  views::Widget* widget = GetWidget();
+  ASSERT_TRUE(widget);
+
+  // The status bubble has never been shown.
+  EXPECT_FALSE(widget->IsVisible());
+
+  // Setting status shows the widget.
+  bubble->SetStatus(base::ASCIIToUTF16("test"));
+  EXPECT_TRUE(widget->IsVisible());
+
+#if defined(OS_MACOSX)
+  // Check alpha on Mac as well. On other platforms it is redundant.
+  EXPECT_EQ(1.f, test::GetNativeWindowAlphaValue(widget->GetNativeWindow()));
+  bubble->Hide();
+  // On Mac, the bubble widget remains visible so it can remain a child window.
+  // However, it is fully transparent.
+  EXPECT_TRUE(widget->IsVisible());
+  EXPECT_EQ(0.f, test::GetNativeWindowAlphaValue(widget->GetNativeWindow()));
+#else
+  bubble->Hide();
+  EXPECT_FALSE(widget->IsVisible());
+#endif
+}
diff --git a/chrome/browser/ui/views/status_bubble_views_browsertest_mac.h b/chrome/browser/ui/views/status_bubble_views_browsertest_mac.h
new file mode 100644
index 0000000..300a3bf
--- /dev/null
+++ b/chrome/browser/ui/views/status_bubble_views_browsertest_mac.h
@@ -0,0 +1,22 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_STATUS_BUBBLE_VIEWS_BROWSERTEST_MAC_H_
+#define CHROME_BROWSER_UI_VIEWS_STATUS_BUBBLE_VIEWS_BROWSERTEST_MAC_H_
+
+#include "build/build_config.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace test {
+
+#if defined(OS_MACOSX)
+// Returns [window alphaValue]. Widget doesn't offer a GetOpacity(), only
+// SetOpacity(). Currently this is only defined for Mac. Obtaining this for
+// other platforms is convoluted.
+float GetNativeWindowAlphaValue(gfx::NativeWindow window);
+#endif
+
+}  // namespace test
+
+#endif  // CHROME_BROWSER_UI_VIEWS_STATUS_BUBBLE_VIEWS_BROWSERTEST_MAC_H_
diff --git a/chrome/browser/ui/views/status_bubble_views_browsertest_mac.mm b/chrome/browser/ui/views/status_bubble_views_browsertest_mac.mm
new file mode 100644
index 0000000..ac37523
--- /dev/null
+++ b/chrome/browser/ui/views/status_bubble_views_browsertest_mac.mm
@@ -0,0 +1,15 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/status_bubble_views_browsertest_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+namespace test {
+
+float GetNativeWindowAlphaValue(gfx::NativeWindow window) {
+  return [window alphaValue];
+}
+
+}  // namespace test
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc
index 528d652..80edcce2a 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc
@@ -37,12 +37,6 @@
  public:
   ProfileSigninConfirmationDialogTest() {}
 
-  // DialogBrowserTest:
-  void SetUp() override {
-    UseMdOnly();
-    DialogBrowserTest::SetUp();
-  }
-
   void ShowUi(const std::string& name) override {
     Profile* profile = browser()->profile();
 
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc
index 0c7f30b1..7e2ad969 100644
--- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc
@@ -289,11 +289,6 @@
   TryChromeDialogTest()
       : SupportsTestDialog<TryChromeDialogBrowserTestBase>(GetParam()) {}
 
-  // SupportsTestUi:
-  void SetUp() override {
-    UseMdOnly();
-    SupportsTestUi::SetUp();
-  }
   void ShowUi(const std::string& name) override { ShowDialogSync(); }
 
  private:
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index de9468e..71e6595 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1697,12 +1697,12 @@
   AddLocalizedStringsBulk(html_source, localized_strings,
                           arraysize(localized_strings));
 
-  // TODO(https://crbug.com/854562): Integrate these strings into the
-  // |localized_strings| array once Autofill Home is fully launched.
   if (base::FeatureList::IsEnabled(password_manager::features::kAutofillHome)) {
+    // TODO(https://crbug.com/854562): Integrate this string into the
+    // |localized_strings| array once Autofill Home is fully launched.
     html_source->AddLocalizedString(
         "enablePaymentsIntegrationCheckboxLabel",
-        IDS_SETTINGS_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL_AUTOFILL_HOME);
+        IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL);
   } else {
     html_source->AddLocalizedString(
         "enablePaymentsIntegrationCheckboxLabel",
diff --git a/chrome/browser/ui/webui/welcome_ui.cc b/chrome/browser/ui/webui/welcome_ui.cc
index db32e35..7d131cf 100644
--- a/chrome/browser/ui/webui/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome_ui.cc
@@ -21,6 +21,7 @@
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+#include "base/metrics/histogram_macros.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "components/nux/show_promo_delegate.h"
 #include "components/nux_google_apps/constants.h"
@@ -130,6 +131,12 @@
   if (url.EqualsIgnoringRef(GURL(nux_google_apps::kNuxGoogleAppsUrl))) {
     // Record that the new user experience page was visited.
     profile->GetPrefs()->SetBoolean(prefs::kHasSeenGoogleAppsPromoPage, true);
+
+    // Record UMA.
+    UMA_HISTOGRAM_ENUMERATION(
+        nux_google_apps::kGoogleAppsInteractionHistogram,
+        nux_google_apps::GoogleAppsInteraction::kPromptShown,
+        nux_google_apps::GoogleAppsInteraction::kCount);
     return;
   }
 #endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/chrome_cleaner/os/digest_verifier.cc b/chrome/chrome_cleaner/os/digest_verifier.cc
index f28e23e0..435a4a8 100644
--- a/chrome/chrome_cleaner/os/digest_verifier.cc
+++ b/chrome/chrome_cleaner/os/digest_verifier.cc
@@ -36,7 +36,7 @@
     return false;
 
   std::string actual_digest;
-  if (!chrome_cleaner::ComputeDigestSHA256(file, &actual_digest)) {
+  if (!chrome_cleaner::ComputeSHA256DigestOfPath(file, &actual_digest)) {
     LOG(ERROR) << "Failed to compute digest for " << SanitizePath(file);
     return false;
   }
diff --git a/chrome/chrome_cleaner/os/disk_util.cc b/chrome/chrome_cleaner/os/disk_util.cc
index 6b8e4b2..3f2ceff7 100644
--- a/chrome/chrome_cleaner/os/disk_util.cc
+++ b/chrome/chrome_cleaner/os/disk_util.cc
@@ -542,7 +542,7 @@
   RetrievePathInformation(expanded_path, file_information);
 
   // Retrieve the detailed file information.
-  if (!ComputeDigestSHA256(expanded_path, &file_information->sha256)) {
+  if (!ComputeSHA256DigestOfPath(expanded_path, &file_information->sha256)) {
     LOG(ERROR) << "Unable to compute digest SHA256 for: '"
                << file_information->path << "'";
     return false;
@@ -588,7 +588,8 @@
   }
 }
 
-bool ComputeDigestSHA256(const base::FilePath& path, std::string* digest) {
+bool ComputeSHA256DigestOfPath(const base::FilePath& path,
+                               std::string* digest) {
   DCHECK(digest);
 
   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
@@ -611,6 +612,21 @@
   return true;
 }
 
+bool ComputeSHA256DigestOfString(const std::string& content,
+                                 std::string* digest) {
+  DCHECK(digest);
+
+  std::unique_ptr<SecureHash> ctx(SecureHash::Create(SecureHash::SHA256));
+
+  ctx->Update(content.c_str(), content.length());
+
+  char digest_bytes[crypto::kSHA256Length];
+  ctx->Finish(digest_bytes, crypto::kSHA256Length);
+
+  *digest = base::HexEncode(digest_bytes, crypto::kSHA256Length);
+  return true;
+}
+
 bool GUIDLess::operator()(const GUID& smaller, const GUID& larger) const {
   if (smaller.Data1 < larger.Data1)
     return true;
diff --git a/chrome/chrome_cleaner/os/disk_util.h b/chrome/chrome_cleaner/os/disk_util.h
index 4285c1e8..885d4a6 100644
--- a/chrome/chrome_cleaner/os/disk_util.h
+++ b/chrome/chrome_cleaner/os/disk_util.h
@@ -124,7 +124,12 @@
 
 // Compute the SHA256 checksum of |path| and store it as base16 into |digest|.
 // Return true on success.
-bool ComputeDigestSHA256(const base::FilePath& path, std::string* digest);
+bool ComputeSHA256DigestOfPath(const base::FilePath& path, std::string* digest);
+
+// Compute the SHA256 of |content| and store it as base16 into |digest|.
+// Return true on success.
+bool ComputeSHA256DigestOfString(const std::string& content,
+                                 std::string* digest);
 
 // Return the list of registered Layered Service Providers. In case the same DLL
 // is registered with multiple ProviderId, |providers| is a map from the DLL
diff --git a/chrome/chrome_cleaner/os/disk_util_unittest.cc b/chrome/chrome_cleaner/os/disk_util_unittest.cc
index 84eaada..295e78e 100644
--- a/chrome/chrome_cleaner/os/disk_util_unittest.cc
+++ b/chrome/chrome_cleaner/os/disk_util_unittest.cc
@@ -630,14 +630,14 @@
   ASSERT_TRUE(PathEqual(expanded_file3, file_path3_native));
 }
 
-TEST(DiskUtilTests, ComputeDigestSHA256) {
+TEST(DiskUtilTests, ComputeSHA256DigestOfPath) {
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   // Check the digest of an non-existing file.
   base::FilePath file_path1(temp_dir.GetPath().Append(kFileName1));
   std::string digest1;
-  EXPECT_FALSE(ComputeDigestSHA256(file_path1, &digest1));
+  EXPECT_FALSE(ComputeSHA256DigestOfPath(file_path1, &digest1));
   EXPECT_TRUE(digest1.empty());
 
   // Create an empty file and validate the digest.
@@ -646,7 +646,7 @@
   empty_file.Close();
 
   std::string digest2;
-  EXPECT_TRUE(ComputeDigestSHA256(file_path2, &digest2));
+  EXPECT_TRUE(ComputeSHA256DigestOfPath(file_path2, &digest2));
   EXPECT_STREQ(
       "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
       digest2.c_str());
@@ -661,13 +661,13 @@
   valid_file.Close();
 
   std::string digest3;
-  EXPECT_TRUE(ComputeDigestSHA256(file_path3, &digest3));
+  EXPECT_TRUE(ComputeSHA256DigestOfPath(file_path3, &digest3));
   EXPECT_STREQ(
       "BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371",
       digest3.c_str());
 }
 
-TEST(DiskUtilTests, ComputeDigestSHA256OnBigFile) {
+TEST(DiskUtilTests, ComputeSHA256DigestOfPathOnBigFile) {
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
@@ -712,11 +712,18 @@
     valid_file.Close();
 
     std::string digest;
-    EXPECT_TRUE(ComputeDigestSHA256(file_path, &digest));
+    EXPECT_TRUE(ComputeSHA256DigestOfPath(file_path, &digest));
     EXPECT_STREQ(info->digest, digest.c_str());
   }
 }
 
+TEST(DiskUtilTests, ComputeSHA256DigestOfString) {
+  std::string digest_result;
+  std::string content(kFileContent2, sizeof(kFileContent2));
+  EXPECT_TRUE(ComputeSHA256DigestOfString(content, &digest_result));
+  EXPECT_STREQ(kFileContentDigests[2], digest_result.c_str());
+}
+
 TEST(DiskUtilTests, GetLayeredServiceProviders) {
   // Make sure that running the OS implementation doesn't crash/dcheck.
   LSPPathToGUIDs providers;
diff --git a/chrome/chrome_cleaner/test/test_service_main.cc b/chrome/chrome_cleaner/test/test_service_main.cc
index 20afd27..9cd46090 100644
--- a/chrome/chrome_cleaner/test/test_service_main.cc
+++ b/chrome/chrome_cleaner/test/test_service_main.cc
@@ -57,9 +57,9 @@
   }
 
  private:
-  SERVICE_STATUS_HANDLE status_handle_ = 0;
-  SERVICE_STATUS service_status_ = {};
-  HANDLE service_stop_event_ = INVALID_HANDLE_VALUE;
+  SERVICE_STATUS_HANDLE status_handle_{};
+  SERVICE_STATUS service_status_{};
+  HANDLE service_stop_event_{INVALID_HANDLE_VALUE};
   THREAD_CHECKER(service_status_thread_checker_);
 };
 
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index eca08ff7..8b3d165 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -59,10 +59,12 @@
     if (is_chromeos) {
       sources += [
         "$root_gen_dir/ash/components/resources/ash_components_resources_${percent}_percent.pak",
+        "$root_gen_dir/ash/login/resources/login_resources_${percent}_percent.pak",
         "$root_gen_dir/ui/chromeos/resources/ui_chromeos_resources_${percent}_percent.pak",
       ]
       deps += [
         "//ash/components/resources",
+        "//ash/login/resources",
         "//ui/chromeos/resources",
       ]
     }
diff --git a/chrome/common/extensions/api/chromeos_info_private.json b/chrome/common/extensions/api/chromeos_info_private.json
index c0a5f0bd..62e156a 100644
--- a/chrome/common/extensions/api/chromeos_info_private.json
+++ b/chrome/common/extensions/api/chromeos_info_private.json
@@ -57,7 +57,7 @@
       {
         "id": "StylusStatus",
         "type": "string",
-        "enum": ["unsupported", "supported"],
+        "enum": ["unsupported", "supported", "seen"],
         "description": "Status of stylus."
       }
     ],
diff --git a/chrome/common/media_router/discovery/media_sink_service_base.cc b/chrome/common/media_router/discovery/media_sink_service_base.cc
index a9536cd..d59f037 100644
--- a/chrome/common/media_router/discovery/media_sink_service_base.cc
+++ b/chrome/common/media_router/discovery/media_sink_service_base.cc
@@ -59,17 +59,7 @@
 
 void MediaSinkServiceBase::RemoveSink(const MediaSinkInternal& sink) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Make a copy of the sink to avoid potential use-after-free.
-  MediaSink::Id sink_id = sink.sink().id();
-  MediaSinkInternal sink_copy = sink;
-  if (!sinks_.erase(sink_id))
-    return;
-
-  for (auto& observer : observers_)
-    observer.OnSinkRemoved(sink_copy);
-
-  StartTimer();
+  RemoveSinkById(sink.sink().id());
 }
 
 void MediaSinkServiceBase::RemoveSinkById(const MediaSink::Id& sink_id) {
@@ -78,8 +68,7 @@
   if (it == sinks_.end())
     return;
 
-  // Make a copy of the sink to avoid potential use-after-free.
-  MediaSinkInternal sink = it->second;
+  MediaSinkInternal sink = std::move(it->second);
   sinks_.erase(it);
   for (auto& observer : observers_)
     observer.OnSinkRemoved(sink);
diff --git a/chrome/install_static/install_util.cc b/chrome/install_static/install_util.cc
index 6bbb85e..9e1d3a4b 100644
--- a/chrome/install_static/install_util.cc
+++ b/chrome/install_static/install_util.cc
@@ -731,11 +731,16 @@
 
   // The first argument (the program) is delimited by whitespace or quotes based
   // on its first character.
-  int argv0_length = 0;
-  if (p[0] == L'"')
-    argv0_length = wcschr(++p, L'"') - (command_line.c_str() + 1);
-  else
+  size_t argv0_length = 0;
+  if (p[0] == L'"') {
+    const wchar_t* closing = wcschr(++p, L'"');
+    if (!closing)
+      argv0_length = command_line.size() - 1;  // Skip the opening quote.
+    else
+      argv0_length = closing - (command_line.c_str() + 1);
+  } else {
     argv0_length = wcscspn(p, kSpaceTab);
+  }
   result.emplace_back(p, argv0_length);
   if (p[argv0_length] == 0)
     return result;
@@ -748,11 +753,8 @@
     p += wcsspn(p, kSpaceTab);
 
     // End of arguments.
-    if (p[0] == 0) {
-      if (!token.empty())
-        result.push_back(token);
+    if (p[0] == 0)
       break;
-    }
 
     state = SpecialChars::kInterpret;
 
diff --git a/chrome/install_static/install_util_unittest.cc b/chrome/install_static/install_util_unittest.cc
index e040d8f..98df794a 100644
--- a/chrome/install_static/install_util_unittest.cc
+++ b/chrome/install_static/install_util_unittest.cc
@@ -93,6 +93,10 @@
   value = GetSwitchValueFromCommandLine(L"c:\\temp\\bleh.exe --type=\t\t\t",
                                         L"type");
   EXPECT_TRUE(value.empty());
+
+  // Bad command line without closing quotes. Should not crash.
+  value = GetSwitchValueFromCommandLine(L"\"blah --type=\t\t\t", L"type");
+  EXPECT_TRUE(value.empty());
 }
 
 TEST(InstallStaticTest, SpacesAndQuotesInCommandLineArguments) {
@@ -156,15 +160,20 @@
 
   tokenized = TokenizeCommandLineToArray(
       L"\"C:\\with space\\b.exe\" --stuff=\"d:\\stuff and things\"");
-  EXPECT_EQ(2u, tokenized.size());
+  ASSERT_EQ(2u, tokenized.size());
   EXPECT_EQ(L"C:\\with space\\b.exe", tokenized[0]);
   EXPECT_EQ(L"--stuff=d:\\stuff and things", tokenized[1]);
 
   tokenized = TokenizeCommandLineToArray(
       L"\"C:\\with space\\b.exe\" \\\\\\\"\"");
-  EXPECT_EQ(2u, tokenized.size());
+  ASSERT_EQ(2u, tokenized.size());
   EXPECT_EQ(L"C:\\with space\\b.exe", tokenized[0]);
   EXPECT_EQ(L"\\\"", tokenized[1]);
+
+  tokenized =
+      TokenizeCommandLineToArray(L"\"blah --type=\t\t\t no closing quote");
+  ASSERT_EQ(1u, tokenized.size());
+  EXPECT_EQ(L"blah --type=\t\t\t no closing quote", tokenized[0]);
 }
 
 // Test cases from
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ad05ec6..00b91a6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -789,7 +789,6 @@
       "../browser/task_manager/task_manager_tester.h",
       "../browser/themes/theme_service_browsertest.cc",
       "../browser/tracing/chrome_tracing_delegate_browsertest.cc",
-      "../browser/translate/translate_browsertest.cc",
       "../browser/translate/translate_manager_browsertest.cc",
       "../browser/ui/ask_google_for_suggestions_dialog_browsertest.cc",
       "../browser/ui/autofill/card_unmask_prompt_view_browsertest.cc",
@@ -1523,6 +1522,9 @@
           "../browser/ui/views/passwords/password_generation_popup_view_tester_views.cc",
           "../browser/ui/views/passwords/password_generation_popup_view_tester_views.h",
           "../browser/ui/views/session_crashed_bubble_view_browsertest.cc",
+          "../browser/ui/views/status_bubble_views_browsertest.cc",
+          "../browser/ui/views/status_bubble_views_browsertest_mac.h",
+          "../browser/ui/views/status_bubble_views_browsertest_mac.mm",
           "../browser/ui/views/task_manager_view_browsertest.cc",
           "../browser/ui/views/toolbar/browser_actions_container_browsertest.cc",
           "../browser/ui/views/translate/translate_bubble_test_utils_views.cc",
@@ -1793,6 +1795,7 @@
         "//services/preferences/public/cpp",
         "//services/preferences/public/mojom",
         "//services/service_manager/public/cpp",
+        "//services/ui/public/cpp/input_devices:test_support",
         "//ui/keyboard:test_support",
         "//ui/login:resources",
         "//url",
@@ -4953,6 +4956,7 @@
 
     if (is_chromeos) {
       sources += [
+        "../browser/chromeos/accessibility/dictation_chromeos_browsertest.cc",
         "../browser/chromeos/accessibility/magnification_controller_browsertest.cc",
         "../browser/chromeos/accessibility/select_to_speak_browsertest.cc",
         "../browser/chromeos/accessibility/spoken_feedback_browsertest.cc",
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 391d57b..04cbc89 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -113,8 +113,6 @@
     'ChromeDriverTest.testWindowMinimize',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2522
     'ChromeDriverTest.testWindowMaximize',
-    # https://bugs.chromium.org/p/chromium/issues/detail?id=868376
-    'ChromeDriverTest.testHasFocusOnStartup',
 ]
 
 _DESKTOP_NEGATIVE_FILTER = [
@@ -1098,11 +1096,6 @@
     self._driver.MouseClick(2)
     self.assertTrue(self._driver.ExecuteScript('return success'))
 
-  def testHasFocusOnStartup(self):
-    # Some pages (about:blank) cause Chrome to put the focus in URL bar.
-    # This breaks tests depending on focus.
-    self.assertTrue(self._driver.ExecuteScript('return document.hasFocus()'))
-
   def testTabCrash(self):
     # If a tab is crashed, the session will be deleted.
     # When 31 is released, will reload the tab instead.
diff --git a/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js b/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
index 50b4d40..0465155 100644
--- a/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
+++ b/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
@@ -53,6 +53,9 @@
             case 'stylus supported':
               chrome.test.assertEq('supported', values['stylusStatus']);
               break;
+            case 'stylus seen':
+              chrome.test.assertEq('seen', values['stylusStatus']);
+              break;
           }
         }));
   });
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index 0766d3b3..0a947cc 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -73,10 +73,6 @@
 
     <dialog div id="edit-bg-dialog">
       <div id="edit-bg-title"></div>
-      <div id="edit-bg-google-photos" class="bg-option" tabindex="0" hidden>
-        <div class="bg-option-img"></div>
-        <div id="edit-bg-google-photos-text" class="bg-option-text"></div>
-      </div>
       <div id="edit-bg-default-wallpapers" class="bg-option" tabindex="0">
         <div class="bg-option-img"></div>
         <div id="edit-bg-default-wallpapers-text" class="bg-option-text">
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index b856beb..867e342aa 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3823,7 +3823,7 @@
     "test_policy": {"UpdateTimeRestrictions": [{"start" : {"day_of_week" : "Wednesday", "minutes": 30, "hours": 3}, "end": {"day_of_week" : "Thursday", "minutes": 20, "hours": 13}}, {"start": {"day_of_week": "Monday", "minutes": 45, "hours": 1}, "end": {"day_of_week": "Monday", "minutes": 50, "hours": 15}}]}
   },
 
-  "DeviceUpdateStagingPercentOfFleetPerWeek": {
+  "DeviceUpdateStagingSchedule": {
     "os": ["chromeos"],
     "note": "Chrome OS device policy used by update_engine only, not used in Chrome."
   },
diff --git a/chromecast/crash/linux/minidump_uploader.cc b/chromecast/crash/linux/minidump_uploader.cc
index be7717a..124e4264 100644
--- a/chromecast/crash/linux/minidump_uploader.cc
+++ b/chromecast/crash/linux/minidump_uploader.cc
@@ -42,6 +42,8 @@
     "https://clients2.google.com/cr/staging_report";
 const char kCrashServerProduction[] = "https://clients2.google.com/cr/report";
 
+const char kVirtualChannel[] = "virtual-channel";
+
 typedef std::vector<std::unique_ptr<DumpInfo>> DumpList;
 
 std::unique_ptr<PrefService> CreatePrefService() {
@@ -52,6 +54,7 @@
   PrefRegistrySimple* registry = new PrefRegistrySimple;
   registry->RegisterBooleanPref(prefs::kOptInStats, true);
   registry->RegisterStringPref(::metrics::prefs::kMetricsClientID, "");
+  registry->RegisterStringPref(kVirtualChannel, "");
 
   PrefServiceFactory prefServiceFactory;
   prefServiceFactory.SetUserPrefsFile(
@@ -128,6 +131,10 @@
   std::unique_ptr<PrefService> pref_service = pref_service_generator_.Run();
   const std::string& client_id(
       pref_service->GetString(::metrics::prefs::kMetricsClientID));
+  std::string virtual_channel(pref_service->GetString(kVirtualChannel));
+  if (virtual_channel.empty()) {
+    virtual_channel = release_channel_;
+  }
   bool opt_in_stats = pref_service->GetBoolean(prefs::kOptInStats);
   // Handle each dump and consume it out of the structure.
   while (dumps.size()) {
@@ -231,6 +238,7 @@
     g.SetParameter("ro.product.model", device_model_);
     g.SetParameter("ro.product.manufacturer", manufacturer_);
     g.SetParameter("ro.system.version", system_version_);
+    g.SetParameter("release.virtual-channel", virtual_channel);
 
     // Add app state information
     if (!dump.params().previous_app_name.empty()) {
diff --git a/chromecast/crash/linux/minidump_uploader_unittest.cc b/chromecast/crash/linux/minidump_uploader_unittest.cc
index 8047e8aa..ac89484 100644
--- a/chromecast/crash/linux/minidump_uploader_unittest.cc
+++ b/chromecast/crash/linux/minidump_uploader_unittest.cc
@@ -31,6 +31,8 @@
 const char kLockfileName[] = "lockfile";
 const char kMetadataName[] = "metadata";
 const char kMinidumpSubdir[] = "minidumps";
+const char kVirtualChannel[] = "virtual-channel";
+const char kVirtualChannelName[] = "a-virtual-chanel";
 
 typedef std::vector<std::unique_ptr<DumpInfo>> DumpList;
 
@@ -40,6 +42,7 @@
   retval->registry()->RegisterBooleanPref(prefs::kOptInStats, opt_in);
   retval->registry()->RegisterStringPref(::metrics::prefs::kMetricsClientID,
                                          "");
+  retval->registry()->RegisterStringPref(kVirtualChannel, kVirtualChannelName);
   return std::move(retval);
 }
 
diff --git a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeControl.java b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeControl.java
index 6217294..a91cb1c 100644
--- a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeControl.java
+++ b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeControl.java
@@ -45,6 +45,7 @@
         Settings(int streamType) {
             mStreamType = streamType;
             mMaxVolumeIndexAsFloat = (float) mAudioManager.getStreamMaxVolume(mStreamType);
+            mMinVolumeIndex = mAudioManager.getStreamMinVolume(mStreamType);
             refreshVolume();
             refreshMuteState();
         }
@@ -55,10 +56,11 @@
         }
 
         /** Sets the given volume level in AudioManager. The given level is in the range
-         * [0.0f .. 1.0f] and converted to a volume index in the range [0 .. mMaxVolumeIndex] before
-         * writing to AudioManager. */
+         * [0.0f .. 1.0f] and converted to a volume index in the range
+         * [mMinVolumeIndex .. mMaxVolumeIndex] before writing to AudioManager. */
         void setVolumeLevel(float level) {
             int volumeIndex = Math.round(level * mMaxVolumeIndexAsFloat);
+            volumeIndex = Math.max(volumeIndex, mMinVolumeIndex);
             mVolumeIndexAsFloat = (float) volumeIndex;
             if (DEBUG_LEVEL >= 1) {
                 Log.i(TAG,
@@ -106,6 +108,9 @@
         // Cached maximum volume index. Stored as float for easier calculations.
         private final float mMaxVolumeIndexAsFloat;
 
+        // Cached minimum volume index.
+        private int mMinVolumeIndex;
+
         // Current volume index. Stored as float for easier calculations.
         float mVolumeIndexAsFloat;
 
diff --git a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java
index 647454a..8f71bddf 100644
--- a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java
+++ b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java
@@ -53,6 +53,19 @@
         }
     };
 
+    private static final SparseIntArray MIN_VOLUME_INDEX = new SparseIntArray(4) {
+        {
+            append(AudioManager.STREAM_MUSIC,
+                    getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC));
+            append(AudioManager.STREAM_ALARM,
+                    getAudioManager().getStreamMinVolume(AudioManager.STREAM_ALARM));
+            append(AudioManager.STREAM_SYSTEM,
+                    getAudioManager().getStreamMinVolume(AudioManager.STREAM_SYSTEM));
+            append(AudioManager.STREAM_VOICE_CALL,
+                    getAudioManager().getStreamMinVolume(AudioManager.STREAM_VOICE_CALL));
+        }
+    };
+
     private static AudioManager getAudioManager() {
         if (sAudioManager == null) {
             Context context = ContextUtils.getApplicationContext();
@@ -104,8 +117,11 @@
     static void dumpVolumeTables(int castType) {
         int streamType = getStreamType(castType);
         int maxIndex = MAX_VOLUME_INDEX.get(streamType);
-        Log.i(TAG, "Volume points for stream " + streamType + " (maxIndex=" + maxIndex + "):");
-        for (int idx = 0; idx <= maxIndex; idx++) {
+        int minIndex = MIN_VOLUME_INDEX.get(streamType);
+        Log.i(TAG,
+                "Volume points for stream " + streamType + " (maxIndex=" + maxIndex
+                        + " minIndex=" + minIndex + "):");
+        for (int idx = minIndex; idx <= maxIndex; idx++) {
             float db = getStreamVolumeDB(streamType, idx);
             float level = (float) idx / (float) maxIndex;
             Log.i(TAG, "    " + idx + "(" + level + ") -> " + db);
@@ -120,6 +136,7 @@
         level = Math.min(1.0f, Math.max(0.0f, level));
         int streamType = getStreamType(castType);
         int volumeIndex = Math.round(level * (float) MAX_VOLUME_INDEX.get(streamType));
+        volumeIndex = Math.max(volumeIndex, MIN_VOLUME_INDEX.get(streamType));
         return getStreamVolumeDB(streamType, volumeIndex);
     }
 
@@ -129,9 +146,10 @@
     static float dbFsToVolume(int castType, float db) {
         int streamType = getStreamType(castType);
         int maxIndex = MAX_VOLUME_INDEX.get(streamType);
+        int minIndex = MIN_VOLUME_INDEX.get(streamType);
 
-        float dbMin = getStreamVolumeDB(streamType, 0);
-        if (db <= dbMin) return 0.0f;
+        float dbMin = getStreamVolumeDB(streamType, minIndex);
+        if (db <= dbMin) return (float) minIndex / (float) maxIndex;
         float dbMax = getStreamVolumeDB(streamType, maxIndex);
         if (db >= dbMax) return 1.0f;
 
@@ -139,7 +157,7 @@
         // and find the interval [dbLeft .. dbRight] that contains db, then
         // interpolate to estimate the volume level to return.
         float dbLeft = dbMin, dbRight = dbMin;
-        int idx = 1;
+        int idx = minIndex + 1;
         for (; idx <= maxIndex; idx++) {
             dbLeft = dbRight;
             dbRight = getStreamVolumeDB(streamType, idx);
diff --git a/components/autofill/core/browser/autofill_assistant_unittest.cc b/components/autofill/core/browser/autofill_assistant_unittest.cc
index 342c81e..fc51c265 100644
--- a/components/autofill/core/browser/autofill_assistant_unittest.cc
+++ b/components/autofill/core/browser/autofill_assistant_unittest.cc
@@ -104,8 +104,7 @@
   std::unique_ptr<FormStructure> CreateValidCreditCardForm() {
     std::unique_ptr<FormStructure> form_structure;
     form_structure.reset(new FormStructure(CreateValidCreditCardFormData()));
-    form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                            /*source_id=*/0);
+    form_structure->DetermineHeuristicTypes();
     return form_structure;
   }
 
@@ -154,8 +153,7 @@
   // Can be shown if the context is secure.
   FormData form = CreateValidCreditCardFormData();
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::move(form_structure));
@@ -172,8 +170,7 @@
   form.origin = GURL("http://myform.com");
   form.action = GURL("http://myform.com/submit");
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::move(form_structure));
@@ -188,8 +185,7 @@
   FormData form = CreateValidCreditCardFormData();
   form.action = GURL("javascript:alert('hello');");
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::move(form_structure));
@@ -204,8 +200,7 @@
   FormData form = CreateValidCreditCardFormData();
   form.action = GURL("javascript:myFunc");
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::move(form_structure));
@@ -219,8 +214,7 @@
   FormData form = CreateValidCreditCardFormData();
   form.action = GURL();
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   std::vector<std::unique_ptr<FormStructure>> form_structures;
   form_structures.push_back(std::move(form_structure));
diff --git a/components/autofill/core/browser/autofill_handler.cc b/components/autofill/core/browser/autofill_handler.cc
index 4038683..0fb4776 100644
--- a/components/autofill/core/browser/autofill_handler.cc
+++ b/components/autofill/core/browser/autofill_handler.cc
@@ -190,6 +190,8 @@
                                       /*only_server_and_autofill_state=*/true);
   }
 
+  form_structure->DetermineHeuristicTypes();
+
   // Ownership is transferred to |form_structures_| which maintains it until
   // the manager is Reset() or destroyed. It is safe to use references below
   // as long as receivers don't take ownership.
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 4237918..56e7509ed 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -159,6 +159,19 @@
          type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
 }
 
+void LogDeveloperEngagementUkm(ukm::UkmRecorder* ukm_recorder,
+                               ukm::SourceId source_id,
+                               FormStructure* form_structure) {
+  if (form_structure->developer_engagement_metrics()) {
+    AutofillMetrics::LogDeveloperEngagementUkm(
+        ukm_recorder, source_id, form_structure->main_frame_origin().GetURL(),
+        form_structure->IsCompleteCreditCardForm(),
+        form_structure->GetFormTypes(),
+        form_structure->developer_engagement_metrics(),
+        form_structure->form_signature());
+  }
+}
+
 }  // namespace
 
 AutofillManager::FillingContext::FillingContext() = default;
@@ -949,7 +962,7 @@
   FormStructure* cached_form = nullptr;
   ignore_result(FindCachedForm(form, &cached_form));
 
-  if (!ParseFormInternal(form, cached_form, &form_structure))
+  if (!ParseForm(form, cached_form, &form_structure))
     return;
 
   if (ShouldTriggerRefill(*form_structure))
@@ -1483,7 +1496,7 @@
   // Note: We _must not_ remove the original version of the cached form from
   // the list of |form_structures_|. Otherwise, we break parsing of the
   // crowdsourcing server's response to our query.
-  if (!ParseFormInternal(live_form, cached_form, updated_form))
+  if (!ParseForm(live_form, cached_form, updated_form))
     return false;
 
   // Annotate the updated form with its predicted types.
@@ -1571,8 +1584,10 @@
   std::vector<FormStructure*> queryable_forms;
   std::set<FormType> form_types;
   for (FormStructure* form_structure : form_structures) {
-    form_structure->DetermineHeuristicTypes(client_->GetUkmRecorder(),
-                                            client_->GetUkmSourceId());
+    // TODO(crbug.com/869482): avoid logging developer engagement multiple
+    // times for a given form if it or other forms on the page are dynamic.
+    LogDeveloperEngagementUkm(client_->GetUkmRecorder(),
+                              client_->GetUkmSourceId(), form_structure);
     forms_loaded_timestamps_[form_structure->ToFormData()] = timestamp;
     std::set<FormType> current_form_types = form_structure->GetFormTypes();
     form_types.insert(current_form_types.begin(), current_form_types.end());
@@ -1649,18 +1664,6 @@
   }
 }
 
-bool AutofillManager::ParseFormInternal(const FormData& form,
-                                        const FormStructure* cached_form,
-                                        FormStructure** parsed_form_structure) {
-  if (ParseForm(form, cached_form, parsed_form_structure)) {
-    (*parsed_form_structure)
-        ->DetermineHeuristicTypes(client_->GetUkmRecorder(),
-                                  client_->GetUkmSourceId());
-    return true;
-  }
-  return false;
-}
-
 int AutofillManager::BackendIDToInt(const std::string& backend_id) const {
   if (!base::IsValidGUID(backend_id))
     return 0;
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 7733d20..649daf0 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -335,10 +335,6 @@
     SuppressReason suppress_reason = SuppressReason::kNotSuppressed;
   };
 
-  bool ParseFormInternal(const FormData& form,
-                         const FormStructure* cached_form,
-                         FormStructure** parsed_form_structure);
-
   // AutofillDownloadManager::Observer:
   void OnLoadedServerPredictions(
       std::string response,
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 4db3f8b..a117aa5 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -4128,8 +4128,7 @@
   // Simulate having seen this form on page load.
   // |form_structure| will be owned by |autofill_manager_|.
   TestFormStructure* form_structure = new TestFormStructure(form);
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
 
   // Similarly, a second form.
@@ -4149,8 +4148,7 @@
   form2.fields.push_back(field);
 
   TestFormStructure* form_structure2 = new TestFormStructure(form2);
-  form_structure2->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                           /*source_id=*/0);
+  form_structure2->DetermineHeuristicTypes();
   autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure2));
 
   AutofillQueryResponseContents response;
@@ -4202,8 +4200,7 @@
   // Simulate having seen this form on page load.
   // |form_structure| will be owned by |autofill_manager_|.
   TestFormStructure* form_structure = new TestFormStructure(form);
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
 
   AutofillQueryResponseContents response;
@@ -4256,8 +4253,7 @@
   // Simulate having seen this form on page load.
   // |form_structure| will be owned by |autofill_manager_|.
   TestFormStructure* form_structure = new TestFormStructure(form);
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
 
   AutofillQueryResponseContents response;
@@ -4324,8 +4320,7 @@
   // Simulate having seen this form on page load.
   // |form_structure| will be owned by |autofill_manager_|.
   TestFormStructure* form_structure = new TestFormStructure(form);
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   // Clear the heuristic types, and instead set the appropriate server types.
   std::vector<ServerFieldType> heuristic_types, server_types;
@@ -5964,8 +5959,7 @@
   form.fields.push_back(field);
 
   auto form_structure = std::make_unique<TestFormStructure>(form);
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   // Make sure the form can not be autofilled now.
   ASSERT_EQ(0u, form_structure->autofill_count());
   for (size_t idx = 0; idx < form_structure->field_count(); ++idx) {
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 8052cca1..84301a7 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -1924,8 +1924,7 @@
   std::unique_ptr<TestFormStructure> form_structure =
       std::make_unique<TestFormStructure>(form);
   TestFormStructure* form_structure_ptr = form_structure.get();
-  form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                          0 /* source_id */);
+  form_structure->DetermineHeuristicTypes();
   autofill_manager_->mutable_form_structures()->push_back(
       std::move(form_structure));
 
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 3c0c3300..ef9940f 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -183,8 +183,7 @@
                                                const char* exp_cc_month,
                                                const char* exp_cc_year) {
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     std::unique_ptr<CreditCard> imported_credit_card;
     EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
     ASSERT_TRUE(imported_credit_card);
@@ -294,8 +293,7 @@
   test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -332,8 +330,7 @@
   test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure));
 
   ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -362,8 +359,7 @@
                             "example@example.com", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -394,8 +390,7 @@
                             &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure));
 
   ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -415,8 +410,7 @@
                             "4111 1111 1111 1111", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure));
 
   ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -442,8 +436,7 @@
   test::CreateTestFormField("Country:", "country", "USA", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -470,8 +463,7 @@
                             &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -493,8 +485,7 @@
   test::CreateTestFormField("Country:", "country", "Gibraltar", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -534,8 +525,7 @@
   test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -574,8 +564,7 @@
   test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
   form.fields.push_back(field);
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -615,8 +604,7 @@
   form1.fields.push_back(field);
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure1));
 
   WaitForOnPersonalDataChanged();
@@ -652,8 +640,7 @@
   form2.fields.push_back(field);
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure2));
 
   WaitForOnPersonalDataChanged();
@@ -710,8 +697,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -785,8 +771,7 @@
 
   // Still able to do the import.
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -868,8 +853,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -923,8 +907,7 @@
   form1.fields.push_back(field);
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure1));
 
   WaitForOnPersonalDataChanged();
@@ -970,8 +953,7 @@
   form2.fields.push_back(field);
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure2));
 
   WaitForOnPersonalDataChanged();
@@ -1008,8 +990,7 @@
   form1.fields.push_back(field);
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure1));
 
   WaitForOnPersonalDataChanged();
@@ -1045,8 +1026,7 @@
   form2.fields.push_back(field);
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure2));
 
   WaitForOnPersonalDataChanged();
@@ -1090,8 +1070,7 @@
   form1.fields.push_back(field);
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure1));
 
   WaitForOnPersonalDataChanged();
@@ -1129,8 +1108,7 @@
   form2.fields.push_back(field);
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure2));
 
   WaitForOnPersonalDataChanged();
@@ -1166,8 +1144,7 @@
   form1.fields.push_back(field);
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure1));
 
   // Since no refresh is expected, reload the data from the database to make
@@ -1219,8 +1196,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   // Wait for the refresh, which in this case is a no-op.
@@ -1240,8 +1216,8 @@
   form.fields[0] = field;
 
   FormStructure form_structure2(form);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
+
   EXPECT_TRUE(ImportAddressProfiles(form_structure2));
 
   // Wait for the refresh, which in this case is a no-op.
@@ -1281,8 +1257,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure));
 
   // Since no refresh is expected, reload the data from the database to make
@@ -1321,8 +1296,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(ImportAddressProfiles(form_structure));
 
   WaitForOnPersonalDataChanged();
@@ -1369,8 +1343,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_FALSE(ImportAddressProfiles(form_structure));
 
   // Since no refresh is expected, reload the data from the database to make
@@ -1391,8 +1364,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   base::HistogramTester histogram_tester;
   EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1420,8 +1392,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   base::HistogramTester histogram_tester;
   EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1444,8 +1415,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   base::HistogramTester histogram_tester;
   EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1481,8 +1451,7 @@
   form.fields[2].option_contents = contents;
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   base::HistogramTester histogram_tester;
   EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1511,8 +1480,7 @@
                         "2999");
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
   ASSERT_TRUE(imported_credit_card);
@@ -1533,8 +1501,8 @@
   AddFullCreditCardForm(&form2, "", "5500 0000 0000 0004", "02", "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
+
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
   ASSERT_TRUE(imported_credit_card2);
@@ -1652,8 +1620,7 @@
   // The card should not be offered to be saved locally because the feature flag
   // is disabled.
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
   ASSERT_FALSE(imported_credit_card);
@@ -1682,8 +1649,7 @@
   // The card should not be offered to be saved locally because it only matches
   // the full server card.
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
   ASSERT_FALSE(imported_credit_card);
@@ -1696,8 +1662,7 @@
                         "2998");
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
   ASSERT_TRUE(imported_credit_card);
@@ -1720,8 +1685,7 @@
                         /* different year */ "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
   EXPECT_FALSE(imported_credit_card2);
@@ -1746,8 +1710,7 @@
                         "2998");
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
   ASSERT_TRUE(imported_credit_card);
@@ -1770,8 +1733,7 @@
                         /* different year */ "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_TRUE(ImportCreditCard(form_structure2,
                                /* should_return_local_card= */ true,
@@ -1799,8 +1761,8 @@
                         "2998");
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
+
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
   ASSERT_TRUE(imported_credit_card);
@@ -1822,8 +1784,7 @@
                         "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_FALSE(
       ImportCreditCard(form_structure2, false, &imported_credit_card2));
@@ -1850,8 +1811,7 @@
                         "2999");
 
   FormStructure form_structure1(form1);
-  form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure1.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
   ASSERT_TRUE(imported_credit_card);
@@ -1874,8 +1834,7 @@
                         "4111-1111-1111-1111", "01", "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure2.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
   EXPECT_FALSE(imported_credit_card2);
@@ -1900,8 +1859,7 @@
                         /* no year */ nullptr);
 
   FormStructure form_structure3(form3);
-  form_structure3.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure3.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card3;
   EXPECT_FALSE(
       ImportCreditCard(form_structure3, false, &imported_credit_card3));
@@ -1943,8 +1901,7 @@
                         /* different year */ "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
   EXPECT_FALSE(imported_credit_card);
@@ -1985,8 +1942,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
   EXPECT_FALSE(imported_credit_card);
@@ -2026,8 +1982,7 @@
                         /* different year */ "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
   ASSERT_FALSE(imported_credit_card);
@@ -2065,8 +2020,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2086,8 +2040,7 @@
                         "2999");
 
   FormStructure form_structure2(form2);
-  form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                          0 /* source_id */);
+  form_structure2.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card2;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure2, /*profile_autofill_enabled=*/true,
@@ -2124,8 +2077,7 @@
   test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
   form3.fields.push_back(field);
   FormStructure form_structure3(form3);
-  form_structure3.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                          0 /* source_id */);
+  form_structure3.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card3;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure3, /*profile_autofill_enabled=*/true,
@@ -2146,8 +2098,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2182,8 +2133,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2218,8 +2168,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2253,8 +2202,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2275,8 +2223,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2298,8 +2245,7 @@
                         "1999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2338,8 +2284,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2384,8 +2329,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2463,8 +2407,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   // Still returns true because the credit card import was successful.
   EXPECT_TRUE(form_data_importer_->ImportFormData(
@@ -2519,8 +2462,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/false,
@@ -2574,8 +2516,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_TRUE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2633,8 +2574,7 @@
                         "2999");
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2689,8 +2629,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
@@ -2737,8 +2676,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2781,8 +2719,7 @@
 
   base::HistogramTester histogram_tester;
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2829,8 +2766,7 @@
 
   base::HistogramTester histogram_tester;
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2877,8 +2813,7 @@
 
   base::HistogramTester histogram_tester;
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure,
@@ -2926,8 +2861,7 @@
 
   base::HistogramTester histogram_tester;
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   std::unique_ptr<CreditCard> imported_credit_card;
   EXPECT_FALSE(form_data_importer_->ImportFormData(
       form_structure, /*profile_autofill_enabled=*/true,
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 847da60..63e3e00c 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -44,7 +44,6 @@
 #include "components/autofill/core/common/form_field_data_predictions.h"
 #include "components/autofill/core/common/signatures_util.h"
 #include "components/security_state/core/security_state.h"
-#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "url/origin.h"
 
 namespace autofill {
@@ -353,7 +352,8 @@
       is_formless_checkout_(form.is_formless_checkout),
       all_fields_are_passwords_(!form.fields.empty()),
       is_signin_upload_(false),
-      passwords_were_revealed_(false) {
+      passwords_were_revealed_(false),
+      developer_engagement_metrics_(0) {
   // Copy the form fields.
   std::map<base::string16, size_t> unique_names;
   for (const FormFieldData& field : form.fields) {
@@ -381,8 +381,7 @@
 
 FormStructure::~FormStructure() {}
 
-void FormStructure::DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder,
-                                            ukm::SourceId source_id) {
+void FormStructure::DetermineHeuristicTypes() {
   const auto determine_heuristic_types_start_time = base::TimeTicks::Now();
 
   // First, try to detect field types based on each field's |autocomplete|
@@ -407,30 +406,23 @@
   UpdateAutofillCount();
   IdentifySections(has_author_specified_sections_);
 
-  int developer_engagement_metrics = 0;
+  developer_engagement_metrics_ = 0;
   if (IsAutofillable()) {
     AutofillMetrics::DeveloperEngagementMetric metric =
         has_author_specified_types_
             ? AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS
             : AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS;
-    developer_engagement_metrics |= 1 << metric;
+    developer_engagement_metrics_ |= 1 << metric;
     AutofillMetrics::LogDeveloperEngagementMetric(metric);
   }
 
   if (has_author_specified_upi_vpa_hint_) {
     AutofillMetrics::LogDeveloperEngagementMetric(
         AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT);
-    developer_engagement_metrics |=
+    developer_engagement_metrics_ |=
         1 << AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT;
   }
 
-  if (developer_engagement_metrics) {
-    AutofillMetrics::LogDeveloperEngagementUkm(
-        ukm_recorder, source_id, main_frame_origin().GetURL(),
-        IsCompleteCreditCardForm(), GetFormTypes(),
-        developer_engagement_metrics, form_signature());
-  }
-
   if (base::FeatureList::IsEnabled(kAutofillRationalizeFieldTypePredictions))
     RationalizeFieldTypePredictions();
 
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 3635b15..bcafb97 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -32,10 +32,6 @@
 class TimeTicks;
 }
 
-namespace ukm {
-class UkmRecorder;
-}
-
 namespace autofill {
 
 // Password attributes (whether a password has special symbols, numeric, etc.)
@@ -58,10 +54,8 @@
   virtual ~FormStructure();
 
   // Runs several heuristics against the form fields to determine their possible
-  // types. If |ukm_recorder| and |source_id| is specified, logs UKM for
-  // the form structure corresponding to the source mapped from the |source_id|.
-  void DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder,
-                               ukm::SourceId source_id);
+  // types.
+  void DetermineHeuristicTypes();
 
   // Encodes the proto |upload| request from this FormStructure.
   // In some cases, a |login_form_signature| is included as part of the upload.
@@ -306,6 +300,8 @@
   // - Name for Autofill of first field
   base::string16 GetIdentifierForRefill() const;
 
+  int developer_engagement_metrics() { return developer_engagement_metrics_; };
+
  private:
   friend class AutofillMergeTest;
   friend class FormStructureTest;
@@ -534,6 +530,11 @@
   // has no value, |password_length_vote_| should be ignored.
   size_t password_length_vote_;
 
+  // Used to record whether developer has used autocomplete markup or
+  // UPI-VPA hints, This is a bitmask of DeveloperEngagementMetric and set in
+  // DetermineHeuristicTypes().
+  int developer_engagement_metrics_;
+
   DISALLOW_COPY_AND_ASSIGN(FormStructure);
 };
 
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 8d54b05..b75e65c 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -96,8 +96,7 @@
     InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics,
                 enforce_min_fields);
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     return form_structure.IsAutofillable();
   }
 
@@ -210,8 +209,7 @@
 
   // Only text and select fields that are heuristically matched are counted.
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_EQ(3U, form_structure->autofill_count());
 
   // Add a field with should_autocomplete=false. This should not be considered a
@@ -223,8 +221,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_EQ(4U, form_structure->autofill_count());
 }
 
@@ -470,8 +467,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->ShouldBeParsed());
   EXPECT_EQ(3U, form_structure->autofill_count());
   EXPECT_EQ(NAME_FULL, form_structure->field(0)->Type().GetStorableType());
@@ -525,8 +521,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -583,8 +578,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->has_author_specified_types());
   EXPECT_TRUE(form_structure->has_author_specified_upi_vpa_hint());
@@ -630,8 +624,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -647,8 +640,7 @@
   form.is_form_tag = false;
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -689,8 +681,7 @@
   form.fields.push_back(field);
 
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -730,8 +721,7 @@
   form.fields.push_back(field);
 
   std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -767,8 +757,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
 }
@@ -806,8 +795,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
 }
@@ -825,8 +813,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
 }
@@ -867,8 +854,7 @@
   field.name = base::string16();
   form.fields.push_back(field);
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
 }
@@ -898,8 +884,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -939,8 +924,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
   EXPECT_TRUE(form_structure->ShouldBeUploaded());
@@ -979,8 +963,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
   EXPECT_TRUE(form_structure->ShouldBeUploaded());
@@ -1024,8 +1007,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   EXPECT_TRUE(form_structure->ShouldBeQueried());
 
@@ -1069,8 +1051,7 @@
         // Disabled.
         {});
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(2U, form_structure.field_count());
     ASSERT_EQ(0U, form_structure.autofill_count());
     EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1083,8 +1064,7 @@
   // Default configuration.
   {
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(2U, form_structure.field_count());
     ASSERT_EQ(0U, form_structure.autofill_count());
     EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1100,8 +1080,7 @@
     feature_list.InitAndDisableFeature(
         kAutofillEnforceMinRequiredFieldsForHeuristics);
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(2U, form_structure.field_count());
     ASSERT_EQ(2U, form_structure.autofill_count());
     EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
@@ -1150,8 +1129,7 @@
         // Disabled.
         {});
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(2U, form_structure.field_count());
     ASSERT_EQ(1U, form_structure.autofill_count());
     EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1167,8 +1145,7 @@
     feature_list.InitAndDisableFeature(
         kAutofillEnforceMinRequiredFieldsForHeuristics);
     FormStructure form_structure(form);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(2U, form_structure.field_count());
     ASSERT_EQ(2U, form_structure.autofill_count());
     EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
@@ -1190,8 +1167,7 @@
     FormData form_copy = form;
     form_copy.fields.pop_back();
     FormStructure form_structure(form_copy);
-    form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                           0 /* source_id */);
+    form_structure.DetermineHeuristicTypes();
     ASSERT_EQ(1U, form_structure.field_count());
     ASSERT_EQ(1U, form_structure.autofill_count());
     EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1231,8 +1207,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure.ShouldBeQueried());
   EXPECT_TRUE(form_structure.ShouldBeUploaded());
 }
@@ -1284,8 +1259,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure.IsAutofillable());
 
   // Expect the correct number of fields.
@@ -1330,8 +1304,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
 
   // Expect the correct number of fields.
   ASSERT_EQ(6U, form_structure.field_count());
@@ -1360,8 +1333,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
 
   // Expect the correct number of fields.
   ASSERT_EQ(2U, form_structure.field_count());
@@ -1398,8 +1370,7 @@
   form.fields.push_back(field);
 
   FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  form_structure.DetermineHeuristicTypes();
 
   // Expect the correct number of fields.
   ASSERT_EQ(4U, form_structure.field_count());
@@ -1462,8 +1433,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(10U, form_structure->field_count());
   ASSERT_EQ(9U, form_structure->autofill_count());
@@ -1529,8 +1499,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(7U, form_structure->field_count());
   ASSERT_EQ(6U, form_structure->autofill_count());
@@ -1595,8 +1564,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(8U, form_structure->field_count());
   ASSERT_EQ(7U, form_structure->autofill_count());
@@ -1653,8 +1621,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(6U, form_structure->field_count());
   ASSERT_EQ(5U, form_structure->autofill_count());
@@ -1714,8 +1681,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(7U, form_structure->field_count());
   ASSERT_EQ(5U, form_structure->autofill_count());
@@ -1762,8 +1728,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
   ASSERT_EQ(4U, form_structure->autofill_count());
@@ -1803,8 +1768,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   ASSERT_EQ(4U, form_structure->field_count());
   ASSERT_EQ(3U, form_structure->autofill_count());
 
@@ -1847,8 +1811,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
   EXPECT_EQ(4U, form_structure->autofill_count());
@@ -1886,8 +1849,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(3U, form_structure->field_count());
   ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1920,8 +1882,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(3U, form_structure->field_count());
   ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1987,8 +1948,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(11U, form_structure->field_count());
   ASSERT_EQ(11U, form_structure->autofill_count());
@@ -2037,8 +1997,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
   ASSERT_EQ(4U, form_structure->field_count());
   ASSERT_EQ(4U, form_structure->autofill_count());
@@ -2081,8 +2040,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -2136,8 +2094,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -2195,8 +2152,7 @@
   form.fields.push_back(field);
 
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
   EXPECT_TRUE(form_structure->IsAutofillable());
 
   // Expect the correct number of fields.
@@ -2379,8 +2335,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2566,8 +2521,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.label = ASCIIToUTF16("First Name");
@@ -2684,8 +2638,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2759,8 +2712,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2847,8 +2799,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -2918,8 +2869,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -3058,8 +3008,7 @@
   // Setting the form name which we expect to see in the upload.
   form.name = ASCIIToUTF16("myform");
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -3122,8 +3071,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -3197,8 +3145,7 @@
   std::vector<ServerFieldTypeSet> possible_field_types;
   FormData form;
   form_structure.reset(new FormStructure(form));
-  form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                          /*source_id=*/0);
+  form_structure->DetermineHeuristicTypes();
 
   FormFieldData field;
   field.form_control_type = "text";
@@ -4098,7 +4045,7 @@
   form_data.fields.push_back(field);
 
   FormStructure form(form_data);
-  form.DetermineHeuristicTypes(nullptr /* ukm_service */, 0 /* source_id */);
+  form.DetermineHeuristicTypes();
 
   // Setup the query response.
   AutofillQueryResponseContents response;
@@ -4225,8 +4172,7 @@
   FormStructure form_structure(form);
   std::vector<FormStructure*> forms;
   forms.push_back(&form_structure);
-  forms.front()->DetermineHeuristicTypes(nullptr /* ukm_service */,
-                                         0 /* source_id */);
+  forms.front()->DetermineHeuristicTypes();
 
   AutofillQueryResponseContents response;
   response.add_field()->set_overall_type_prediction(EMAIL_ADDRESS);
@@ -5143,8 +5089,7 @@
   std::vector<FormStructure*> forms;
   forms.push_back(&form_structure);
   // Will identify the sections based on the heuristics types.
-  form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                         /*source_id=*/0);
+  form_structure.DetermineHeuristicTypes();
 
   AutofillQueryResponseContents response;
   // Billing
@@ -5235,8 +5180,7 @@
   std::vector<FormStructure*> forms;
   forms.push_back(&form_structure);
   // Will identify the sections based on the heuristics types.
-  form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                         /*source_id=*/0);
+  form_structure.DetermineHeuristicTypes();
 
   AutofillQueryResponseContents response;
   response.add_field()->set_overall_type_prediction(NAME_FULL);
@@ -5335,8 +5279,7 @@
   forms.push_back(&form_structure);
 
   // Will identify the sections based on the heuristics types.
-  form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                         /*source_id=*/0);
+  form_structure.DetermineHeuristicTypes();
 
   AutofillQueryResponseContents response;
   response.add_field()->set_overall_type_prediction(NAME_FULL);
@@ -5599,9 +5542,7 @@
   forms.push_back(&form_structure);
 
   // Will identify the sections based on the heuristics types.
-  form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
-                                         /*source_id=*/0);
-
+  form_structure.DetermineHeuristicTypes();
   AutofillQueryResponseContents response;
   response.add_field()->set_overall_type_prediction(NAME_FULL);
   response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index e8951f6..01de117 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -390,6 +390,18 @@
   </message>
 
   <!-- Autofill/Wallet integration preferences -->
+  <if expr="not is_ios">
+    <then>
+      <message name="IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL" desc="Label for the checkbox that controls the Autofill/Payments integration feature used for Autofill Home. 'Google Pay' should not be translated as it is the product name. Sentence-Cased.">
+        Payment methods and addresses using Google Pay
+      </message>
+    </then>
+    <else>
+      <message name="IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL" desc="Label for the checkbox that controls the Autofill/Payments integration feature used for Autofill Home. 'Google Pay' should not be translated as it is the product name. Title-Cased.">
+        Payment Methods and Addresses Using Google Pay
+      </message>
+    </else>
+  </if>
   <message name="IDS_AUTOFILL_WALLET_MANAGEMENT_LINK_TEXT" desc="Text for link that allows users to see and edit their Wallet information." formatter_data="android_java">
     Edit
   </message>
diff --git a/components/feed/core/BUILD.gn b/components/feed/core/BUILD.gn
index f7b47dd..f2d06c4 100644
--- a/components/feed/core/BUILD.gn
+++ b/components/feed/core/BUILD.gn
@@ -22,6 +22,8 @@
     "feed_storage_database.h",
     "pref_names.cc",
     "pref_names.h",
+    "refresh_throttler.cc",
+    "refresh_throttler.h",
     "time_serialization.cc",
     "time_serialization.h",
     "user_classifier.cc",
@@ -69,6 +71,7 @@
     "feed_networking_host_unittest.cc",
     "feed_scheduler_host_unittest.cc",
     "feed_storage_database_unittest.cc",
+    "refresh_throttler_unittest.cc",
     "user_classifier_unittest.cc",
   ]
 
diff --git a/components/feed/core/feed_scheduler_host.cc b/components/feed/core/feed_scheduler_host.cc
index 30230d7..2626206 100644
--- a/components/feed/core/feed_scheduler_host.cc
+++ b/components/feed/core/feed_scheduler_host.cc
@@ -4,7 +4,6 @@
 
 #include "components/feed/core/feed_scheduler_host.h"
 
-#include <map>
 #include <string>
 #include <utility>
 #include <vector>
@@ -159,6 +158,19 @@
   if (eula_accepted_notifier_) {
     eula_accepted_notifier_->Init(this);
   }
+
+  throttlers_.emplace(
+      UserClassifier::UserClass::kRareNtpUser,
+      std::make_unique<RefreshThrottler>(
+          UserClassifier::UserClass::kRareNtpUser, profile_prefs_, clock_));
+  throttlers_.emplace(
+      UserClassifier::UserClass::kActiveNtpUser,
+      std::make_unique<RefreshThrottler>(
+          UserClassifier::UserClass::kActiveNtpUser, profile_prefs_, clock_));
+  throttlers_.emplace(UserClassifier::UserClass::kActiveSuggestionsConsumer,
+                      std::make_unique<RefreshThrottler>(
+                          UserClassifier::UserClass::kActiveSuggestionsConsumer,
+                          profile_prefs_, clock_));
 }
 
 FeedSchedulerHost::~FeedSchedulerHost() = default;
@@ -360,7 +372,13 @@
     return false;
   }
 
-  // TODO(skym): Check with throttler.
+  auto throttlerIter = throttlers_.find(user_class);
+  if (throttlerIter == throttlers_.end() ||
+      !throttlerIter->second->RequestQuota()) {
+    DVLOG(2) << "Throttler stopped refresh from trigger "
+             << static_cast<int>(trigger);
+    return false;
+  }
 
   switch (trigger) {
     case TriggerType::kNtpShown:
diff --git a/components/feed/core/feed_scheduler_host.h b/components/feed/core/feed_scheduler_host.h
index a1e2c2e..9a2c79d 100644
--- a/components/feed/core/feed_scheduler_host.h
+++ b/components/feed/core/feed_scheduler_host.h
@@ -9,9 +9,11 @@
 #include <set>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/feed/core/refresh_throttler.h"
 #include "components/feed/core/user_classifier.h"
 #include "components/web_resource/eula_accepted_notifier.h"
 
@@ -181,6 +183,11 @@
   bool time_until_first_shown_trigger_reported_ = false;
   bool time_until_first_foregrounded_trigger_reported_ = false;
 
+  // In the case the user transitions between user classes, hold onto a
+  // throttler for any situation.
+  base::flat_map<UserClassifier::UserClass, std::unique_ptr<RefreshThrottler>>
+      throttlers_;
+
   DISALLOW_COPY_AND_ASSIGN(FeedSchedulerHost);
 };
 
diff --git a/components/feed/core/feed_scheduler_host_unittest.cc b/components/feed/core/feed_scheduler_host_unittest.cc
index 7d5e4fba0..3ce5a5a 100644
--- a/components/feed/core/feed_scheduler_host_unittest.cc
+++ b/components/feed/core/feed_scheduler_host_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/feed/core/feed_scheduler_host.h"
 
+#include <algorithm>
 #include <string>
 #include <vector>
 
@@ -12,6 +13,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "components/feed/core/pref_names.h"
+#include "components/feed/core/refresh_throttler.h"
 #include "components/feed/core/time_serialization.h"
 #include "components/feed/core/user_classifier.h"
 #include "components/feed/feed_feature_list.h"
@@ -44,6 +46,7 @@
  protected:
   FeedSchedulerHostTest() : weak_factory_(this) {
     FeedSchedulerHost::RegisterProfilePrefs(profile_prefs_.registry());
+    RefreshThrottler::RegisterProfilePrefs(profile_prefs_.registry());
     UserClassifier::RegisterProfilePrefs(profile_prefs_.registry());
     local_state()->registry()->RegisterBooleanPref(::prefs::kEulaAccepted,
                                                    true);
@@ -874,4 +877,18 @@
   EXPECT_EQ(2, histogram_tester.GetBucketCount(forgroundedHistogram, 0));
 }
 
+TEST_F(FeedSchedulerHostTest, RefreshThrottler) {
+  variations::testing::VariationParamsManager variation_params(
+      kInterestFeedContentSuggestions.name,
+      {{"quota_SuggestionFetcherActiveNTPUser", "3"}},
+      {kInterestFeedContentSuggestions.name});
+  NewScheduler();
+
+  for (int i = 0; i < 5; i++) {
+    scheduler()->OnForegrounded();
+    ResetRefreshState(base::Time());
+    EXPECT_EQ(std::min(i + 1, 3), refresh_call_count());
+  }
+}
+
 }  // namespace feed
diff --git a/components/feed/core/pref_names.cc b/components/feed/core/pref_names.cc
index 1976a47d..4943986 100644
--- a/components/feed/core/pref_names.cc
+++ b/components/feed/core/pref_names.cc
@@ -12,6 +12,9 @@
 
 const char kLastFetchAttemptTime[] = "feed.last_fetch_attempt";
 
+const char kThrottlerRequestCount[] = "feed.refresh_throttler.count";
+const char kThrottlerRequestsDay[] = "feed.refresh_throttler.day";
+
 const char kUserClassifierAverageNTPOpenedPerHour[] =
     "feed.user_classifier.average_ntp_opened_per_hour";
 const char kUserClassifierAverageSuggestionsUsedPerHour[] =
diff --git a/components/feed/core/pref_names.h b/components/feed/core/pref_names.h
index cd61363..c67b1de 100644
--- a/components/feed/core/pref_names.h
+++ b/components/feed/core/pref_names.h
@@ -15,6 +15,12 @@
 // The pref name for the last time when a background fetch was attempted.
 extern const char kLastFetchAttemptTime[];
 
+// The pref name for today's count of RefreshThrottler requests, so far.
+extern const char kThrottlerRequestCount[];
+// The pref name for the current day for the counter of RefreshThrottler's
+// requests.
+extern const char kThrottlerRequestsDay[];
+
 // The pref name for the discounted average number of browsing sessions per hour
 // that involve opening a new NTP.
 extern const char kUserClassifierAverageNTPOpenedPerHour[];
diff --git a/components/feed/core/refresh_throttler.cc b/components/feed/core/refresh_throttler.cc
new file mode 100644
index 0000000..75c14e6
--- /dev/null
+++ b/components/feed/core/refresh_throttler.cc
@@ -0,0 +1,146 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feed/core/refresh_throttler.h"
+
+#include <limits>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/clock.h"
+#include "components/feed/core/pref_names.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace feed {
+
+namespace {
+
+// Values correspond to ntp_snippets::RequestStatus and histograms.xml
+enum class RequestStatus {
+  kObsolete1 = 0,
+  kQuotaGranted = 1,
+  kQuotaExceeded = 2,
+  kObsolete2 = 3,
+  kStatusCount = 4
+};
+
+// When adding a new type here, extend also the "RequestThrottlerTypes"
+// <histogram_suffixes> in histograms.xml with the |name| string. First value in
+// the pair is the name, second is the default requests per day.
+std::pair<std::string, int> GetThrottlerParams(
+    UserClassifier::UserClass user_class) {
+  switch (user_class) {
+    case UserClassifier::UserClass::kRareNtpUser:
+      return {"SuggestionFetcherRareNTPUser", 5};
+    case UserClassifier::UserClass::kActiveNtpUser:
+      return {"SuggestionFetcherActiveNTPUser", 20};
+    case UserClassifier::UserClass::kActiveSuggestionsConsumer:
+      return {"SuggestionFetcherActiveSuggestionsConsumer", 20};
+  }
+}
+
+}  // namespace
+
+RefreshThrottler::RefreshThrottler(UserClassifier::UserClass user_class,
+                                   PrefService* pref_service,
+                                   base::Clock* clock)
+    : pref_service_(pref_service), clock_(clock) {
+  DCHECK(pref_service);
+  DCHECK(clock);
+
+  std::pair<std::string, int> throttler_params = GetThrottlerParams(user_class);
+  name_ = throttler_params.first;
+  max_requests_per_day_ = base::GetFieldTrialParamByFeatureAsInt(
+      kInterestFeedContentSuggestions,
+      base::StringPrintf("quota_%s", name_.c_str()), throttler_params.second);
+
+  // Since the histogram names are dynamic, we cannot use the standard macros
+  // and we need to lookup the histograms, instead.
+  int status_count = static_cast<int>(RequestStatus::kStatusCount);
+  // Corresponds to UMA_HISTOGRAM_ENUMERATION(name, sample, |status_count|).
+  histogram_request_status_ = base::LinearHistogram::FactoryGet(
+      base::StringPrintf("NewTabPage.RequestThrottler.RequestStatus_%s",
+                         name_.c_str()),
+      1, status_count, status_count + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  // Corresponds to UMA_HISTOGRAM_COUNTS_100(name, sample).
+  histogram_per_day_ = base::Histogram::FactoryGet(
+      base::StringPrintf("NewTabPage.RequestThrottler.PerDay_%s",
+                         name_.c_str()),
+      1, 100, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+// static
+void RefreshThrottler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(prefs::kThrottlerRequestCount, 0);
+  registry->RegisterIntegerPref(prefs::kThrottlerRequestsDay, 0);
+}
+
+bool RefreshThrottler::RequestQuota() {
+  ResetCounterIfDayChanged();
+
+  // Increment |new_count| in a overflow safe fashion.
+  int new_count = GetCount();
+  if (new_count < std::numeric_limits<int>::max()) {
+    new_count++;
+  }
+  SetCount(new_count);
+  bool available = (new_count <= GetQuota());
+
+  histogram_request_status_->Add(
+      static_cast<int>(available ? RequestStatus::kQuotaGranted
+                                 : RequestStatus::kQuotaExceeded));
+
+  return available;
+}
+
+void RefreshThrottler::ResetCounterIfDayChanged() {
+  // Grant new quota on local midnight to spread out when clients that start
+  // making un-throttled requests to server.
+  int now_day = clock_->Now().LocalMidnight().since_origin().InDays();
+
+  if (!HasDay()) {
+    // The counter is used for the first time in this profile.
+    SetDay(now_day);
+  } else if (now_day != GetDay()) {
+    // Day has changed - report the number of requests from the previous day.
+    histogram_per_day_->Add(GetCount());
+    // Reset the counters.
+    SetCount(0);
+    SetDay(now_day);
+  }
+}
+
+int RefreshThrottler::GetQuota() const {
+  return max_requests_per_day_;
+}
+
+int RefreshThrottler::GetCount() const {
+  return pref_service_->GetInteger(prefs::kThrottlerRequestCount);
+}
+
+void RefreshThrottler::SetCount(int count) {
+  pref_service_->SetInteger(prefs::kThrottlerRequestCount, count);
+}
+
+int RefreshThrottler::GetDay() const {
+  return pref_service_->GetInteger(prefs::kThrottlerRequestsDay);
+}
+
+void RefreshThrottler::SetDay(int day) {
+  pref_service_->SetInteger(prefs::kThrottlerRequestsDay, day);
+}
+
+bool RefreshThrottler::HasDay() const {
+  return pref_service_->HasPrefPath(prefs::kThrottlerRequestsDay);
+}
+
+}  // namespace feed
diff --git a/components/feed/core/refresh_throttler.h b/components/feed/core/refresh_throttler.h
new file mode 100644
index 0000000..c659ad4
--- /dev/null
+++ b/components/feed/core/refresh_throttler.h
@@ -0,0 +1,78 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FEED_CORE_REFRESH_THROTTLER_H_
+#define COMPONENTS_FEED_CORE_REFRESH_THROTTLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/feed/core/user_classifier.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Clock;
+class HistogramBase;
+}  // namespace base
+
+namespace feed {
+
+// Counts requests to perform refreshes, compares them to a daily quota, and
+// reports them to UMA. In the application code, create one local instance for
+// each given throttler name, identified by the UserClass. Reports to the same
+// histograms that previous NTP implementation used:
+//  - "NewTabPage.RequestThrottler.RequestStatus_|name|" - status of each
+//  request;
+//  - "NewTabPage.RequestThrottler.PerDay_|name|" - the daily count of requests.
+class RefreshThrottler {
+ public:
+  RefreshThrottler(UserClassifier::UserClass user_class,
+                   PrefService* pref_service,
+                   base::Clock* clock);
+
+  // Registers profile prefs, called from browser_prefs.cc.
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  // Returns whether quota is available for another request, persists the usage
+  // of said quota, and reports this information to UMA.
+  bool RequestQuota();
+
+ private:
+  // Also emits the PerDay histogram if the day changed.
+  void ResetCounterIfDayChanged();
+
+  int GetQuota() const;
+  int GetCount() const;
+  void SetCount(int count);
+  int GetDay() const;
+  void SetDay(int day);
+  bool HasDay() const;
+
+  // Provides durable storage.
+  PrefService* pref_service_;
+
+  // Used to access current time, injected for testing.
+  base::Clock* clock_;
+
+  // The name used by this throttler, based off UserClass, which will be used as
+  // a suffix when constructing histogram or finch param names.
+  std::string name_;
+
+  // The total requests allowed before RequestQuota() starts returning false,
+  // reset every time |clock_| changes days. Read from a variation param during
+  // initialization.
+  int max_requests_per_day_;
+
+  // The histograms for reporting the requests of the given |type_|.
+  base::HistogramBase* histogram_request_status_;
+  base::HistogramBase* histogram_per_day_;
+
+  DISALLOW_COPY_AND_ASSIGN(RefreshThrottler);
+};
+
+}  // namespace feed
+
+#endif  // COMPONENTS_FEED_CORE_REFRESH_THROTTLER_H_
diff --git a/components/feed/core/refresh_throttler_unittest.cc b/components/feed/core/refresh_throttler_unittest.cc
new file mode 100644
index 0000000..d756d1600
--- /dev/null
+++ b/components/feed/core/refresh_throttler_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feed/core/refresh_throttler.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/test/simple_test_clock.h"
+#include "components/feed/core/pref_names.h"
+#include "components/feed/core/user_classifier.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feed {
+
+class RefreshThrottlerTest : public testing::Test {
+ public:
+  RefreshThrottlerTest() {
+    RefreshThrottler::RegisterProfilePrefs(test_prefs_.registry());
+    test_clock_.SetNow(base::Time::Now());
+
+    variations::testing::VariationParamsManager variation_params(
+        kInterestFeedContentSuggestions.name,
+        {{"quota_SuggestionFetcherActiveNTPUser", "2"}},
+        {kInterestFeedContentSuggestions.name});
+
+    throttler_ = std::make_unique<RefreshThrottler>(
+        UserClassifier::UserClass::kActiveNtpUser, &test_prefs_, &test_clock_);
+  }
+
+ protected:
+  TestingPrefServiceSimple test_prefs_;
+  base::SimpleTestClock test_clock_;
+  std::unique_ptr<RefreshThrottler> throttler_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RefreshThrottlerTest);
+};
+
+TEST_F(RefreshThrottlerTest, QuotaExceeded) {
+  EXPECT_TRUE(throttler_->RequestQuota());
+  EXPECT_TRUE(throttler_->RequestQuota());
+  EXPECT_FALSE(throttler_->RequestQuota());
+}
+
+TEST_F(RefreshThrottlerTest, QuotaIsPerDay) {
+  EXPECT_TRUE(throttler_->RequestQuota());
+  EXPECT_TRUE(throttler_->RequestQuota());
+  EXPECT_FALSE(throttler_->RequestQuota());
+
+  test_clock_.Advance(base::TimeDelta::FromDays(1));
+  EXPECT_TRUE(throttler_->RequestQuota());
+}
+
+TEST_F(RefreshThrottlerTest, RollOver) {
+  // Exhaust our quota so the for loop can verify everything as false.
+  EXPECT_TRUE(throttler_->RequestQuota());
+  EXPECT_TRUE(throttler_->RequestQuota());
+
+  test_clock_.SetNow(test_clock_.Now().LocalMidnight());
+  for (int i = 0; i < 24; i++) {
+    EXPECT_FALSE(throttler_->RequestQuota());
+    test_clock_.Advance(base::TimeDelta::FromHours(1));
+  }
+  EXPECT_TRUE(throttler_->RequestQuota());
+}
+
+TEST_F(RefreshThrottlerTest, Overflow) {
+  test_prefs_.SetInteger(prefs::kThrottlerRequestCount,
+                         std::numeric_limits<int>::max());
+  EXPECT_FALSE(throttler_->RequestQuota());
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            test_prefs_.GetInteger(prefs::kThrottlerRequestCount));
+}
+
+}  // namespace feed
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index 9be64496..4b2eb536 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -31,6 +31,10 @@
 
 namespace {
 
+// The maximum number of custom links that can be shown. This is independent of
+// the maximum number of Most Visited sites that can be shown.
+const size_t kMaxNumCustomLinks = 10;
+
 const base::Feature kDisplaySuggestionsServiceTiles{
     "DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -602,7 +606,7 @@
   DCHECK(IsCustomLinksEnabled());
 
   NTPTilesVector tiles;
-  size_t num_tiles = std::min(links.size(), max_num_sites_);
+  size_t num_tiles = std::min(links.size(), kMaxNumCustomLinks);
   for (size_t i = 0; i < num_tiles; ++i) {
     const CustomLinksManager::Link& link = links.at(i);
     if (supervisor_ && supervisor_->IsBlocked(link.url))
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
index 43ef930..b0258ce 100644
--- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
+++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
@@ -18,6 +18,7 @@
 #include "base/task_runner_util.h"
 #include "base/values.h"
 #include "components/favicon/core/favicon_service.h"
+#include "components/ntp_tiles/constants.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/ntp_tiles/pref_names.h"
 #include "components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h"
@@ -58,7 +59,10 @@
     favicon::FaviconService* favicon_service)
     : favicon_service_(favicon_service),
       client_(nullptr),
-      site_count_(8),
+      // 9 tiles are required for the custom links feature in order to balance
+      // the Most Visited rows (this is due to an additional "Add" button).
+      // Otherwise, Most Visited should return the regular 8 tiles.
+      site_count_(IsCustomLinksEnabled() ? 9 : 8),
       weak_ptr_factory_(this) {}
 
 NTPTilesInternalsMessageHandler::~NTPTilesInternalsMessageHandler() = default;
diff --git a/components/nux_google_apps/google_apps_handler.cc b/components/nux_google_apps/google_apps_handler.cc
index d07cb26..c9b2a624 100644
--- a/components/nux_google_apps/google_apps_handler.cc
+++ b/components/nux_google_apps/google_apps_handler.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
@@ -22,6 +23,21 @@
 
 namespace nux_google_apps {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GoogleApps {
+  kGmail = 0,
+  kYouTube = 1,
+  kMaps = 2,
+  kTranslate = 3,
+  kNews = 4,
+  kChromeWebStore = 5,
+  kCount,
+};
+
+const char* kGoogleAppsInteractionHistogram =
+    "FirstRun.NewUserExperience.GoogleAppsInteraction";
+
 // Strings in costants not translated because this is an experiment.
 // Translate before wide release.
 
@@ -37,8 +53,8 @@
 
 static_assert(base::size(kGoogleAppNames) == base::size(kGoogleAppUrls),
               "names and urls must match");
-
-constexpr const size_t kGoogleAppCount = 6;
+static_assert(base::size(kGoogleAppNames) == (size_t)GoogleApps::kCount,
+              "names and histograms must match");
 
 GoogleAppsHandler::GoogleAppsHandler(PrefService* prefs,
                                      bookmarks::BookmarkModel* bookmark_model)
@@ -48,18 +64,32 @@
 
 void GoogleAppsHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
+      "rejectGoogleApps",
+      base::BindRepeating(&GoogleAppsHandler::HandleRejectGoogleApps,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
       "addGoogleApps",
-      base::BindRepeating(&GoogleAppsHandler::HandleAddBookmarks,
+      base::BindRepeating(&GoogleAppsHandler::HandleAddGoogleApps,
                           base::Unretained(this)));
 }
 
-void GoogleAppsHandler::HandleAddBookmarks(const base::ListValue* args) {
+void GoogleAppsHandler::HandleRejectGoogleApps(const base::ListValue* args) {
+  UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
+                            GoogleAppsInteraction::kNoThanks,
+                            GoogleAppsInteraction::kCount);
+}
+
+void GoogleAppsHandler::HandleAddGoogleApps(const base::ListValue* args) {
   // Add bookmarks for all selected apps.
   int bookmarkIndex = 0;
-  for (size_t i = 0; i < kGoogleAppCount; ++i) {
+  for (size_t i = 0; i < (size_t)GoogleApps::kCount; ++i) {
     bool selected = false;
     CHECK(args->GetBoolean(i, &selected));
     if (selected) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "FirstRun.NewUserExperience.GoogleAppsSelection", (GoogleApps)i,
+          GoogleApps::kCount);
       bookmark_model_->AddURL(
           bookmark_model_->bookmark_bar_node(), bookmarkIndex++,
           base::ASCIIToUTF16(kGoogleAppNames[i]), GURL(kGoogleAppUrls[i]));
@@ -75,6 +105,10 @@
   // Show bookmark bubble.
   ShowPromoDelegate::CreatePromoDelegate()->ShowForNode(
       bookmark_model_->bookmark_bar_node()->GetChild(0));
+
+  UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
+                            GoogleAppsInteraction::kGetStarted,
+                            GoogleAppsInteraction::kCount);
 }
 
 void GoogleAppsHandler::AddSources(content::WebUIDataSource* html_source) {
diff --git a/components/nux_google_apps/google_apps_handler.h b/components/nux_google_apps/google_apps_handler.h
index 7b6245d..c9c6ecef 100644
--- a/components/nux_google_apps/google_apps_handler.h
+++ b/components/nux_google_apps/google_apps_handler.h
@@ -21,6 +21,17 @@
 
 namespace nux_google_apps {
 
+extern const char* kGoogleAppsInteractionHistogram;
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GoogleAppsInteraction {
+  kPromptShown = 0,
+  kNoThanks = 1,
+  kGetStarted = 2,
+  kCount,
+};
+
 class GoogleAppsHandler : public content::WebUIMessageHandler {
  public:
   GoogleAppsHandler(PrefService* prefs,
@@ -30,8 +41,9 @@
   // WebUIMessageHandler:
   void RegisterMessages() override;
 
-  // Callback for JS API that will add bookmarks.
-  void HandleAddBookmarks(const base::ListValue* args);
+  // Callbacks for JS APIs.
+  void HandleRejectGoogleApps(const base::ListValue* args);
+  void HandleAddGoogleApps(const base::ListValue* args);
 
   // Adds webui sources.
   static void AddSources(content::WebUIDataSource* html_source);
diff --git a/components/nux_google_apps/resources/nux_google_apps.js b/components/nux_google_apps/resources/nux_google_apps.js
index ee8bedf9..911ec744 100644
--- a/components/nux_google_apps/resources/nux_google_apps.js
+++ b/components/nux_google_apps/resources/nux_google_apps.js
@@ -12,6 +12,7 @@
 
   /** @private */
   onNoThanksClicked_: function() {
+    chrome.send('rejectGoogleApps');
     window.location.replace('chrome://newtab');
   },
 
diff --git a/components/offline_items_collection/core/offline_content_aggregator.cc b/components/offline_items_collection/core/offline_content_aggregator.cc
index d55c746..18dcc4f 100644
--- a/components/offline_items_collection/core/offline_content_aggregator.cc
+++ b/components/offline_items_collection/core/offline_content_aggregator.cc
@@ -35,6 +35,7 @@
 void OfflineContentAggregator::RegisterProvider(
     const std::string& name_space,
     OfflineContentProvider* provider) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Validate that this is the first OfflineContentProvider registered that is
   // associated with |name_space|.
   DCHECK(providers_.find(name_space) == providers_.end());
@@ -49,6 +50,7 @@
 
 void OfflineContentAggregator::UnregisterProvider(
     const std::string& name_space) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto provider_it = providers_.find(name_space);
 
   OfflineContentProvider* provider = provider_it->second;
@@ -63,6 +65,7 @@
 }
 
 void OfflineContentAggregator::OpenItem(const ContentId& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end())
@@ -72,6 +75,7 @@
 }
 
 void OfflineContentAggregator::RemoveItem(const ContentId& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end())
@@ -81,6 +85,7 @@
 }
 
 void OfflineContentAggregator::CancelDownload(const ContentId& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end())
@@ -90,6 +95,7 @@
 }
 
 void OfflineContentAggregator::PauseDownload(const ContentId& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end())
@@ -100,6 +106,7 @@
 
 void OfflineContentAggregator::ResumeDownload(const ContentId& id,
                                               bool has_user_gesture) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end())
@@ -110,6 +117,7 @@
 
 void OfflineContentAggregator::GetItemById(const ContentId& id,
                                            SingleItemCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
   if (it == providers_.end()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -125,10 +133,12 @@
 void OfflineContentAggregator::OnGetItemByIdDone(
     SingleItemCallback callback,
     const base::Optional<OfflineItem>& item) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::move(callback).Run(item);
 }
 
 void OfflineContentAggregator::GetAllItems(MultipleItemCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // If there is already a call in progress, queue up the callback and wait for
   // the results.
   if (!multiple_item_get_callbacks_.empty()) {
@@ -158,6 +168,7 @@
 void OfflineContentAggregator::OnGetAllItemsDone(
     OfflineContentProvider* provider,
     const OfflineItemList& items) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   aggregated_items_.insert(aggregated_items_.end(), items.begin(), items.end());
   pending_providers_.erase(provider);
   if (!pending_providers_.empty()) {
@@ -174,6 +185,7 @@
 void OfflineContentAggregator::GetVisualsForItem(
     const ContentId& id,
     const VisualsCallback& callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = providers_.find(id.name_space);
 
   if (it == providers_.end()) {
@@ -187,6 +199,7 @@
 
 void OfflineContentAggregator::AddObserver(
     OfflineContentProvider::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(observer);
   if (observers_.HasObserver(observer))
     return;
@@ -196,6 +209,7 @@
 
 void OfflineContentAggregator::RemoveObserver(
     OfflineContentProvider::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(observer);
   if (!observers_.HasObserver(observer))
     return;
@@ -204,16 +218,19 @@
 }
 
 void OfflineContentAggregator::OnItemsAdded(const OfflineItemList& items) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (auto& observer : observers_)
     observer.OnItemsAdded(items);
 }
 
 void OfflineContentAggregator::OnItemRemoved(const ContentId& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (auto& observer : observers_)
     observer.OnItemRemoved(id);
 }
 
 void OfflineContentAggregator::OnItemUpdated(const OfflineItem& item) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (auto& observer : observers_)
     observer.OnItemUpdated(item);
 }
diff --git a/components/offline_items_collection/core/offline_content_aggregator.h b/components/offline_items_collection/core/offline_content_aggregator.h
index 144ac00..f9f4d8e 100644
--- a/components/offline_items_collection/core/offline_content_aggregator.h
+++ b/components/offline_items_collection/core/offline_content_aggregator.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/sequence_checker.h"
 #include "base/supports_user_data.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/offline_items_collection/core/offline_content_provider.h"
@@ -31,6 +32,8 @@
 //   created by the provider must also be tagged with the same namespace so that
 //   actions taken on the OfflineItem can be routed to the correct internal
 //   provider.  The namespace must also be consistent across startups.
+//
+// Methods on OfflineContentAggregator should be called from the UI thread.
 class OfflineContentAggregator : public OfflineContentProvider,
                                  public OfflineContentProvider::Observer,
                                  public base::SupportsUserData,
@@ -92,6 +95,8 @@
   // A list of all currently registered observers.
   base::ObserverList<OfflineContentProvider::Observer> observers_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<OfflineContentAggregator> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(OfflineContentAggregator);
diff --git a/components/offline_pages/core/task_queue.cc b/components/offline_pages/core/task_queue.cc
index 93bc953c..096412c 100644
--- a/components/offline_pages/core/task_queue.cc
+++ b/components/offline_pages/core/task_queue.cc
@@ -17,6 +17,7 @@
 TaskQueue::~TaskQueue() {}
 
 void TaskQueue::AddTask(std::unique_ptr<Task> task) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   task->SetTaskCompletionCallback(
       base::ThreadTaskRunnerHandle::Get(),
       base::BindOnce(&TaskQueue::TaskCompleted,
@@ -26,14 +27,17 @@
 }
 
 bool TaskQueue::HasPendingTasks() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return !tasks_.empty() || HasRunningTask();
 }
 
 bool TaskQueue::HasRunningTask() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return current_task_ != nullptr;
 }
 
 void TaskQueue::StartTaskIfAvailable() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DVLOG(2) << "running? " << HasRunningTask() << ", pending? "
            << HasPendingTasks() << " " << __func__;
   if (HasRunningTask())
@@ -52,6 +56,7 @@
 }
 
 void TaskQueue::TaskCompleted(Task* task) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(task, current_task_.get());
   if (task == current_task_.get()) {
     current_task_.reset(nullptr);
@@ -60,6 +65,7 @@
 }
 
 void TaskQueue::InformTaskQueueIsIdle() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   delegate_->OnTaskQueueIsIdle();
 }
 
diff --git a/components/offline_pages/core/task_queue.h b/components/offline_pages/core/task_queue.h
index 20ae12df..19de13a 100644
--- a/components/offline_pages/core/task_queue.h
+++ b/components/offline_pages/core/task_queue.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "components/offline_pages/core/task.h"
 
 namespace offline_pages {
@@ -27,6 +28,9 @@
 // Consumers of this class should create an instance of TaskQueue and implement
 // tasks that need to be run sequentially. New task will only be started when
 // the previous one calls |Task::TaskComplete|.
+//
+// Methods on TaskQueue should be called from the same thread from which it
+// is created.
 class TaskQueue {
  public:
   class Delegate {
@@ -67,6 +71,8 @@
   // A FIFO queue of tasks that will be run using this task queue.
   base::queue<std::unique_ptr<Task>> tasks_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<TaskQueue> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TaskQueue);
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index 291bd6d7..196892ea 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -171,6 +171,8 @@
 
 // Enterprise controls for auto-update behavior of Chrome OS.
 message AutoUpdateSettingsProto {
+  reserved 13;
+
   // True if we don't want the device to auto-update (target_version_prefix is
   // ignored in this case).
   optional bool update_disabled = 1;
@@ -268,12 +270,11 @@
   // "DeviceAutoUpdateTimeRestrictions" in policy_templates.json.
   optional string disallowed_time_intervals = 12;
 
-  // Specifies the percent of the fleet to update per week. Each week is
-  // represented by an int in the list. For example, a list with values
-  // [5,20,100] means: by the end of week 1, 5% of devices should be updated. By
-  // the end of week 2, 20% of devices should be updated. By the end of week 3,
-  // 100% of devices should be updated.
-  repeated int32 staging_percent_of_fleet_per_week = 13;
+  // Specifies how much of the fleet to update per day as a json
+  // string that contains a list of pairs <day, percentage>. For more
+  // details and examples, see "DeviceUpdateStagingSchedule" in
+  // policy_templates.json.
+  optional string staging_schedule = 14;
 }
 
 message OBSOLETE_StartUpUrlsProto {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 6b13418..e0b2d788 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -12482,24 +12482,42 @@
       '''
     },
     {
-      'name': 'DeviceUpdateStagingPercentOfFleetPerWeek',
-      'type': 'list',
+      'name': 'DeviceUpdateStagingSchedule',
+      'type': 'dict',
       'schema': {
         'type': 'array',
-        'items': { 'type': 'integer' },
+        'items': {
+          'type': 'object',
+          'description': 'Contains the number of days and the percentage of the fleet that should be updated after those days have passed.',
+          'id': 'DayPercentagePair',
+          'properties': {
+            'days': {
+              'description': 'Days from update discovery.',
+              'type': 'integer',
+              'minimum': 1,
+              'maximum': 28,
+            },
+            'percentage': {
+              'description': 'Percentage of the fleet that should be updated after the given days.',
+              'type': 'integer',
+              'minimum': 0,
+              'maximum': 100,
+            }
+          }
+        },
       },
       'supported_on': ['chrome_os:69-'],
       'device_only': True,
       'features': {
         'dynamic_refresh': True,
       },
-      'example_value': [ 0, 20, 100 ],
+      'example_value': [{'days': 7, 'percentage': 50}, {'days': 10, 'percentage': 100}],
       'id': 458,
       'caption': '''The staging schedule for applying a new update''',
       'tags': ['system-security'],
-      'desc': '''This policy defines a list of percentages that will define the fraction of <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices in the OU to update per week starting from the day the update is first discovered. The discovery time is later than the update published time, since it could be a while after the update publishing until the device checks for updates.
+      'desc': '''This policy defines a list of percentages that will define the fraction of <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices in the OU to update per day starting from the day the update is first discovered. The discovery time is later than the update published time, since it could be a while after the update publishing until the device checks for updates.
 
-      The n-th value in the list will be used as the percentage of devices that should complete updating to the next version in the n-th week after the update has been discovered. For example, if an update is discovered today, then the first value in the list defines the percentage of devices in the OU that should update to that version by a week from today. The second value defines the percentage of devices in the OU that should update to that version by 2 weeks from today, and so on.
+      Each (day, percentage) pair contains which percentage of the fleet has to be updated by the given number of days since the update has been discovered. For example, if we have the pairs [(4, 40), (10, 70), (15, 100)], then 40% of the fleet should have been updated 4 days after seeing the update. 70% should be updated after 10 days, and so on.
 
       If there is a value defined for this policy, updates will ignore the <ph name="DEVICE_UPDATE_SCATTER_FACTOR_POLICY_NAME">DeviceUpdateScatterFactor</ph> policy and follow this policy instead.
 
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer.cc b/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
index e6f7a02..d896989 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
@@ -11,6 +11,7 @@
 #include "base/files/file.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/task_runner_util.h"
 #include "base/trace_event/trace_event.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
@@ -40,7 +41,7 @@
 
 void VerifiedRulesetDealer::SetRulesetFile(base::File ruleset_file) {
   RulesetDealer::SetRulesetFile(std::move(ruleset_file));
-  status_ = RulesetVerificationStatus::NOT_VERIFIED;
+  status_ = RulesetVerificationStatus::kNotVerified;
 }
 
 scoped_refptr<const MemoryMappedRuleset> VerifiedRulesetDealer::GetRuleset() {
@@ -48,25 +49,31 @@
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
                "VerifiedRulesetDealer::GetRuleset");
 
-  // TODO(pkalinnikov): Record verification status to a histogram.
   switch (status_) {
-    case RulesetVerificationStatus::NOT_VERIFIED: {
+    case RulesetVerificationStatus::kNotVerified: {
       auto ruleset = RulesetDealer::GetRuleset();
-      if (ruleset &&
-          IndexedRulesetMatcher::Verify(ruleset->data(), ruleset->length())) {
-        status_ = RulesetVerificationStatus::INTACT;
-        return ruleset;
+      if (ruleset) {
+        if (IndexedRulesetMatcher::Verify(ruleset->data(), ruleset->length())) {
+          status_ = RulesetVerificationStatus::kIntact;
+        } else {
+          status_ = RulesetVerificationStatus::kCorrupt;
+          ruleset.reset();
+        }
+      } else {
+        status_ = RulesetVerificationStatus::kInvalidFile;
       }
-      status_ = RulesetVerificationStatus::CORRUPT;
-      return nullptr;
+      UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.RulesetVerificationStatus",
+                                status_);
+      return ruleset;
     }
-    case RulesetVerificationStatus::INTACT: {
+    case RulesetVerificationStatus::kIntact: {
       auto ruleset = RulesetDealer::GetRuleset();
       // Note, |ruleset| can still be nullptr here if mmap fails, despite the
       // fact that it must have succeeded previously.
       return ruleset;
     }
-    case RulesetVerificationStatus::CORRUPT:
+    case RulesetVerificationStatus::kCorrupt:
+    case RulesetVerificationStatus::kInvalidFile:
       return nullptr;
     default:
       NOTREACHED();
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer.h b/components/subresource_filter/content/browser/verified_ruleset_dealer.h
index bdde4822..bff9370 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer.h
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer.h
@@ -25,19 +25,24 @@
 
 // The integrity verification status of a given ruleset version.
 //
-// A ruleset file starts from the NOT_VERIFIED state, after which it can be
-// classified as INTACT or CORRUPT upon integrity verification.
+// A ruleset file starts from the kNotVerified state, after which it can be
+// classified as kIntact, kCorrupt, or kInvalidFile upon integrity verification.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
 enum class RulesetVerificationStatus {
-  NOT_VERIFIED,
-  INTACT,
-  CORRUPT,
+  kNotVerified = 0,
+  kIntact = 1,
+  kCorrupt = 2,
+  kInvalidFile = 3,
+  kMaxValue = kInvalidFile,
 };
 
 // This class is the same as RulesetDealer, but additionally does a one-time
 // integrity checking on the ruleset before handing it out from GetRuleset().
 //
 // The |status| of verification is persisted throughout the entire lifetime of
-// |this| object, and is reset to NOT_VERIFIED only when a new ruleset is
+// |this| object, and is reset to kNotVerified only when a new ruleset is
 // supplied to SetRulesetFile() method.
 class VerifiedRulesetDealer : public RulesetDealer {
  public:
@@ -60,7 +65,7 @@
   RulesetVerificationStatus status() const { return status_; }
 
  private:
-  RulesetVerificationStatus status_ = RulesetVerificationStatus::NOT_VERIFIED;
+  RulesetVerificationStatus status_ = RulesetVerificationStatus::kNotVerified;
 
   DISALLOW_COPY_AND_ASSIGN(VerifiedRulesetDealer);
 };
diff --git a/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc b/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
index 5862fbe..6916280 100644
--- a/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
+++ b/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/files/file.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
@@ -100,6 +101,9 @@
 // MemoryMappedRuleset, its lazy creation, etc., are covered with tests to
 // RulesetDealer, therefore these aspects are not tested here.
 
+constexpr char kVerificationHistogram[] =
+  "SubresourceFilter.RulesetVerificationStatus";
+
 class SubresourceFilterVerifiedRulesetDealerTest : public ::testing::Test {
  public:
   SubresourceFilterVerifiedRulesetDealerTest() = default;
@@ -117,9 +121,14 @@
     return ruleset_dealer_->has_cached_ruleset();
   }
 
+  const base::HistogramTester& histogram_tester() const {
+    return histogram_tester_;
+  }
+
  private:
   TestRulesets rulesets_;
   std::unique_ptr<VerifiedRulesetDealer> ruleset_dealer_;
+  base::HistogramTester histogram_tester_;
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetDealerTest);
 };
@@ -131,7 +140,7 @@
 
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
             ruleset_dealer()->status());
 
   scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
@@ -140,7 +149,9 @@
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_TRUE(ref_to_ruleset);
   EXPECT_TRUE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::INTACT, ruleset_dealer()->status());
+  EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kIntact, 1);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -152,7 +163,7 @@
 
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
             ruleset_dealer()->status());
 
   scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
@@ -161,12 +172,29 @@
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(ref_to_ruleset);
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::CORRUPT, ruleset_dealer()->status());
+  EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kCorrupt, 1);
+}
+
+TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailureInitial) {
+  ruleset_dealer()->SetRulesetFile(
+      testing::TestRuleset::Open(rulesets().indexed_1()));
+
+  MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
+  EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
+  MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
+  EXPECT_FALSE(has_cached_ruleset());
+  EXPECT_EQ(RulesetVerificationStatus::kInvalidFile,
+            ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kInvalidFile,
+                                        1);
 }
 
 // This is a duplicated test from RulesetDealer, to ensure that verification
 // doesn't introduce any bad assumptions about mmap failures.
-TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailure) {
+TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailureSubsequent) {
   ruleset_dealer()->SetRulesetFile(
       testing::TestRuleset::Open(rulesets().indexed_1()));
   {
@@ -181,11 +209,23 @@
     // is still around.
     EXPECT_TRUE(ruleset_dealer()->has_cached_ruleset());
     EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+    EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
+    histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                          RulesetVerificationStatus::kIntact,
+                                          1);
   }
   EXPECT_FALSE(ruleset_dealer()->has_cached_ruleset());
   EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
+  EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kIntact,
+                                        1);
   MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
   EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+  EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kIntact,
+                                        1);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -199,7 +239,9 @@
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(ref_to_ruleset);
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::CORRUPT, ruleset_dealer()->status());
+  EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kCorrupt, 1);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -215,7 +257,9 @@
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(ref_to_ruleset);
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::CORRUPT, ruleset_dealer()->status());
+  EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kCorrupt, 1);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -227,7 +271,7 @@
 
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
             ruleset_dealer()->status());
 
   scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
@@ -236,18 +280,24 @@
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(ref_to_ruleset);
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::CORRUPT, ruleset_dealer()->status());
-
+  EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
+  histogram_tester().ExpectUniqueSample(kVerificationHistogram,
+                                        RulesetVerificationStatus::kCorrupt, 1);
   ruleset_dealer()->SetRulesetFile(
       testing::TestRuleset::Open(rulesets().indexed_2()));
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
             ruleset_dealer()->status());
   ref_to_ruleset = ruleset_dealer()->GetRuleset();
 
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_TRUE(ref_to_ruleset);
   EXPECT_TRUE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::INTACT, ruleset_dealer()->status());
+  EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
+  histogram_tester().ExpectTotalCount(kVerificationHistogram, 2);
+  histogram_tester().ExpectBucketCount(kVerificationHistogram,
+                                       RulesetVerificationStatus::kCorrupt, 1);
+  histogram_tester().ExpectBucketCount(kVerificationHistogram,
+                                       RulesetVerificationStatus::kIntact, 1);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -262,8 +312,9 @@
   // Check |OpenAndSetRulesetFile| forwards call to |SetRulesetFile| on success.
   EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
   EXPECT_FALSE(has_cached_ruleset());
-  EXPECT_EQ(RulesetVerificationStatus::NOT_VERIFIED,
+  EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
             ruleset_dealer()->status());
+  histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
@@ -273,6 +324,7 @@
 
   EXPECT_FALSE(file.IsValid());
   EXPECT_FALSE(ruleset_dealer()->IsRulesetFileAvailable());
+  histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
 }
 
 // Tests for VerifiedRulesetDealer::Handle. ------------------------------------
@@ -290,7 +342,7 @@
 
   void ExpectRulesetState(bool expected_availability,
                           RulesetVerificationStatus expected_status =
-                              RulesetVerificationStatus::NOT_VERIFIED,
+                              RulesetVerificationStatus::kNotVerified,
                           bool expected_cached = false) const {
     ASSERT_EQ(1, invocation_counter_);
     EXPECT_EQ(expected_availability, is_ruleset_file_available_);
@@ -300,7 +352,7 @@
 
   void ExpectRulesetContents(const std::vector<uint8_t>& expected_contents,
                              bool expected_cached = false) const {
-    ExpectRulesetState(true, RulesetVerificationStatus::INTACT,
+    ExpectRulesetState(true, RulesetVerificationStatus::kIntact,
                        expected_cached);
     EXPECT_TRUE(ruleset_is_created_);
     EXPECT_EQ(expected_contents, contents_);
@@ -323,7 +375,7 @@
 
   bool is_ruleset_file_available_ = false;
   bool has_cached_ruleset_ = false;
-  RulesetVerificationStatus status_ = RulesetVerificationStatus::NOT_VERIFIED;
+  RulesetVerificationStatus status_ = RulesetVerificationStatus::kNotVerified;
 
   bool ruleset_is_created_ = false;
   std::vector<uint8_t> contents_;
@@ -432,7 +484,7 @@
   after_set_ruleset_1.ExpectRulesetState(true);
   read_ruleset_1.ExpectRulesetContents(rulesets().indexed_1().contents);
   after_set_ruleset_2.ExpectRulesetState(true,
-                                         RulesetVerificationStatus::INTACT);
+                                         RulesetVerificationStatus::kIntact);
   read_ruleset_2.ExpectRulesetContents(rulesets().indexed_1().contents);
 }
 
@@ -535,7 +587,7 @@
 
   created_handle.ExpectRulesetContents(rulesets().indexed_1().contents, true);
   read_ruleset.ExpectRulesetContents(rulesets().indexed_1().contents);
-  deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::INTACT);
+  deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::kIntact);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
@@ -576,7 +628,7 @@
   read_ruleset_again_from_handle_2.ExpectRulesetContents(
       rulesets().indexed_1().contents);
   deleted_both_handles.ExpectRulesetState(true,
-                                          RulesetVerificationStatus::INTACT);
+                                          RulesetVerificationStatus::kIntact);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
@@ -629,7 +681,7 @@
   read_from_handle_2_after_update.ExpectRulesetContents(
       rulesets().indexed_2().contents);
   deleted_all_handles.ExpectRulesetState(true,
-                                         RulesetVerificationStatus::INTACT);
+                                         RulesetVerificationStatus::kIntact);
 }
 
 TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
@@ -650,9 +702,9 @@
   dealer_handle()->GetDealerAsync(deleted_handle.GetCallback());
   task_runner()->RunUntilIdle();
 
-  created_handle.ExpectRulesetState(true, RulesetVerificationStatus::CORRUPT);
+  created_handle.ExpectRulesetState(true, RulesetVerificationStatus::kCorrupt);
   read_ruleset.ExpectNoRuleset();
-  deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::CORRUPT);
+  deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::kCorrupt);
 }
 
 }  // namespace subresource_filter
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 745c26bb..543be58 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -5,9 +5,14 @@
 #include "components/viz/common/features.h"
 
 #include "base/command_line.h"
+#include "base/sys_info.h"
 #include "build/build_config.h"
 #include "components/viz/common/switches.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
 namespace features {
 
 // Enables running draw occlusion algorithm to remove Draw Quads that are not
@@ -52,7 +57,7 @@
   auto* command_line = base::CommandLine::ForCurrentProcess();
   return base::FeatureList::IsEnabled(kEnableSurfaceSynchronization) ||
          command_line->HasSwitch(switches::kEnableSurfaceSynchronization) ||
-         base::FeatureList::IsEnabled(kVizDisplayCompositor);
+         IsVizDisplayCompositorEnabled();
 }
 
 bool IsSurfaceInvariantsViolationLoggingEnabled() {
@@ -62,7 +67,7 @@
 
 bool IsVizHitTestingDrawQuadEnabled() {
   return base::FeatureList::IsEnabled(kEnableVizHitTestDrawQuad) ||
-         base::FeatureList::IsEnabled(kVizDisplayCompositor);
+         IsVizDisplayCompositorEnabled();
 }
 
 bool IsVizHitTestingEnabled() {
@@ -90,7 +95,21 @@
 bool IsUsingSkiaDeferredDisplayList() {
   return IsUsingSkiaRenderer() &&
          base::FeatureList::IsEnabled(kUseSkiaDeferredDisplayList) &&
-         base::FeatureList::IsEnabled(kVizDisplayCompositor);
+         IsVizDisplayCompositorEnabled();
+}
+
+bool IsVizDisplayCompositorEnabled() {
+// To work around current bugs, some Android configs do not allow for Viz to be
+// enabled.
+// TODO(cblume): Remove this once https://crbug.com/867453 is addressed.
+#if defined(OS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_KITKAT) {
+    return false;
+  }
+#endif  // defined(OS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
+
+  return base::FeatureList::IsEnabled(kVizDisplayCompositor);
 }
 
 }  // namespace features
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index f5f21df..0398c12 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -28,6 +28,7 @@
 VIZ_COMMON_EXPORT bool IsVizHitTestingSurfaceLayerEnabled();
 VIZ_COMMON_EXPORT bool IsUsingSkiaDeferredDisplayList();
 VIZ_COMMON_EXPORT bool IsUsingSkiaRenderer();
+VIZ_COMMON_EXPORT bool IsVizDisplayCompositorEnabled();
 
 }  // namespace features
 
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 7093641..bf4c745e 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -282,8 +282,10 @@
 
   base::ElapsedTimer aggregate_timer;
   CompositorFrame frame = aggregator_->Aggregate(
-      current_surface_id_, scheduler_ ? scheduler_->current_frame_display_time()
-                                      : base::TimeTicks::Now());
+      current_surface_id_,
+      scheduler_ ? scheduler_->current_frame_display_time()
+                 : base::TimeTicks::Now(),
+      ++swapped_trace_id_);
   UMA_HISTOGRAM_COUNTS_1M("Compositing.SurfaceAggregator.AggregateUs",
                           aggregate_timer.Elapsed().InMicroseconds());
 
@@ -293,6 +295,9 @@
     return false;
   }
 
+  TRACE_EVENT_ASYNC_BEGIN0("viz", "Graphics.Pipeline.DrawAndSwap",
+                           swapped_trace_id_);
+
   // Run callbacks early to allow pipelining and collect presented callbacks.
   for (const auto& surface_id : surfaces_to_ack_on_next_draw_) {
     Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
@@ -336,6 +341,8 @@
   client_->DisplayWillDrawAndSwap(should_draw, frame.render_pass_list);
 
   if (should_draw) {
+    TRACE_EVENT_ASYNC_STEP_INTO0("viz", "Graphics.Pipeline.DrawAndSwap",
+                                 swapped_trace_id_, "Draw");
     if (settings_.enable_draw_occlusion) {
       base::ElapsedTimer draw_occlusion_timer;
       RemoveOverdrawQuads(&frame);
@@ -371,6 +378,8 @@
 
   bool should_swap = should_draw && size_matches;
   if (should_swap) {
+    TRACE_EVENT_ASYNC_STEP_INTO0("viz", "Graphics.Pipeline.DrawAndSwap",
+                                 swapped_trace_id_, "Swap");
     swapped_since_resize_ = true;
 
     if (scheduler_) {
@@ -421,11 +430,16 @@
       }
     }
 
+    ++last_acked_trace_id_;
+    TRACE_EVENT_ASYNC_END0("viz", "Graphics.Pipeline.DrawAndSwap",
+                           last_acked_trace_id_);
     if (scheduler_) {
       scheduler_->DidSwapBuffers();
       scheduler_->DidReceiveSwapBuffersAck();
     }
   }
+  TRACE_EVENT_ASYNC_STEP_INTO0("viz", "Graphics.Pipeline.DrawAndSwap",
+                               swapped_trace_id_, "WaitForAck");
 
   client_->DisplayDidDrawAndSwap();
 
@@ -438,6 +452,9 @@
 }
 
 void Display::DidReceiveSwapBuffersAck() {
+  ++last_acked_trace_id_;
+  TRACE_EVENT_ASYNC_END0("viz", "Graphics.Pipeline.DrawAndSwap",
+                         last_acked_trace_id_);
   if (scheduler_)
     scheduler_->DidReceiveSwapBuffersAck();
   if (renderer_)
diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h
index fc4993f..6c6eaae 100644
--- a/components/viz/service/display/display.h
+++ b/components/viz/service/display/display.h
@@ -171,7 +171,9 @@
   base::circular_deque<std::vector<Surface::PresentedCallback>>
       pending_presented_callbacks_;
 
- private:
+  int32_t swapped_trace_id_ = 0;
+  int32_t last_acked_trace_id_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
 
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index da35a4e..b7518493 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -8,6 +8,7 @@
 
 #include <map>
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/containers/adapters.h"
 #include "base/logging.h"
@@ -303,11 +304,11 @@
 
   ++uma_stats_.valid_surface;
   const CompositorFrame& frame = surface->GetActiveFrame();
-  TRACE_EVENT_WITH_FLOW1(
+  TRACE_EVENT_WITH_FLOW2(
       "viz,benchmark", "Graphics.Pipeline",
       TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id),
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
-      "SurfaceAggregation");
+      "SurfaceAggregation", "display_trace", display_trace_id_);
 
   if (ignore_undamaged) {
     gfx::Transform quad_to_target_transform(
@@ -1150,7 +1151,8 @@
 
 CompositorFrame SurfaceAggregator::Aggregate(
     const SurfaceId& surface_id,
-    base::TimeTicks expected_display_time) {
+    base::TimeTicks expected_display_time,
+    int32_t display_trace_id) {
   DCHECK(!expected_display_time.is_null());
 
   uma_stats_.Reset();
@@ -1167,12 +1169,14 @@
   if (!surface->HasActiveFrame())
     return {};
 
+  base::AutoReset<int32_t> reset_display_trace_id(&display_trace_id_,
+                                                  display_trace_id);
   const CompositorFrame& root_surface_frame = surface->GetActiveFrame();
-  TRACE_EVENT_WITH_FLOW1(
+  TRACE_EVENT_WITH_FLOW2(
       "viz,benchmark", "Graphics.Pipeline",
       TRACE_ID_GLOBAL(root_surface_frame.metadata.begin_frame_ack.trace_id),
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
-      "SurfaceAggregation");
+      "SurfaceAggregation", "display_trace", display_trace_id_);
 
   CompositorFrame frame;
 
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index c2794d1..a2dab40 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -38,7 +38,8 @@
   ~SurfaceAggregator();
 
   CompositorFrame Aggregate(const SurfaceId& surface_id,
-                            base::TimeTicks expected_display_time);
+                            base::TimeTicks expected_display_time,
+                            int32_t display_trace_id = -1);
   void ReleaseResources(const SurfaceId& surface_id);
   const SurfaceIndexMap& previous_contained_surfaces() const {
     return previous_contained_surfaces_;
@@ -280,6 +281,8 @@
   base::flat_map<FrameSinkId, std::pair<LocalSurfaceId, LocalSurfaceId>>
       damage_ranges_;
 
+  int32_t display_trace_id_ = -1;
+
   base::WeakPtrFactory<SurfaceAggregator> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SurfaceAggregator);
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index eb942391..300bc79 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1301,7 +1301,7 @@
     compositing_mode_reporter_impl_ =
         std::make_unique<viz::CompositingModeReporterImpl>();
 
-    if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+    if (features::IsVizDisplayCompositorEnabled()) {
       auto transport_factory = std::make_unique<VizProcessTransportFactory>(
           BrowserGpuChannelHostFactory::instance(), GetResizeTaskRunner(),
           compositing_mode_reporter_impl_.get());
diff --git a/content/browser/compositor/image_transport_factory_browsertest.cc b/content/browser/compositor/image_transport_factory_browsertest.cc
index 659533c..6ce1145 100644
--- a/content/browser/compositor/image_transport_factory_browsertest.cc
+++ b/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -84,7 +84,7 @@
 
   // TODO(crbug.com/844469): Delete after OOP-D is launched because GLHelper
   // and OwnedMailbox aren't used.
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 
   // This test doesn't make sense in software compositing mode.
diff --git a/content/browser/compositor/test/test_image_transport_factory.cc b/content/browser/compositor/test/test_image_transport_factory.cc
index 0e74486..9ce7cab 100644
--- a/content/browser/compositor/test/test_image_transport_factory.cc
+++ b/content/browser/compositor/test/test_image_transport_factory.cc
@@ -35,8 +35,7 @@
 }  // namespace
 
 TestImageTransportFactory::TestImageTransportFactory()
-    : enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+    : enable_viz_(features::IsVizDisplayCompositorEnabled()),
       frame_sink_id_allocator_(kDefaultClientId) {
   if (enable_viz_) {
     test_frame_sink_manager_impl_ =
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index fcbffe4a..e209787 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -202,7 +202,7 @@
       emulation_handler_(emulation_handler),
       observer_(this),
       weak_factory_(this) {
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       base::FeatureList::IsEnabled(
           features::kUseVideoCaptureApiForDevToolsSnapshots)) {
     video_consumer_ = std::make_unique<DevToolsVideoConsumer>(
@@ -266,6 +266,9 @@
 
 void PageHandler::OnSwapCompositorFrame(
     viz::CompositorFrameMetadata frame_metadata) {
+  if (video_consumer_)
+    return;
+
   last_compositor_frame_metadata_ = std::move(frame_metadata);
   has_compositor_frame_metadata_ = true;
 
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 296270a..957e1d5ecd 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -214,7 +214,7 @@
       return_as_stream_(false),
       gzip_compression_(false),
       weak_factory_(this) {
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       base::FeatureList::IsEnabled(
           features::kUseVideoCaptureApiForDevToolsSnapshots)) {
     video_consumer_ =
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index f131c03..1149853b 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -477,7 +477,7 @@
 
   if (sessions().size() == 1) {
     // Taking screenshots using the video capture API is done in TracingHandler.
-    if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor) &&
+    if (!features::IsVizDisplayCompositorEnabled() &&
         !base::FeatureList::IsEnabled(
             features::kUseVideoCaptureApiForDevToolsSnapshots)) {
       frame_trace_recorder_.reset(new DevToolsFrameTraceRecorder());
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 4b14d65a..261946e 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -399,7 +399,7 @@
     int gpu_client_id,
     const base::FilePath& cache_dir) {
   GetShaderCacheFactorySingleton()->SetCacheInfo(gpu_client_id, cache_dir);
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  if (features::IsVizDisplayCompositorEnabled()) {
     GetShaderCacheFactorySingleton()->SetCacheInfo(
         gpu::kInProcessCommandBufferClientId, cache_dir);
   }
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index a57fbd9..95bc987 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -190,7 +190,7 @@
        "WebGL2 has been disabled via blacklist or the command line.", false,
        true},
       {"viz_display_compositor", gpu::kGpuFeatureStatusEnabled,
-       !base::FeatureList::IsEnabled(features::kVizDisplayCompositor),
+       !features::IsVizDisplayCompositorEnabled(),
        "Viz service display compositor is not enabled by default.", false,
        false},
       {"skia_renderer", gpu::kGpuFeatureStatusEnabled,
@@ -257,7 +257,7 @@
           status += "_on";
       }
       if (gpu_feature_data.name == "viz_display_compositor") {
-        if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+        if (features::IsVizDisplayCompositorEnabled())
           status += "_on";
       }
       if (gpu_feature_data.name == "skia_renderer") {
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index e926af0..971fe8b 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -353,8 +353,7 @@
 }
 
 bool GpuDataManagerImplPrivate::GpuProcessStartAllowed() const {
-  return base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
-         GpuAccessAllowed(nullptr);
+  return features::IsVizDisplayCompositorEnabled() || GpuAccessAllowed(nullptr);
 }
 
 void GpuDataManagerImplPrivate::RequestCompleteGpuInfoIfNeeded() {
@@ -840,7 +839,7 @@
     return gpu::GpuMode::HARDWARE_ACCELERATED;
   } else if (SwiftShaderAllowed()) {
     return gpu::GpuMode::SWIFTSHADER;
-  } else if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  } else if (features::IsVizDisplayCompositorEnabled()) {
     return gpu::GpuMode::DISPLAY_COMPOSITOR;
   } else {
     return gpu::GpuMode::DISABLED;
@@ -862,7 +861,7 @@
   } else if (SwiftShaderAllowed()) {
     swiftshader_blocked_ = true;
     OnGpuBlocked();
-  } else if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  } else if (features::IsVizDisplayCompositorEnabled()) {
     // The GPU process is frequently crashing with only the display compositor
     // running. This should never happen so something is wrong. Crash the
     // browser process to reset everything.
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index d4b857bc..bc78898 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -1018,8 +1018,7 @@
       switches::kDisableGpuShaderDiskCache)) {
     CreateChannelCache(client_id);
 
-    bool oopd_enabled =
-        base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+    bool oopd_enabled = features::IsVizDisplayCompositorEnabled();
     if (oopd_enabled)
       CreateChannelCache(gpu::kInProcessCommandBufferClientId);
 
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index 9bb2156..b35a210 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -60,8 +60,7 @@
   // content (otherwise this solid color will be flashed during navigation).
   root_layer_->SetColor(SK_ColorTRANSPARENT);
   delegated_frame_host_.reset(new DelegatedFrameHost(
-      frame_sink_id, this,
-      base::FeatureList::IsEnabled(features::kVizDisplayCompositor),
+      frame_sink_id, this, features::IsVizDisplayCompositorEnabled(),
       true /* should_register_frame_sink_id */));
 
   SetRenderWidgetHostIsHidden(render_widget_host_is_hidden);
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 454ebca..49b832e 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -191,8 +191,7 @@
   friend class base::NoDestructor<CompositorDependencies>;
 
   CompositorDependencies() : frame_sink_id_allocator(kDefaultClientId) {
-    bool enable_viz =
-        base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+    bool enable_viz = features::IsVizDisplayCompositorEnabled();
     if (!enable_viz) {
       // The SharedBitmapManager can be null as software compositing is not
       // supported or used on Android.
@@ -700,8 +699,7 @@
       lock_manager_(base::ThreadTaskRunnerHandle::Get(), this),
       enable_surface_synchronization_(
           features::IsSurfaceSynchronizationEnabled()),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       weak_factory_(this) {
   GetHostFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_, this);
   GetHostFrameSinkManager()->SetFrameSinkDebugLabel(frame_sink_id_,
diff --git a/content/browser/renderer_host/input/autoscroll_browsertest.cc b/content/browser/renderer_host/input/autoscroll_browsertest.cc
new file mode 100644
index 0000000..0b662c1
--- /dev/null
+++ b/content/browser/renderer_host/input/autoscroll_browsertest.cc
@@ -0,0 +1,155 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "third_party/blink/public/platform/web_input_event.h"
+#include "ui/events/base_event_utils.h"
+
+using blink::WebInputEvent;
+
+namespace {
+
+const std::string kAutoscrollDataURL = R"HTML(
+  <!DOCTYPE html>
+  <meta name='viewport' content='width=device-width'/>
+  <style>
+  html, body {
+    margin: 0;
+  }
+  .spacer { height: 10000px; }
+  </style>
+  <div class=spacer></div>
+  <script>
+    document.title='ready';
+  </script>)HTML";
+}  // namespace
+
+namespace content {
+
+class AutoscrollBrowserTest : public ContentBrowserTest {
+ public:
+  AutoscrollBrowserTest() {}
+  ~AutoscrollBrowserTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII("--enable-blink-features",
+                                    "MiddleClickAutoscroll");
+  }
+
+ protected:
+  RenderWidgetHostImpl* GetWidgetHost() {
+    return RenderWidgetHostImpl::From(
+        shell()->web_contents()->GetRenderViewHost()->GetWidget());
+  }
+
+  void LoadURL(const std::string& page_data) {
+    const GURL data_url("data:text/html," + page_data);
+    NavigateToURL(shell(), data_url);
+
+    RenderWidgetHostImpl* host = GetWidgetHost();
+    host->GetView()->SetSize(gfx::Size(400, 400));
+
+    base::string16 ready_title(base::ASCIIToUTF16("ready"));
+    TitleWatcher watcher(shell()->web_contents(), ready_title);
+    ignore_result(watcher.WaitAndGetTitle());
+
+    MainThreadFrameObserver main_thread_sync(host);
+    main_thread_sync.Wait();
+  }
+
+  void SimulateMiddleClick(int x, int y, int modifiers) {
+    // Simulate and send middle click mouse down.
+    blink::WebMouseEvent down_event = SyntheticWebMouseEventBuilder::Build(
+        blink::WebInputEvent::kMouseDown, x, y, modifiers);
+    down_event.button = blink::WebMouseEvent::Button::kMiddle;
+    down_event.SetTimeStamp(ui::EventTimeForNow());
+    down_event.SetPositionInScreen(x, y);
+    GetWidgetHost()->ForwardMouseEvent(down_event);
+
+    // Simulate and send middle click mouse up.
+    blink::WebMouseEvent up_event = SyntheticWebMouseEventBuilder::Build(
+        blink::WebInputEvent::kMouseUp, x, y, modifiers);
+    up_event.button = blink::WebMouseEvent::Button::kMiddle;
+    up_event.SetTimeStamp(ui::EventTimeForNow());
+    up_event.SetPositionInScreen(x, y);
+    GetWidgetHost()->ForwardMouseEvent(up_event);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AutoscrollBrowserTest);
+};
+
+// TODO(sahel): This test is flaky https://crbug.com/838769
+IN_PROC_BROWSER_TEST_F(AutoscrollBrowserTest, DISABLED_AutoscrollFling) {
+  LoadURL(kAutoscrollDataURL);
+
+  // Start autoscroll with middle click.
+  auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
+      GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
+  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
+  input_msg_watcher->WaitForAck();
+
+  // The page should start scrolling with mouse move.
+  RenderFrameSubmissionObserver observer(
+      GetWidgetHost()->render_frame_metadata_provider());
+  blink::WebMouseEvent move_event = SyntheticWebMouseEventBuilder::Build(
+      blink::WebInputEvent::kMouseMove, 30, 30,
+      blink::WebInputEvent::kNoModifiers);
+  move_event.SetTimeStamp(ui::EventTimeForNow());
+  move_event.SetPositionInScreen(30, 30);
+  GetWidgetHost()->ForwardMouseEvent(move_event);
+  gfx::Vector2dF default_scroll_offset;
+  while (observer.LastRenderFrameMetadata()
+             .root_scroll_offset.value_or(default_scroll_offset)
+             .y() <= 0) {
+    observer.WaitForMetadataChange();
+  }
+}
+
+#if !defined(OS_ANDROID)
+#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
+  WheelScrollingWorksAfterAutoscrollCancel
+#else
+#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
+  DISABLED_WheelScrollingWorksAfterAutoscrollCancel
+#endif
+// Checks that wheel scrolling works after autoscroll cancelation.
+IN_PROC_BROWSER_TEST_F(AutoscrollBrowserTest,
+                       MAYBE_WheelScrollingWorksAfterAutoscrollCancel) {
+  LoadURL(kAutoscrollDataURL);
+
+  // Start autoscroll with middle click.
+  auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
+      GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
+  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
+  input_msg_watcher->WaitForAck();
+
+  // Without moving the mouse cancel the autoscroll fling with another click.
+  input_msg_watcher = std::make_unique<InputMsgWatcher>(
+      GetWidgetHost(), blink::WebInputEvent::kGestureScrollEnd);
+  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
+  input_msg_watcher->WaitForAck();
+
+  // The mouse wheel scrolling must work after autoscroll cancellation.
+  RenderFrameSubmissionObserver observer(
+      GetWidgetHost()->render_frame_metadata_provider());
+  blink::WebMouseWheelEvent wheel_event =
+      SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true);
+  wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
+  GetWidgetHost()->ForwardWheelEvent(wheel_event);
+  gfx::Vector2dF default_scroll_offset;
+  while (observer.LastRenderFrameMetadata()
+             .root_scroll_offset.value_or(default_scroll_offset)
+             .y() <= 0) {
+    observer.WaitForMetadataChange();
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/input/fling_browsertest.cc b/content/browser/renderer_host/input/fling_browsertest.cc
index c952871f..eb26d87 100644
--- a/content/browser/renderer_host/input/fling_browsertest.cc
+++ b/content/browser/renderer_host/input/fling_browsertest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/feature_list.h"
-#include "build/build_config.h"
 #include "components/viz/common/features.h"
 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -53,11 +51,6 @@
   BrowserSideFlingBrowserTest() {}
   ~BrowserSideFlingBrowserTest() override {}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("--enable-blink-features",
-                                    "MiddleClickAutoscroll");
-  }
-
   void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
     EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
     run_loop_->Quit();
@@ -84,24 +77,6 @@
     main_thread_sync.Wait();
   }
 
-  void SimulateMiddleClick(int x, int y, int modifiers) {
-    // Simulate and send middle click mouse down.
-    blink::WebMouseEvent down_event = SyntheticWebMouseEventBuilder::Build(
-        blink::WebInputEvent::kMouseDown, x, y, modifiers);
-    down_event.button = blink::WebMouseEvent::Button::kMiddle;
-    down_event.SetTimeStamp(ui::EventTimeForNow());
-    down_event.SetPositionInScreen(x, y);
-    GetWidgetHost()->ForwardMouseEvent(down_event);
-
-    // Simulate and send middle click mouse up.
-    blink::WebMouseEvent up_event = SyntheticWebMouseEventBuilder::Build(
-        blink::WebInputEvent::kMouseUp, x, y, modifiers);
-    up_event.button = blink::WebMouseEvent::Button::kMiddle;
-    up_event.SetTimeStamp(ui::EventTimeForNow());
-    up_event.SetPositionInScreen(x, y);
-    GetWidgetHost()->ForwardMouseEvent(up_event);
-  }
-
   std::unique_ptr<base::RunLoop> run_loop_;
 
  private:
@@ -112,7 +87,7 @@
 #if defined(OS_MACOSX)
   // TODO(jonross): Re-enable once fling on Mac works with Viz.
   // https://crbug.com/842325
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 #endif  // defined(OS_MACOSX)
 
@@ -152,7 +127,7 @@
 #if defined(OS_MACOSX)
   // TODO(jonross): Re-enable once fling on Mac works with Viz.
   // https://crbug.com/842325
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 #endif  // defined(OS_MACOSX)
 
@@ -186,72 +161,6 @@
   }
 }
 
-// TODO(sahel): This test is flaky https://crbug.com/838769
-IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, DISABLED_AutoscrollFling) {
-  LoadURL(kBrowserFlingDataURL);
-
-  // Start autoscroll with middle click.
-  auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
-      GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
-  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
-  input_msg_watcher->WaitForAck();
-
-  // The page should start scrolling with mouse move.
-  RenderFrameSubmissionObserver observer(
-      GetWidgetHost()->render_frame_metadata_provider());
-  blink::WebMouseEvent move_event = SyntheticWebMouseEventBuilder::Build(
-      blink::WebInputEvent::kMouseMove, 30, 30,
-      blink::WebInputEvent::kNoModifiers);
-  move_event.SetTimeStamp(ui::EventTimeForNow());
-  move_event.SetPositionInScreen(30, 30);
-  GetWidgetHost()->ForwardMouseEvent(move_event);
-  gfx::Vector2dF default_scroll_offset;
-  while (observer.LastRenderFrameMetadata()
-             .root_scroll_offset.value_or(default_scroll_offset)
-             .y() <= 0) {
-    observer.WaitForMetadataChange();
-  }
-}
-
-#if !defined(OS_ANDROID)
-#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
-  WheelScrollingWorksAfterAutoscrollCancel
-#else
-#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
-  DISABLED_WheelScrollingWorksAfterAutoscrollCancel
-#endif
-// Checks that wheel scrolling works after autoscroll cancelation.
-IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
-                       MAYBE_WheelScrollingWorksAfterAutoscrollCancel) {
-  LoadURL(kBrowserFlingDataURL);
-
-  // Start autoscroll with middle click.
-  auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
-      GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
-  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
-  input_msg_watcher->WaitForAck();
-
-  // Without moving the mouse cancel the autoscroll fling with another click.
-  input_msg_watcher = std::make_unique<InputMsgWatcher>(
-      GetWidgetHost(), blink::WebInputEvent::kGestureScrollEnd);
-  SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
-  input_msg_watcher->WaitForAck();
-
-  // The mouse wheel scrolling must work after autoscroll cancellation.
-  RenderFrameSubmissionObserver observer(
-      GetWidgetHost()->render_frame_metadata_provider());
-  blink::WebMouseWheelEvent wheel_event =
-      SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true);
-  wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
-  GetWidgetHost()->ForwardWheelEvent(wheel_event);
-  gfx::Vector2dF default_scroll_offset;
-  while (observer.LastRenderFrameMetadata()
-             .root_scroll_offset.value_or(default_scroll_offset)
-             .y() <= 0) {
-    observer.WaitForMetadataChange();
-  }
-}
-
 // Disabled on MacOS because it doesn't support touchscreen scroll.
 #if defined(OS_MACOSX)
 #define MAYBE_ScrollEndGeneratedForFilteredFling \
diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
index 63a7100..cb015802a 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
@@ -534,16 +534,20 @@
 
 class MockSyntheticPointerActionTarget : public MockSyntheticGestureTarget {
  public:
-  MockSyntheticPointerActionTarget() : num_actions_dispatched_(0) {}
+  MockSyntheticPointerActionTarget() : num_dispatched_pointer_actions_(0) {}
   ~MockSyntheticPointerActionTarget() override {}
 
   WebInputEvent::Type type() const { return type_; }
-  int num_actions_dispatched() const { return num_actions_dispatched_; }
-  void reset_num_actions_dispatched() { num_actions_dispatched_ = 0; }
+  int num_dispatched_pointer_actions() const {
+    return num_dispatched_pointer_actions_;
+  }
+  void reset_num_dispatched_pointer_actions() {
+    num_dispatched_pointer_actions_ = 0;
+  }
 
  protected:
   WebInputEvent::Type type_;
-  int num_actions_dispatched_;
+  int num_dispatched_pointer_actions_;
 };
 
 class MockSyntheticPointerTouchActionTarget
@@ -559,25 +563,26 @@
     for (size_t i = 0; i < WebTouchEvent::kTouchesLengthCap; ++i) {
       if (WebTouchPointStateToEventType(touch_event.touches[i].state) != type_)
         continue;
-
-      indexes_[i] = touch_event.touches[i].id;
-      positions_[i] = gfx::PointF(touch_event.touches[i].PositionInWidget());
-      states_[i] = touch_event.touches[i].state;
+      indexes_[num_dispatched_pointer_actions_] = i;
+      positions_[num_dispatched_pointer_actions_] =
+          gfx::PointF(touch_event.touches[i].PositionInWidget());
+      states_[num_dispatched_pointer_actions_] = touch_event.touches[i].state;
+      num_dispatched_pointer_actions_++;
     }
-    num_actions_dispatched_++;
   }
 
   testing::AssertionResult SyntheticTouchActionDispatchedCorrectly(
       const SyntheticPointerActionParams& param,
-      int index) {
+      int index,
+      int touch_index) {
     if (param.pointer_action_type() ==
             SyntheticPointerActionParams::PointerActionType::PRESS ||
         param.pointer_action_type() ==
             SyntheticPointerActionParams::PointerActionType::MOVE) {
-      if (indexes_[index] != param.index()) {
+      if (indexes_[index] != touch_index) {
         return testing::AssertionFailure()
                << "Pointer index at index " << index << " was "
-               << indexes_[index] << ", expected " << param.index() << ".";
+               << indexes_[index] << ", expected " << touch_index << ".";
       }
 
       if (positions_[index] != param.position()) {
@@ -598,13 +603,15 @@
   }
 
   testing::AssertionResult SyntheticTouchActionListDispatchedCorrectly(
-      const std::vector<SyntheticPointerActionParams>& params_list) {
+      const std::vector<SyntheticPointerActionParams>& params_list,
+      int start_index,
+      int index_array[]) {
     testing::AssertionResult result = testing::AssertionSuccess();
     for (size_t i = 0; i < params_list.size(); ++i) {
       if (params_list[i].pointer_action_type() !=
           SyntheticPointerActionParams::PointerActionType::IDLE)
         result = SyntheticTouchActionDispatchedCorrectly(
-            params_list[i], params_list[i].index());
+            params_list[i], start_index + i, index_array[i]);
       if (result == testing::AssertionFailure())
         return result;
     }
@@ -630,7 +637,7 @@
     position_ = gfx::PointF(mouse_event.PositionInWidget());
     clickCount_ = mouse_event.click_count;
     button_ = mouse_event.button;
-    num_actions_dispatched_++;
+    num_dispatched_pointer_actions_++;
   }
 
   testing::AssertionResult SyntheticMouseActionDispatchedCorrectly(
@@ -1666,11 +1673,12 @@
 
   MockSyntheticPointerTouchActionTarget* pointer_touch_target =
       static_cast<MockSyntheticPointerTouchActionTarget*>(target_);
+  int index_array[2] = {0, 1};
   EXPECT_EQ(1, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_touch_target->num_actions_dispatched(), 2);
+  EXPECT_EQ(pointer_touch_target->num_dispatched_pointer_actions(), 2);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list));
+      param_list, 0, index_array));
 
   // Second, send a touch release for finger 0, a touch move for finger 1.
   param0.set_pointer_action_type(
@@ -1684,14 +1692,15 @@
   params.PushPointerActionParamsList(param_list);
   gesture.reset(new SyntheticPointerAction(params));
   QueueSyntheticGesture(std::move(gesture));
-  pointer_touch_target->reset_num_actions_dispatched();
+  pointer_touch_target->reset_num_dispatched_pointer_actions();
   FlushInputUntilComplete();
 
+  index_array[1] = 0;
   EXPECT_EQ(2, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_touch_target->num_actions_dispatched(), 4);
+  EXPECT_EQ(pointer_touch_target->num_dispatched_pointer_actions(), 4);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list));
+      param_list, 2, index_array));
 
   // Third, send a touch release for finger 1.
   param1.set_pointer_action_type(
@@ -1701,14 +1710,14 @@
   params.PushPointerActionParamsList(param_list);
   gesture.reset(new SyntheticPointerAction(params));
   QueueSyntheticGesture(std::move(gesture));
-  pointer_touch_target->reset_num_actions_dispatched();
+  pointer_touch_target->reset_num_dispatched_pointer_actions();
   FlushInputUntilComplete();
 
   EXPECT_EQ(3, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_touch_target->num_actions_dispatched(), 5);
+  EXPECT_EQ(pointer_touch_target->num_dispatched_pointer_actions(), 5);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list));
+      param_list, 4, index_array));
 }
 
 TEST_F(SyntheticGestureControllerTest, PointerMouseAction) {
@@ -1732,7 +1741,7 @@
       static_cast<MockSyntheticPointerMouseActionTarget*>(target_);
   EXPECT_EQ(1, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_mouse_target->num_actions_dispatched(), 1);
+  EXPECT_EQ(pointer_mouse_target->num_dispatched_pointer_actions(), 1);
   EXPECT_TRUE(
       pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(param, 0));
 
@@ -1743,12 +1752,12 @@
   params.PushPointerActionParams(param);
   gesture.reset(new SyntheticPointerAction(params));
   QueueSyntheticGesture(std::move(gesture));
-  pointer_mouse_target->reset_num_actions_dispatched();
+  pointer_mouse_target->reset_num_dispatched_pointer_actions();
   FlushInputUntilComplete();
 
   EXPECT_EQ(2, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_mouse_target->num_actions_dispatched(), 2);
+  EXPECT_EQ(pointer_mouse_target->num_dispatched_pointer_actions(), 2);
   EXPECT_TRUE(
       pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(param, 1));
 
@@ -1759,12 +1768,12 @@
   params.PushPointerActionParams(param);
   gesture.reset(new SyntheticPointerAction(params));
   QueueSyntheticGesture(std::move(gesture));
-  pointer_mouse_target->reset_num_actions_dispatched();
+  pointer_mouse_target->reset_num_dispatched_pointer_actions();
   FlushInputUntilComplete();
 
   EXPECT_EQ(3, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_mouse_target->num_actions_dispatched(), 3);
+  EXPECT_EQ(pointer_mouse_target->num_dispatched_pointer_actions(), 3);
   EXPECT_TRUE(
       pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(param, 1));
 
@@ -1774,12 +1783,12 @@
   params.PushPointerActionParams(param);
   gesture.reset(new SyntheticPointerAction(params));
   QueueSyntheticGesture(std::move(gesture));
-  pointer_mouse_target->reset_num_actions_dispatched();
+  pointer_mouse_target->reset_num_dispatched_pointer_actions();
   FlushInputUntilComplete();
 
   EXPECT_EQ(4, num_success_);
   EXPECT_EQ(0, num_failure_);
-  EXPECT_EQ(pointer_mouse_target->num_actions_dispatched(), 4);
+  EXPECT_EQ(pointer_mouse_target->num_dispatched_pointer_actions(), 4);
   EXPECT_TRUE(
       pointer_mouse_target->SyntheticMouseActionDispatchedCorrectly(param, 1));
 }
diff --git a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
index c4d45f9d..98aa23e0 100644
--- a/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
+++ b/content/browser/renderer_host/input/synthetic_pointer_action_unittest.cc
@@ -115,7 +115,8 @@
 class MockSyntheticPointerTouchActionTarget
     : public MockSyntheticPointerActionTarget {
  public:
-  MockSyntheticPointerTouchActionTarget() {}
+  MockSyntheticPointerTouchActionTarget()
+      : num_dispatched_pointer_actions_(0) {}
   ~MockSyntheticPointerTouchActionTarget() override {}
 
   void DispatchInputEventToPlatform(const WebInputEvent& event) override {
@@ -125,16 +126,18 @@
     for (size_t i = 0; i < WebTouchEvent::kTouchesLengthCap; ++i) {
       if (WebTouchPointStateToEventType(touch_event.touches[i].state) != type_)
         continue;
-
-      indexes_[i] = touch_event.touches[i].id;
-      positions_[i] = gfx::PointF(touch_event.touches[i].PositionInWidget());
-      states_[i] = touch_event.touches[i].state;
+      indexes_[num_dispatched_pointer_actions_] = i;
+      positions_[num_dispatched_pointer_actions_] =
+          gfx::PointF(touch_event.touches[i].PositionInWidget());
+      states_[num_dispatched_pointer_actions_] = touch_event.touches[i].state;
+      num_dispatched_pointer_actions_++;
     }
   }
 
   testing::AssertionResult SyntheticTouchActionDispatchedCorrectly(
       const SyntheticPointerActionParams& param,
-      int index) {
+      int index,
+      int touch_index) {
     if (param.pointer_action_type() ==
             SyntheticPointerActionParams::PointerActionType::PRESS ||
         param.pointer_action_type() ==
@@ -163,13 +166,16 @@
   }
 
   testing::AssertionResult SyntheticTouchActionListDispatchedCorrectly(
-      const std::vector<SyntheticPointerActionParams>& params_list) {
+      const std::vector<SyntheticPointerActionParams>& params_list,
+      int index_array[]) {
     testing::AssertionResult result = testing::AssertionSuccess();
+    num_dispatched_pointer_actions_ = 0;
+    int result_index = 0;
     for (size_t i = 0; i < params_list.size(); ++i) {
       if (params_list[i].pointer_action_type() !=
           SyntheticPointerActionParams::PointerActionType::IDLE)
         result = SyntheticTouchActionDispatchedCorrectly(
-            params_list[i], params_list[i].index());
+            params_list[i], result_index++, index_array[i]);
       if (result == testing::AssertionFailure())
         return result;
     }
@@ -182,6 +188,7 @@
   }
 
  private:
+  int num_dispatched_pointer_actions_;
   gfx::PointF positions_[WebTouchEvent::kTouchesLengthCap];
   int indexes_[WebTouchEvent::kTouchesLengthCap];
   WebTouchPoint::State states_[WebTouchEvent::kTouchesLengthCap];
@@ -381,11 +388,12 @@
   ForwardSyntheticPointerAction();
   MockSyntheticPointerTouchActionTarget* pointer_touch_target =
       static_cast<MockSyntheticPointerTouchActionTarget*>(target_.get());
+  int index_array[2] = {0, 1};
   EXPECT_EQ(1, num_success_);
   EXPECT_EQ(0, num_failure_);
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchStart);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list1));
+      param_list1, index_array));
 
   ForwardSyntheticPointerAction();
   EXPECT_EQ(2, num_success_);
@@ -393,21 +401,22 @@
   // The type of the SyntheticWebTouchEvent is the action of the last finger.
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchStart);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list2));
+      param_list2, index_array));
 
   ForwardSyntheticPointerAction();
   EXPECT_EQ(3, num_success_);
   EXPECT_EQ(0, num_failure_);
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchMove);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list3));
+      param_list3, index_array));
 
   ForwardSyntheticPointerAction();
+  index_array[1] = 0;
   EXPECT_EQ(4, num_success_);
   EXPECT_EQ(0, num_failure_);
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchEnd);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list4));
+      param_list4, index_array));
 }
 
 TEST_F(SyntheticPointerActionTest, PointerTouchActionsMultiPressRelease) {
@@ -450,11 +459,12 @@
   ForwardSyntheticPointerAction();
   MockSyntheticPointerTouchActionTarget* pointer_touch_target =
       static_cast<MockSyntheticPointerTouchActionTarget*>(target_.get());
+  int index_array[2] = {0, 1};
   EXPECT_EQ(count_success++, num_success_);
   EXPECT_EQ(0, num_failure_);
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchStart);
   EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-      param_list1));
+      param_list1, index_array));
 
   for (int index = 1; index < 4; ++index) {
     ForwardSyntheticPointerAction();
@@ -464,7 +474,7 @@
     EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchStart);
     EXPECT_TRUE(
         pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-            param_list2));
+            param_list2, index_array));
 
     ForwardSyntheticPointerAction();
     EXPECT_EQ(count_success++, num_success_);
@@ -473,7 +483,7 @@
     EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchEnd);
     EXPECT_TRUE(
         pointer_touch_target->SyntheticTouchActionListDispatchedCorrectly(
-            param_list3));
+            param_list3, index_array));
   }
 }
 
@@ -517,8 +527,8 @@
   EXPECT_EQ(1, num_success_);
   EXPECT_EQ(2, num_failure_);
   EXPECT_EQ(pointer_touch_target->type(), WebInputEvent::kTouchStart);
-  EXPECT_TRUE(
-      pointer_touch_target->SyntheticTouchActionDispatchedCorrectly(param, 0));
+  EXPECT_TRUE(pointer_touch_target->SyntheticTouchActionDispatchedCorrectly(
+      param, 0, 0));
 
   // Cannot send a touch press again without releasing the finger.
   ForwardSyntheticPointerAction();
diff --git a/content/browser/renderer_host/input/synthetic_touch_driver.cc b/content/browser/renderer_host/input/synthetic_touch_driver.cc
index a8e14a2..d79deeb 100644
--- a/content/browser/renderer_host/input/synthetic_touch_driver.cc
+++ b/content/browser/renderer_host/input/synthetic_touch_driver.cc
@@ -25,6 +25,7 @@
   if (touch_event_.GetType() != blink::WebInputEvent::kUndefined)
     target->DispatchInputEventToPlatform(touch_event_);
   touch_event_.ResetPoints();
+  ResetIndexMap();
 }
 
 void SyntheticTouchDriver::Press(float x,
@@ -84,4 +85,26 @@
   return true;
 }
 
+void SyntheticTouchDriver::ResetIndexMap() {
+  unsigned free_index = 0;
+  for (unsigned int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; ++i) {
+    if (free_index >= touch_event_.touches_length)
+      break;
+    if (touch_event_.touches[i].state !=
+        blink::WebTouchPoint::kStateUndefined) {
+      touch_event_.touches[free_index] = touch_event_.touches[i];
+      int index = GetIndexFromMap(i);
+      index_map_[index] = free_index;
+      free_index++;
+    }
+  }
+}
+
+int SyntheticTouchDriver::GetIndexFromMap(int value) const {
+  int index = std::find(index_map_.begin(), index_map_.end(), value) -
+              index_map_.begin();
+  DCHECK(index >= 0 && index < blink::WebTouchEvent::kTouchesLengthCap);
+  return index;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/input/synthetic_touch_driver.h b/content/browser/renderer_host/input/synthetic_touch_driver.h
index 95470f3..86b9940 100644
--- a/content/browser/renderer_host/input/synthetic_touch_driver.h
+++ b/content/browser/renderer_host/input/synthetic_touch_driver.h
@@ -38,6 +38,9 @@
  private:
   using IndexMap = std::array<int, blink::WebTouchEvent::kTouchesLengthCap>;
 
+  void ResetIndexMap();
+  int GetIndexFromMap(int value) const;
+
   SyntheticWebTouchEvent touch_event_;
   IndexMap index_map_;
 
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index c8c5e3b..16e30e8 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -52,7 +52,9 @@
       DCHECK(!suppress_manipulation_events_);
       DCHECK(!touchscreen_scroll_in_progress_);
       touchscreen_scroll_in_progress_ = true;
-      DCHECK(scrolling_touch_action_.has_value());
+      // TODO(https://crbug.com/851644): Make sure the value is properly set.
+      if (!scrolling_touch_action_.has_value())
+        SetTouchAction(cc::kTouchActionAuto);
       suppress_manipulation_events_ =
           ShouldSuppressManipulation(*gesture_event);
       return suppress_manipulation_events_
@@ -116,6 +118,9 @@
     // If double tap is disabled, there's no reason for the tap delay.
     case WebInputEvent::kGestureTapUnconfirmed: {
       DCHECK_EQ(1, gesture_event->data.tap.tap_count);
+      // TODO(https://crbug.com/851644): Make sure the value is properly set.
+      if (!scrolling_touch_action_.has_value())
+        SetTouchAction(cc::kTouchActionAuto);
       allow_current_double_tap_event_ = (scrolling_touch_action_.value() &
                                          cc::kTouchActionDoubleTapZoom) != 0;
       if (!allow_current_double_tap_event_) {
@@ -139,6 +144,11 @@
       if (gesture_event->is_source_touch_event_set_non_blocking)
         SetTouchAction(cc::kTouchActionAuto);
       scrolling_touch_action_ = allowed_touch_action_;
+      // TODO(https://crbug.com/851644): The value may not set in the case when
+      // the gesture event is flushed due to touch ack time out after the finger
+      // is lifted up. Make sure the value is properly set.
+      if (!scrolling_touch_action_.has_value())
+        SetTouchAction(cc::kTouchActionAuto);
       DCHECK(!drop_current_tap_ending_event_);
       break;
 
diff --git a/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
index 69dc42a..9a5d4a8 100644
--- a/content/browser/renderer_host/input/touch_action_filter_unittest.cc
+++ b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
@@ -888,8 +888,8 @@
 
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
             FilterGestureEventResult::kFilterGestureEventAllowed);
-  EXPECT_FALSE(filter_.allowed_touch_action().has_value());
-  EXPECT_FALSE(ScrollingTouchAction().has_value());
+  EXPECT_TRUE(filter_.allowed_touch_action().has_value());
+  EXPECT_TRUE(ScrollingTouchAction().has_value());
 }
 
 TEST_F(TouchActionFilterTest, TouchpadScroll) {
diff --git a/content/browser/renderer_host/input/web_input_event_builders_mac.h b/content/browser/renderer_host/input/web_input_event_builders_mac.h
index d5755a7..99f0d86 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_mac.h
+++ b/content/browser/renderer_host/input/web_input_event_builders_mac.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_keyboard_event.h"
 #include "third_party/blink/public/platform/web_mouse_wheel_event.h"
+#include "third_party/blink/public/platform/web_touch_event.h"
 
 @class NSEvent;
 @class NSView;
@@ -41,6 +42,11 @@
   static blink::WebGestureEvent Build(NSEvent*, NSView*);
 };
 
+class CONTENT_EXPORT WebTouchEventBuilder {
+ public:
+  static blink::WebTouchEvent Build(NSEvent* event, NSView* view);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_MAC_H_
diff --git a/content/browser/renderer_host/input/web_input_event_builders_mac.mm b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
index be7624b..cb73466 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_mac.mm
+++ b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
@@ -571,4 +571,72 @@
   return result;
 }
 
+// WebTouchEvent --------------------------------------------------------------
+
+blink::WebTouchEvent WebTouchEventBuilder::Build(NSEvent* event, NSView* view) {
+  blink::WebInputEvent::Type event_type =
+      blink::WebInputEvent::Type::kUndefined;
+  NSEventType type = [event type];
+  blink::WebTouchPoint::State state = blink::WebTouchPoint::kStateUndefined;
+  switch (type) {
+    case NSLeftMouseDown:
+      event_type = blink::WebInputEvent::kTouchStart;
+      state = blink::WebTouchPoint::kStatePressed;
+      break;
+    case NSLeftMouseUp:
+      event_type = blink::WebInputEvent::kTouchEnd;
+      state = blink::WebTouchPoint::kStateReleased;
+      break;
+    case NSLeftMouseDragged:
+    case NSRightMouseDragged:
+    case NSOtherMouseDragged:
+    case NSMouseMoved:
+    case NSRightMouseDown:
+    case NSOtherMouseDown:
+    case NSRightMouseUp:
+    case NSOtherMouseUp:
+      event_type = blink::WebInputEvent::kTouchMove;
+      state = blink::WebTouchPoint::kStateMoved;
+      break;
+    default:
+      NOTREACHED() << "Invalid types for touch events." << type;
+  }
+
+  blink::WebTouchEvent result(event_type, ModifiersFromEvent(event),
+                              ui::EventTimeStampFromSeconds([event timestamp]));
+  result.hovering = event_type == blink::WebInputEvent::kTouchEnd;
+  result.unique_touch_event_id = ui::GetNextTouchEventId();
+  result.touches_length = 1;
+
+  // Use a temporary WebMouseEvent to get the location.
+  blink::WebMouseEvent temp;
+  SetWebEventLocationFromEventInView(&temp, event, view);
+  result.touches[0].SetPositionInWidget(temp.PositionInWidget());
+  result.touches[0].SetPositionInScreen(temp.PositionInScreen());
+  result.touches[0].movement_x = temp.movement_x;
+  result.touches[0].movement_y = temp.movement_y;
+
+  result.touches[0].state = state;
+  result.touches[0].pointer_type =
+      blink::WebPointerProperties::PointerType::kPen;
+  result.touches[0].id = [event pointingDeviceID];
+  result.touches[0].force = [event pressure];
+  NSPoint tilt = [event tilt];
+  result.touches[0].tilt_x = lround(tilt.x * 90);
+  result.touches[0].tilt_y = lround(tilt.y * 90);
+  result.touches[0].tangential_pressure = [event tangentialPressure];
+  // NSEvent spec doesn't specify the range of rotation, we make sure that
+  // this value is in the range of [0,359].
+  int twist = (int)[event rotation];
+  twist = twist % 360;
+  if (twist < 0)
+    twist += 360;
+  result.touches[0].twist = twist;
+  float rotation_angle = twist % 180;
+  if (rotation_angle > 90)
+    rotation_angle = 180.f - rotation_angle;
+  result.touches[0].rotation_angle = rotation_angle;
+  return result;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/input/web_input_event_builders_mac_unittest.mm b/content/browser/renderer_host/input/web_input_event_builders_mac_unittest.mm
index 2b619cc0..32f080a 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_mac_unittest.mm
+++ b/content/browser/renderer_host/input/web_input_event_builders_mac_unittest.mm
@@ -66,6 +66,29 @@
                            keyCode:key_code];
 }
 
+NSEvent* BuildFakeMouseEvent(CGEventType mouse_type,
+                             CGPoint location,
+                             CGMouseButton button,
+                             CGEventMouseSubtype subtype,
+                             float rotation = 0.0,
+                             float pressure = 0.0,
+                             float tilt_x = 0.0,
+                             float tilt_y = 0.0,
+                             float tangential_pressure = 0.0) {
+  CGEventRef cg_event =
+      CGEventCreateMouseEvent(NULL, mouse_type, location, button);
+  CGEventSetIntegerValueField(cg_event, kCGMouseEventSubtype, subtype);
+  CGEventSetDoubleValueField(cg_event, kCGTabletEventRotation, rotation);
+  CGEventSetDoubleValueField(cg_event, kCGMouseEventPressure, pressure);
+  CGEventSetDoubleValueField(cg_event, kCGTabletEventTiltX, tilt_x);
+  CGEventSetDoubleValueField(cg_event, kCGTabletEventTiltY, tilt_y);
+  CGEventSetDoubleValueField(cg_event, kCGTabletEventTangentialPressure,
+                             tangential_pressure);
+  NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
+  CFRelease(cg_event);
+  return event;
+}
+
 }  // namespace
 
 // Test that arrow keys don't have numpad modifier set.
@@ -647,3 +670,111 @@
   EXPECT_EQ(web_event.PositionInWidget().y, ui_event.y());
   [window close];
 }
+
+// Test if the value of twist and rotation_angle are set correctly when the
+// NSEvent's rotation is less than 90.
+TEST(WebInputEventBuilderMacTest, TouchEventsWithPointerTypePenRotationLess90) {
+  NSEvent* mac_event =
+      BuildFakeMouseEvent(kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+                          kCGEventMouseSubtypeTabletPoint, 60);
+  // Create a dummy window, but don't show it. It will be released when closed.
+  NSWindow* window =
+      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                  styleMask:NSBorderlessWindowMask
+                                    backing:NSBackingStoreBuffered
+                                      defer:NO];
+  blink::WebTouchEvent touch_event =
+      content::WebTouchEventBuilder::Build(mac_event, [window contentView]);
+  EXPECT_EQ(60, touch_event.touches[0].twist);
+  EXPECT_EQ(60, touch_event.touches[0].rotation_angle);
+}
+
+// Test if the value of twist and rotation_angle are set correctly when the
+// NSEvent's rotation is between 90 and 180.
+TEST(WebInputEventBuilderMacTest,
+     TouchEventsWithPointerTypePenRotationLess180) {
+  NSEvent* mac_event =
+      BuildFakeMouseEvent(kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+                          kCGEventMouseSubtypeTabletPoint, 160);
+  // Create a dummy window, but don't show it. It will be released when closed.
+  NSWindow* window =
+      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                  styleMask:NSBorderlessWindowMask
+                                    backing:NSBackingStoreBuffered
+                                      defer:NO];
+  blink::WebTouchEvent touch_event =
+      content::WebTouchEventBuilder::Build(mac_event, [window contentView]);
+  EXPECT_EQ(160, touch_event.touches[0].twist);
+  EXPECT_EQ(20, touch_event.touches[0].rotation_angle);
+}
+
+// Test if the value of twist and rotation_angle are set correctly when the
+// NSEvent's rotation is between 180 and 360.
+TEST(WebInputEventBuilderMacTest,
+     TouchEventsWithPointerTypePenRotationLess360) {
+  NSEvent* mac_event =
+      BuildFakeMouseEvent(kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+                          kCGEventMouseSubtypeTabletPoint, 260);
+  // Create a dummy window, but don't show it. It will be released when closed.
+  NSWindow* window =
+      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                  styleMask:NSBorderlessWindowMask
+                                    backing:NSBackingStoreBuffered
+                                      defer:NO];
+  blink::WebTouchEvent touch_event =
+      content::WebTouchEventBuilder::Build(mac_event, [window contentView]);
+  EXPECT_EQ(260, touch_event.touches[0].twist);
+  EXPECT_EQ(80, touch_event.touches[0].rotation_angle);
+}
+
+// Test if the value of twist and rotation_angle are set correctly when the
+// NSEvent's rotation is greater than 360.
+TEST(WebInputEventBuilderMacTest,
+     TouchEventsWithPointerTypePenRotationGreater360) {
+  NSEvent* mac_event =
+      BuildFakeMouseEvent(kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+                          kCGEventMouseSubtypeTabletPoint, 390);
+  // Create a dummy window, but don't show it. It will be released when closed.
+  NSWindow* window =
+      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                  styleMask:NSBorderlessWindowMask
+                                    backing:NSBackingStoreBuffered
+                                      defer:NO];
+  blink::WebTouchEvent touch_event =
+      content::WebTouchEventBuilder::Build(mac_event, [window contentView]);
+  EXPECT_EQ(30, touch_event.touches[0].twist);
+  EXPECT_EQ(30, touch_event.touches[0].rotation_angle);
+}
+
+// Test if all the values of a WebTouchEvent are set correctly.
+TEST(WebInputEventBuilderMacTest, BuildWebTouchEvents) {
+  NSEvent* mac_event = BuildFakeMouseEvent(
+      kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+      kCGEventMouseSubtypeTabletPoint, /* rotation */ 60,
+      /* pressure */ 0.3, /* tilt_x */ 0.5, /* tilt_y */ 0.6,
+      /* tangential_pressure */ 0.7);
+  // Create a dummy window, but don't show it. It will be released when closed.
+  NSWindow* window =
+      [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
+                                  styleMask:NSBorderlessWindowMask
+                                    backing:NSBackingStoreBuffered
+                                      defer:NO];
+  blink::WebTouchEvent touch_event =
+      content::WebTouchEventBuilder::Build(mac_event, [window contentView]);
+  EXPECT_EQ(blink::WebInputEvent::kTouchStart, touch_event.GetType());
+  EXPECT_FALSE(touch_event.hovering);
+  EXPECT_EQ(1U, touch_event.touches_length);
+  EXPECT_EQ(blink::WebFloatPoint(6, 9),
+            touch_event.touches[0].PositionInScreen());
+  EXPECT_EQ(blink::WebTouchPoint::kStatePressed, touch_event.touches[0].state);
+  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
+            touch_event.touches[0].pointer_type);
+  EXPECT_EQ(0, touch_event.touches[0].id);
+  EXPECT_FLOAT_EQ(0.3, std::round(touch_event.touches[0].force * 10) / 10);
+  EXPECT_EQ(0.5 * 90, touch_event.touches[0].tilt_x);
+  EXPECT_EQ(0.6 * 90, touch_event.touches[0].tilt_y);
+  EXPECT_FLOAT_EQ(
+      0.7, std::round(touch_event.touches[0].tangential_pressure * 10) / 10);
+  EXPECT_EQ(60, touch_event.touches[0].twist);
+  EXPECT_EQ(60, touch_event.touches[0].rotation_angle);
+}
\ No newline at end of file
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 1f3e2b3..e00ac640 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -419,7 +419,7 @@
   }
 
   enable_surface_synchronization_ = features::IsSurfaceSynchronizationEnabled();
-  enable_viz_ = base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+  enable_viz_ = features::IsVizDisplayCompositorEnabled();
 
   if (!enable_viz_) {
 #if !defined(OS_ANDROID)
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 5a3c7ffa..4bb8cb5 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1197,11 +1197,6 @@
       map_size_key,
       base::StringPrintf("%u", static_cast<int>(owner_map_.size())));
 
-  if (events_being_flushed_) {
-    touchscreen_gesture_target_.target->host()
-        ->input_router()
-        ->ForceSetTouchActionAuto();
-  }
   touchscreen_gesture_target_.target->ProcessGestureEvent(event, latency);
 
   if (gesture_event.GetType() == blink::WebInputEvent::kGestureFlingStart)
@@ -1373,11 +1368,6 @@
   return RenderWidgetTargetResult();
 }
 
-void RenderWidgetHostInputEventRouter::SetEventsBeingFlushed(
-        bool events_being_flushed) {
-  events_being_flushed_ = events_being_flushed;
-}
-
 void RenderWidgetHostInputEventRouter::DispatchEventToTarget(
     RenderWidgetHostViewBase* root_view,
     RenderWidgetHostViewBase* target,
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index 1ee53ae..fd990d37 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -280,9 +280,6 @@
       const blink::WebInputEvent& event,
       const ui::LatencyInfo& latency,
       const base::Optional<gfx::PointF>& target_location) override;
-  // Notify whether the events in the queue are being flushed due to touch ack
-  // timeout, or the flushing has completed.
-  void SetEventsBeingFlushed(bool events_being_flushed) override;
 
   FrameSinkIdOwnerMap owner_map_;
   TargetMap touchscreen_gesture_target_map_;
@@ -322,7 +319,6 @@
 
   std::unique_ptr<RenderWidgetTargeter> event_targeter_;
   bool use_viz_hit_test_ = false;
-  bool events_being_flushed_ = false;
 
   std::unique_ptr<TouchEmulator> touch_emulator_;
 
diff --git a/content/browser/renderer_host/render_widget_host_ns_view_client.h b/content/browser/renderer_host/render_widget_host_ns_view_client.h
index 92f4fd7..c7e67f8d 100644
--- a/content/browser/renderer_host/render_widget_host_ns_view_client.h
+++ b/content/browser/renderer_host/render_widget_host_ns_view_client.h
@@ -53,6 +53,8 @@
   // Forward events to the renderer or the input router, as appropriate.
   virtual void RouteOrProcessMouseEvent(
       const blink::WebMouseEvent& web_event) = 0;
+  virtual void RouteOrProcessTouchEvent(
+      const blink::WebTouchEvent& web_event) = 0;
   virtual void RouteOrProcessWheelEvent(
       const blink::WebMouseWheelEvent& web_event) = 0;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 0eec5c7..084e9e7 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1941,8 +1941,7 @@
 
   delegated_frame_host_client_ =
       std::make_unique<DelegatedFrameHostClientAura>(this);
-  const bool enable_viz =
-      base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+  const bool enable_viz = features::IsVizDisplayCompositorEnabled();
   delegated_frame_host_ = std::make_unique<DelegatedFrameHost>(
       frame_sink_id_, delegated_frame_host_client_.get(), enable_viz,
       false /* should_register_frame_sink_id */);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 053df99..78c4e5f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -499,8 +499,7 @@
       RenderWidgetHostViewAura* view,
       std::unique_ptr<DelegatedFrameHostClient> delegated_frame_host_client) {
     view->delegated_frame_host_client_ = std::move(delegated_frame_host_client);
-    const bool enable_viz =
-        base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+    const bool enable_viz = features::IsVizDisplayCompositorEnabled();
     view->delegated_frame_host_ = nullptr;
     view->delegated_frame_host_ = std::make_unique<DelegatedFrameHost>(
         view->frame_sink_id_, view->delegated_frame_host_client_.get(),
@@ -3074,7 +3073,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, TwoOutputSurfaces) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       !features::IsAshInBrowserProcess()) {
     return;
   }
@@ -3381,7 +3380,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, OutputSurfaceIdChange) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       !features::IsAshInBrowserProcess()) {
     return;
   }
@@ -3531,7 +3530,7 @@
        CompositorFrameSinkChange) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       !features::IsAshInBrowserProcess()) {
     return;
   }
@@ -3875,7 +3874,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, ForwardsBeginFrameAcks) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       !features::IsAshInBrowserProcess()) {
     return;
   }
@@ -5816,7 +5815,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, HitTestRegionListSubmitted) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       !features::IsAshInBrowserProcess()) {
     return;
   }
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 46bfcdb2..cea6a0b 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -71,8 +71,7 @@
           base::checked_cast<uint32_t>(widget_host->GetProcess()->GetID()),
           base::checked_cast<uint32_t>(widget_host->GetRoutingID())),
       frame_connector_(nullptr),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       weak_factory_(this) {
   if (!features::IsAshInBrowserProcess()) {
     // In Mus the RenderFrameProxy will eventually assign a viz::FrameSinkId
diff --git a/content/browser/renderer_host/render_widget_host_view_cocoa.h b/content/browser/renderer_host/render_widget_host_view_cocoa.h
index b2d9cec..83af25a 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.h
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.h
@@ -189,6 +189,12 @@
   // The filter used to guide touch events towards a horizontal or vertical
   // orientation.
   content::MouseWheelRailsFilterMac mouseWheelFilter_;
+
+  // Whether the direct manipulation feature is enabled.
+  bool direct_manipulation_enabled_;
+
+  // Whether the pen's tip is in contact with the stylus digital tablet.
+  bool has_pen_contact_;
 }
 
 @property(nonatomic, assign) NSRange markedRange;
diff --git a/content/browser/renderer_host/render_widget_host_view_cocoa.mm b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
index 8b84322..92f940e 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.mm
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
@@ -24,6 +24,7 @@
 #import "ui/base/clipboard/clipboard_util_mac.h"
 #import "ui/base/cocoa/appkit_utils.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/screen.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -41,10 +42,12 @@
 using content::WebGestureEventBuilder;
 using content::WebMouseEventBuilder;
 using content::WebMouseWheelEventBuilder;
+using content::WebTouchEventBuilder;
 using blink::WebInputEvent;
 using blink::WebMouseEvent;
 using blink::WebMouseWheelEvent;
 using blink::WebGestureEvent;
+using blink::WebTouchEvent;
 
 namespace {
 
@@ -87,6 +90,10 @@
       const blink::WebMouseEvent& web_event) override {
     client_->RouteOrProcessMouseEvent(TranslateEvent(web_event));
   }
+  void RouteOrProcessTouchEvent(
+      const blink::WebTouchEvent& web_event) override {
+    client_->RouteOrProcessTouchEvent(TranslateEvent(web_event));
+  }
   void RouteOrProcessWheelEvent(
       const blink::WebMouseWheelEvent& web_event) override {
     client_->RouteOrProcessWheelEvent(TranslateEvent(web_event));
@@ -223,6 +230,9 @@
     isStylusEnteringProximity_ = false;
     keyboardLockActive_ = false;
     textInputType_ = ui::TEXT_INPUT_TYPE_NONE;
+    direct_manipulation_enabled_ =
+        base::FeatureList::IsEnabled(features::kDirectManipulationStylus);
+    has_pen_contact_ = false;
   }
   return self;
 }
@@ -542,9 +552,37 @@
   if (type == NSMouseMoved)
     cursorHidden_ = NO;
 
-  WebMouseEvent event =
-      WebMouseEventBuilder::Build(theEvent, self, pointerType_);
-  localClient_->RouteOrProcessMouseEvent(event);
+  bool send_touch =
+      direct_manipulation_enabled_ &&
+      pointerType_ == blink::WebPointerProperties::PointerType::kPen;
+
+  // Send touch events when the pen is in contact with the tablet.
+  if (send_touch) {
+    // Because the NSLeftMouseUp event's buttonMask is not
+    // NSEventButtonMaskPenTip, we read |has_pen_contact_| to ensure a
+    // TouchRelease is sent appropriately at the end when the stylus is
+    // no longer in contact with the digitizer.
+    send_touch = has_pen_contact_;
+    if (type == NSLeftMouseDown || type == NSLeftMouseUp ||
+        type == NSLeftMouseDragged) {
+      NSEventButtonMask buttonMask = [theEvent buttonMask];
+      if (buttonMask == NSEventButtonMaskPenTip) {
+        DCHECK(type != NSLeftMouseUp);
+        send_touch = has_pen_contact_ = true;
+      } else {
+        has_pen_contact_ = false;
+      }
+    }
+  }
+
+  if (!send_touch) {
+    WebMouseEvent event =
+        WebMouseEventBuilder::Build(theEvent, self, pointerType_);
+    localClient_->RouteOrProcessMouseEvent(event);
+  } else {
+    WebTouchEvent event = WebTouchEventBuilder::Build(theEvent, self);
+    localClient_->RouteOrProcessTouchEvent(event);
+  }
 }
 
 - (void)tabletEvent:(NSEvent*)theEvent {
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 052bcb27..534e667 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -308,6 +308,7 @@
       const ui::LatencyInfo& latency_info,
       const std::vector<EditCommand>& commands) override;
   void RouteOrProcessMouseEvent(const blink::WebMouseEvent& web_event) override;
+  void RouteOrProcessTouchEvent(const blink::WebTouchEvent& web_event) override;
   void RouteOrProcessWheelEvent(
       const blink::WebMouseWheelEvent& web_event) override;
   void ForwardMouseEvent(const blink::WebMouseEvent& web_event) override;
@@ -339,6 +340,7 @@
       bool skip_in_browser,
       const std::vector<EditCommand>& commands) override;
   void RouteOrProcessMouseEvent(std::unique_ptr<InputEvent> event) override;
+  void RouteOrProcessTouchEvent(std::unique_ptr<InputEvent> event) override;
   void RouteOrProcessWheelEvent(std::unique_ptr<InputEvent> event) override;
   void ForwardMouseEvent(std::unique_ptr<InputEvent> event) override;
   void ForwardWheelEvent(std::unique_ptr<InputEvent> event) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 0703dd02c..b3bba4e 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1488,6 +1488,24 @@
   }
 }
 
+void RenderWidgetHostViewMac::RouteOrProcessTouchEvent(
+    const blink::WebTouchEvent& const_web_event) {
+  blink::WebTouchEvent web_event = const_web_event;
+  ui::FilteredGestureProvider::TouchHandlingResult result =
+      gesture_provider_.OnTouchEvent(MotionEventWeb(web_event));
+  if (!result.succeeded)
+    return;
+
+  ui::LatencyInfo latency_info(ui::SourceEventType::OTHER);
+  latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
+  if (ShouldRouteEvent(web_event)) {
+    host()->delegate()->GetInputEventRouter()->RouteTouchEvent(this, &web_event,
+                                                               latency_info);
+  } else {
+    ProcessTouchEvent(web_event, latency_info);
+  }
+}
+
 void RenderWidgetHostViewMac::RouteOrProcessWheelEvent(
     const blink::WebMouseWheelEvent& const_web_event) {
   blink::WebMouseWheelEvent web_event = const_web_event;
@@ -1850,6 +1868,7 @@
   ForwardKeyboardEventWithCommands(native_event, input_event->latency_info,
                                    commands);
 }
+
 void RenderWidgetHostViewMac::RouteOrProcessMouseEvent(
     std::unique_ptr<InputEvent> input_event) {
   if (!input_event || !input_event->web_event ||
@@ -1863,6 +1882,19 @@
   RouteOrProcessMouseEvent(mouse_event);
 }
 
+void RenderWidgetHostViewMac::RouteOrProcessTouchEvent(
+    std::unique_ptr<InputEvent> input_event) {
+  if (!input_event || !input_event->web_event ||
+      !blink::WebInputEvent::IsTouchEventType(
+          input_event->web_event->GetType())) {
+    DLOG(ERROR) << "Absent or non-TouchEventType event.";
+    return;
+  }
+  const blink::WebTouchEvent& touch_event =
+      static_cast<const blink::WebTouchEvent&>(*input_event->web_event);
+  RouteOrProcessTouchEvent(touch_event);
+}
+
 void RenderWidgetHostViewMac::RouteOrProcessWheelEvent(
     std::unique_ptr<InputEvent> input_event) {
   if (!input_event || !input_event->web_event ||
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 1eef2120..01400ba3 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -52,6 +52,7 @@
 #include "ui/base/cocoa/secure_password_input.h"
 #import "ui/base/test/cocoa_helper.h"
 #import "ui/base/test/scoped_fake_nswindow_focus.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/recyclable_compositor_mac.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/blink_features.h"
@@ -134,20 +135,30 @@
   for (auto& event : events)
     result.push_back(event->name());
   return base::JoinString(result, " ");
-    }
+}
 
-    blink::WebPointerProperties::PointerType GetPointerType(
-        const MockWidgetInputHandler::MessageVector& events) {
-      EXPECT_EQ(events.size(), 1U);
-      MockWidgetInputHandler::DispatchedEventMessage* event =
-          events[0]->ToEvent();
-      if (event && blink::WebInputEvent::IsMouseEventType(
-                       event->Event()->web_event->GetType())) {
-        return static_cast<const blink::WebMouseEvent*>(
-                   event->Event()->web_event.get())
-            ->pointer_type;
-      }
-      return blink::WebPointerProperties::PointerType::kUnknown;
+blink::WebPointerProperties::PointerType GetPointerType(
+    const MockWidgetInputHandler::MessageVector& events) {
+  EXPECT_EQ(events.size(), 1U);
+  MockWidgetInputHandler::DispatchedEventMessage* event = events[0]->ToEvent();
+  if (!event)
+    return blink::WebPointerProperties::PointerType::kUnknown;
+
+  if (blink::WebInputEvent::IsMouseEventType(
+          event->Event()->web_event->GetType())) {
+    return static_cast<const blink::WebMouseEvent*>(
+               event->Event()->web_event.get())
+        ->pointer_type;
+  }
+
+  if (blink::WebInputEvent::IsTouchEventType(
+          event->Event()->web_event->GetType())) {
+    return static_cast<const blink::WebTouchEvent*>(
+               event->Event()->web_event.get())
+        ->touches[0]
+        .pointer_type;
+  }
+  return blink::WebPointerProperties::PointerType::kUnknown;
 }
 
 NSEvent* MockTabletEventWithParams(CGEventType type,
@@ -168,12 +179,20 @@
                                   CGPoint location,
                                   CGMouseButton button,
                                   CGEventMouseSubtype subtype,
-                                  bool is_entering_proximity = false) {
+                                  bool is_entering_proximity = false,
+                                  bool is_pen_tip = false) {
   CGEventRef cg_event =
       CGEventCreateMouseEvent(NULL, mouse_type, location, button);
   CGEventSetIntegerValueField(cg_event, kCGMouseEventSubtype, subtype);
   CGEventSetIntegerValueField(cg_event, kCGTabletProximityEventEnterProximity,
                               is_entering_proximity);
+  CGEventSetIntegerValueField(cg_event, kCGTabletEventRotation, 300);
+  if (is_pen_tip)
+    CGEventSetIntegerValueField(cg_event, kCGTabletEventPointButtons, 1);
+  CGEventTimestamp timestamp =
+      (ui::EventTimeForNow() - base::TimeTicks()).InMicroseconds() *
+      base::Time::kNanosecondsPerMicrosecond;
+  CGEventSetTimestamp(cg_event, timestamp);
   NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
   CFRelease(cg_event);
   return event;
@@ -360,6 +379,9 @@
 
   void SetUp() override {
     RenderViewHostImplTestHarness::SetUp();
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(features::kDirectManipulationStylus);
+
     gpu::ImageTransportSurface::SetAllowOSMesaForTesting(true);
 
     browser_context_ = std::make_unique<TestBrowserContext>();
@@ -946,6 +968,77 @@
             GetPointerType(events));
 }
 
+TEST_F(RenderWidgetHostViewMacTest, PointerEventWithPenTypeSendAsTouch) {
+  // Send a NSEvent of NSTabletProximity type which has a device type of pen.
+  NSEvent* event = MockTabletEventWithParams(kCGEventTabletProximity, true,
+                                             NSPenPointingDevice);
+  [rwhv_mac_->cocoa_view() tabletEvent:event];
+  // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
+  base::RunLoop().RunUntilIdle();
+  static_cast<RenderWidgetHostImpl*>(rwhv_mac_->GetRenderWidgetHost())
+      ->input_router()
+      ->ForceSetTouchActionAuto();
+
+  event = MockMouseEventWithParams(
+      kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
+      kCGEventMouseSubtypeTabletPoint, false, true);
+  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  base::RunLoop().RunUntilIdle();
+  MockWidgetInputHandler::MessageVector events =
+      host_->GetAndResetDispatchedMessages();
+  ASSERT_EQ("TouchStart", GetMessageNames(events));
+  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
+            GetPointerType(events));
+  events.clear();
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+
+  event = MockMouseEventWithParams(
+      kCGEventLeftMouseDragged, {16, 29}, kCGMouseButtonLeft,
+      kCGEventMouseSubtypeTabletPoint, false, true);
+  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+  ASSERT_EQ("TouchMove", GetMessageNames(events));
+  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
+            GetPointerType(events));
+
+  events.clear();
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+
+  event = MockMouseEventWithParams(kCGEventLeftMouseUp, {16, 29},
+                                   kCGMouseButtonLeft,
+                                   kCGEventMouseSubtypeTabletPoint, false);
+  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+  ASSERT_EQ("TouchEnd GestureScrollEnd", GetMessageNames(events));
+  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
+            static_cast<const blink::WebTouchEvent*>(
+                events[0]->ToEvent()->Event()->web_event.get())
+                ->touches[0]
+                .pointer_type);
+
+  events.clear();
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+
+  event =
+      MockMouseEventWithParams(kCGEventLeftMouseDown, {6, 9},
+                               kCGMouseButtonLeft, kCGEventMouseSubtypeDefault);
+  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+  ASSERT_EQ("MouseDown", GetMessageNames(events));
+  EXPECT_EQ(blink::WebPointerProperties::PointerType::kMouse,
+            GetPointerType(events));
+
+  events.clear();
+  base::RunLoop().RunUntilIdle();
+  events = host_->GetAndResetDispatchedMessages();
+}
+
 TEST_F(RenderWidgetHostViewMacTest, SendMouseMoveOnShowingContextMenu) {
   rwhv_mac_->SetShowingContextMenu(true);
   base::RunLoop().RunUntilIdle();
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index 1239e5d..4a5acc5 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -215,7 +215,6 @@
 }
 
 void RenderWidgetTargeter::FlushEventQueue() {
-  bool events_being_flushed = false;
   while (!request_in_flight_ && !requests_.empty()) {
     auto request = std::move(requests_.front());
     requests_.pop();
@@ -225,16 +224,9 @@
       continue;
     }
     request.tracker->Stop();
-    // Only notify the delegate once that the current event queue is being
-    // flushed. Once all the events are flushed, notify the delegate again.
-    if (!events_being_flushed) {
-      delegate_->SetEventsBeingFlushed(true);
-      events_being_flushed = true;
-    }
     FindTargetAndDispatch(request.root_view.get(), *request.event,
                           request.latency);
   }
-  delegate_->SetEventsBeingFlushed(false);
 }
 
 void RenderWidgetTargeter::FoundFrameSinkId(
diff --git a/content/browser/renderer_host/render_widget_targeter.h b/content/browser/renderer_host/render_widget_targeter.h
index 6399b2ad..e9b3e2a 100644
--- a/content/browser/renderer_host/render_widget_targeter.h
+++ b/content/browser/renderer_host/render_widget_targeter.h
@@ -70,8 +70,6 @@
         const ui::LatencyInfo& latency,
         const base::Optional<gfx::PointF>& target_location) = 0;
 
-    virtual void SetEventsBeingFlushed(bool events_being_flushed) = 0;
-
     virtual RenderWidgetHostViewBase* FindViewFromFrameSinkId(
         const viz::FrameSinkId& frame_sink_id) const = 0;
   };
diff --git a/content/browser/renderer_host/ui_events_helper.cc b/content/browser/renderer_host/ui_events_helper.cc
index 6ea4ed5..8b3330f 100644
--- a/content/browser/renderer_host/ui_events_helper.cc
+++ b/content/browser/renderer_host/ui_events_helper.cc
@@ -65,8 +65,7 @@
 
   int flags = ui::WebEventModifiersToEventFlags(touch.GetModifiers());
   base::TimeTicks timestamp = touch.TimeStamp();
-  unsigned count = 0;
-  for (unsigned i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; ++i) {
+  for (unsigned i = 0; i < touch.touches_length; ++i) {
     const blink::WebTouchPoint& point = touch.touches[i];
     if (WebTouchPointStateToEventType(point.state) != type)
       continue;
@@ -85,8 +84,6 @@
     uievent->set_root_location_f(location);
     uievent->set_latency(touch_with_latency.latency);
     list->push_back(std::move(uievent));
-    if (++count >= touch.touches_length)
-      break;
   }
   return true;
 }
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index d60314085..57ccbda 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1176,9 +1176,16 @@
             rwhv_nested->GetCompositorViewportPixelSize());
 }
 
+// Disable on Android: crbug/851049
+#if defined(OS_ANDROID)
+#define MAYBE_NoResizeAfterIframeLoad DISABLED_NoResizeAfterIframeLoad
+#else
+#define MAYBE_NoResizeAfterIframeLoad NoResizeAfterIframeLoad
+#endif
 // Verify an OOPIF resize handler doesn't fire immediately after load without
 // the frame having been resized. See https://crbug.com/826457.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NoResizeAfterIframeLoad) {
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+                       MAYBE_NoResizeAfterIframeLoad) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
@@ -1190,6 +1197,7 @@
   GURL site_url =
       embedded_test_server()->GetURL("b.com", "/page_with_resize_handler.html");
   NavigateFrameToURL(iframe, site_url);
+  base::RunLoop().RunUntilIdle();
 
   int resizes = -1;
   EXPECT_TRUE(ExecuteScriptAndExtractInt(
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 11ee010..7150c41 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -16,7 +16,6 @@
 #include "components/viz/common/features.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/renderer_host/cursor_manager.h"
-#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
 #include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
 #include "content/browser/renderer_host/input/touch_emulator.h"
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
@@ -1315,99 +1314,6 @@
   RunTest(TouchActionBubbling);
 }
 
-#if defined(OS_ANDROID) || defined(USE_AURA)
-namespace {
-// This function is used in TouchActionAckTimeout and
-// SubframeGestureEventRouting, which is defined either under Android or Aura.
-void OnSyntheticGestureCompleted(scoped_refptr<MessageLoopRunner> runner,
-                                 SyntheticGesture::Result result) {
-  EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
-  runner->Quit();
-}
-
-}  // namespace
-#endif  // defined(OS_ANDROID) || defined(USE_AURA)
-
-// Regression test for https://crbug.com/851644. The test passes as long as it
-// doesn't crash.
-// Touch action ack timeout is enabled on Android only.
-#if defined(OS_ANDROID)
-IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       TouchActionAckTimeout) {
-  GURL main_url(
-      embedded_test_server()->GetURL("/frame_tree/page_with_janky_frame.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), main_url));
-  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  ASSERT_EQ(1U, root->child_count());
-  GURL frame_url(embedded_test_server()->GetURL(
-      "baz.com", "/page_with_touch_start_janking_main_thread.html"));
-  auto* child_frame_host = root->child_at(0)->current_frame_host();
-
-  RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
-      root->current_frame_host()->GetRenderWidgetHost()->GetView());
-  RenderWidgetHostViewChildFrame* rwhv_child =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          child_frame_host->GetRenderWidgetHost()->GetView());
-
-  WaitForHitTestDataOrChildSurfaceReady(child_frame_host);
-
-  // Compute the point so that the gesture event can target the child frame.
-  const gfx::Rect root_bounds = rwhv_root->GetViewBounds();
-  const gfx::Rect child_bounds = rwhv_child->GetViewBounds();
-  RenderFrameSubmissionObserver render_frame_submission_observer(
-      shell()->web_contents());
-  const float page_scale_factor =
-      render_frame_submission_observer.LastRenderFrameMetadata()
-          .page_scale_factor;
-  const gfx::PointF point_in_child(
-      (child_bounds.x() - root_bounds.x() + 25) * page_scale_factor,
-      (child_bounds.y() - root_bounds.y() + 25) * page_scale_factor);
-
-  SyntheticSmoothScrollGestureParams params;
-  params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  params.anchor = gfx::PointF(point_in_child.x(), point_in_child.y());
-  params.distances.push_back(gfx::Vector2dF(0, -10));
-  // Make this scroll slow so that the second scroll will be queued even before
-  // this one ends.
-  params.speed_in_pixels_s = 1000;
-  std::unique_ptr<SyntheticSmoothScrollGesture> gesture(
-      new SyntheticSmoothScrollGesture(params));
-
-  scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner();
-  RenderWidgetHostImpl* render_widget_host =
-      root->current_frame_host()->GetRenderWidgetHost();
-  render_widget_host->QueueSyntheticGesture(
-      std::move(gesture), base::BindOnce(OnSyntheticGestureCompleted, runner));
-  // The first gesture takes 100ms, so wait for 120ms to ensure that it has
-  // finished.
-  runner->Run();
-  std::unique_ptr<RenderFrameSubmissionObserver> frame_observer =
-          std::make_unique<RenderFrameSubmissionObserver>(
-                  child_frame_host->GetRenderWidgetHost()->render_frame_metadata_provider());
-  frame_observer->WaitForAnyFrameSubmission();
-
-  SyntheticSmoothScrollGestureParams params2;
-  params2.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
-  params2.anchor = gfx::PointF(point_in_child.x(), point_in_child.y());
-  params2.distances.push_back(gfx::Vector2dF(0, -10));
-  params2.speed_in_pixels_s = 100000;
-  std::unique_ptr<SyntheticSmoothScrollGesture> gesture2(
-      new SyntheticSmoothScrollGesture(params2));
-  render_widget_host->QueueSyntheticGesture(
-      std::move(gesture2), base::BindOnce(OnSyntheticGestureCompleted, runner));
-
-  runner->Run();
-  runner = nullptr;
-
-  // Give enough time to make sure all gesture are flushed and handled.
-  base::RunLoop run_loop;
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, run_loop.QuitClosure(),
-      base::TimeDelta::FromMilliseconds(2500));
-  run_loop.Run();
-}
-#endif  // defined(OS_ANDROID)
-
 #if defined(USE_AURA) || defined(OS_ANDROID)
 
 // When unconsumed scrolls in a child bubble to the root and start an
@@ -1891,7 +1797,7 @@
   // TODO(sunxd): Hit test regions are not submitted for overlapping surfaces,
   // causing /2 to fail outside of Viz. https::/crbug.com/846798
   if (base::FeatureList::IsEnabled(features::kEnableVizHitTestSurfaceLayer) &&
-      !base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+      !features::IsVizDisplayCompositorEnabled()) {
     return;
   }
 
@@ -3142,6 +3048,17 @@
             render_widget_host->input_router()->AllowedTouchAction());
 }
 
+namespace {
+
+// Declared here to be close to the SubframeGestureEventRouting test.
+void OnSyntheticGestureCompleted(scoped_refptr<MessageLoopRunner> runner,
+                                 SyntheticGesture::Result result) {
+  EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+  runner->Quit();
+}
+
+}  // anonymous namespace
+
 // https://crbug.com/592320
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
                        DISABLED_SubframeGestureEventRouting) {
diff --git a/content/common/input/synthetic_web_input_event_builders.cc b/content/common/input/synthetic_web_input_event_builders.cc
index 3bf1ed2b..22f090f 100644
--- a/content/common/input/synthetic_web_input_event_builders.cc
+++ b/content/common/input/synthetic_web_input_event_builders.cc
@@ -162,6 +162,7 @@
 SyntheticWebTouchEvent::SyntheticWebTouchEvent() : WebTouchEvent() {
   unique_touch_event_id = ui::GetNextTouchEventId();
   SetTimestamp(ui::EventTimeForNow());
+  pointer_id_ = 0;
 }
 
 void SyntheticWebTouchEvent::ResetPoints() {
@@ -198,7 +199,7 @@
   if (index == -1)
     return -1;
   WebTouchPoint& point = touches[index];
-  point.id = index;
+  point.id = pointer_id_++;
   point.SetPositionInWidget(x, y);
   point.SetPositionInScreen(x, y);
   point.state = WebTouchPoint::kStatePressed;
@@ -210,7 +211,7 @@
   point.pointer_type = blink::WebPointerProperties::PointerType::kTouch;
   ++touches_length;
   WebTouchEventTraits::ResetType(WebInputEvent::kTouchStart, TimeStamp(), this);
-  return point.id;
+  return index;
 }
 
 void SyntheticWebTouchEvent::MovePoint(int index, float x, float y) {
diff --git a/content/common/input/synthetic_web_input_event_builders.h b/content/common/input/synthetic_web_input_event_builders.h
index 3247d0d..b101ce7 100644
--- a/content/common/input/synthetic_web_input_event_builders.h
+++ b/content/common/input/synthetic_web_input_event_builders.h
@@ -97,6 +97,11 @@
   void SetTimestamp(base::TimeTicks timestamp);
 
   int FirstFreeIndex();
+
+ private:
+  // A pointer id of each touch pointer. Every time when a pointer is pressed
+  // the screen, it will be assigned to a new pointer id.
+  unsigned pointer_id_;
 };
 
 }  // namespace content
diff --git a/content/common/input/synthetic_web_input_event_builders_unittest.cc b/content/common/input/synthetic_web_input_event_builders_unittest.cc
new file mode 100644
index 0000000..e93d32c
--- /dev/null
+++ b/content/common/input/synthetic_web_input_event_builders_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/input/touch_event_stream_validator.h"
+
+#include <stddef.h>
+
+#include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/common/input/web_touch_event_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using blink::WebFloatPoint;
+using blink::WebInputEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+
+TEST(SyntheticWebInputEventBuilders, BuildWebTouchEvent) {
+  SyntheticWebTouchEvent event;
+
+  event.PressPoint(1, 2);
+  EXPECT_EQ(1U, event.touches_length);
+  EXPECT_EQ(0, event.touches[0].id);
+  EXPECT_EQ(WebTouchPoint::kStatePressed, event.touches[0].state);
+  EXPECT_EQ(WebFloatPoint(1, 2), event.touches[0].PositionInWidget());
+  event.ResetPoints();
+
+  event.PressPoint(3, 4);
+  EXPECT_EQ(2U, event.touches_length);
+  EXPECT_EQ(1, event.touches[1].id);
+  EXPECT_EQ(WebTouchPoint::kStatePressed, event.touches[1].state);
+  EXPECT_EQ(WebFloatPoint(3, 4), event.touches[1].PositionInWidget());
+  event.ResetPoints();
+
+  event.MovePoint(1, 5, 6);
+  EXPECT_EQ(2U, event.touches_length);
+  EXPECT_EQ(1, event.touches[1].id);
+  EXPECT_EQ(WebTouchPoint::kStateMoved, event.touches[1].state);
+  EXPECT_EQ(WebFloatPoint(5, 6), event.touches[1].PositionInWidget());
+  event.ResetPoints();
+
+  event.ReleasePoint(0);
+  EXPECT_EQ(2U, event.touches_length);
+  EXPECT_EQ(0, event.touches[0].id);
+  EXPECT_EQ(WebTouchPoint::kStateReleased, event.touches[0].state);
+  event.ResetPoints();
+
+  event.MovePoint(1, 7, 8);
+  EXPECT_EQ(1U, event.touches_length);
+  EXPECT_EQ(1, event.touches[1].id);
+  EXPECT_EQ(WebTouchPoint::kStateMoved, event.touches[1].state);
+  EXPECT_EQ(WebFloatPoint(7, 8), event.touches[1].PositionInWidget());
+  EXPECT_EQ(WebTouchPoint::kStateUndefined, event.touches[0].state);
+  event.ResetPoints();
+
+  event.PressPoint(9, 10);
+  EXPECT_EQ(2U, event.touches_length);
+  EXPECT_EQ(2, event.touches[0].id);
+  EXPECT_EQ(WebTouchPoint::kStatePressed, event.touches[0].state);
+  EXPECT_EQ(WebFloatPoint(9, 10), event.touches[0].PositionInWidget());
+}
+
+}  // namespace content
diff --git a/content/common/render_widget_host_ns_view.mojom b/content/common/render_widget_host_ns_view.mojom
index 08667c11..c0afeb7 100644
--- a/content/common/render_widget_host_ns_view.mojom
+++ b/content/common/render_widget_host_ns_view.mojom
@@ -143,6 +143,7 @@
 
   // Forward events to the renderer or the input router, as appropriate.
   RouteOrProcessMouseEvent(Event event);
+  RouteOrProcessTouchEvent(Event event);
   RouteOrProcessWheelEvent(Event event);
 
   // Special case forwarding of synthetic events to the renderer.
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index fdcef72..1abaafd 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -144,8 +144,7 @@
 viz::VizMainImpl::ExternalDependencies CreateVizMainDependencies(
     service_manager::Connector* connector) {
   viz::VizMainImpl::ExternalDependencies deps;
-  deps.create_display_compositor =
-      base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+  deps.create_display_compositor = features::IsVizDisplayCompositorEnabled();
   if (GetContentClient()->gpu())
     deps.sync_point_manager = GetContentClient()->gpu()->GetSyncPointManager();
   auto* process = ChildProcess::current();
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/AdditionalMenuItemProviderImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/AdditionalMenuItemProviderImpl.java
index 307437c5..ea507f8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/AdditionalMenuItemProviderImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/AdditionalMenuItemProviderImpl.java
@@ -46,9 +46,11 @@
         if (count > 0) {
             RemoteAction primaryAction = classification.getActions().get(0);
 
+            MenuItem item = menu.findItem(android.R.id.textAssist);
             if (primaryAction.shouldShowIcon()) {
-                MenuItem item = menu.findItem(android.R.id.textAssist);
                 item.setIcon(primaryAction.getIcon().loadDrawable(context));
+            } else {
+                item.setIcon(null);
             }
         }
 
diff --git a/content/public/common/use_zoom_for_dsf_policy.cc b/content/public/common/use_zoom_for_dsf_policy.cc
index 2b8d484..5519e07 100644
--- a/content/public/common/use_zoom_for_dsf_policy.cc
+++ b/content/public/common/use_zoom_for_dsf_policy.cc
@@ -8,7 +8,7 @@
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
 
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_ANDROID)
 #include "base/feature_list.h"
 #endif
 
@@ -19,10 +19,15 @@
     "use-zoom-for-dsf enabled by default", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
+#if defined(OS_ANDROID)
+const base::Feature kUseZoomForDsfEnabledByDefault{
+    "use-zoom-for-dsf enabled by default", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
 bool IsUseZoomForDSFEnabledByDefault() {
 #if defined(OS_LINUX) || defined(OS_FUCHSIA)
   return true;
-#elif defined(OS_WIN)
+#elif defined(OS_WIN) || defined(OS_ANDROID)
   return base::FeatureList::IsEnabled(kUseZoomForDsfEnabledByDefault);
 #else
   return false;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 02fdaef6..80214cc 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -467,7 +467,6 @@
     "media_recorder/vpx_encoder.h",
     "menu_item_builder.cc",
     "menu_item_builder.h",
-    "message_delivery_policy.h",
     "mojo/blink_interface_provider_impl.cc",
     "mojo/blink_interface_provider_impl.h",
     "mojo/blink_interface_registry_impl.cc",
diff --git a/content/renderer/gpu/frame_swap_message_queue.cc b/content/renderer/gpu/frame_swap_message_queue.cc
index 1d4e974..f180368 100644
--- a/content/renderer/gpu/frame_swap_message_queue.cc
+++ b/content/renderer/gpu/frame_swap_message_queue.cc
@@ -15,8 +15,6 @@
 #include "base/stl_util.h"
 #include "ipc/ipc_message.h"
 
-using std::vector;
-
 namespace content {
 
 class FrameSwapMessageSubQueue {
@@ -37,7 +35,6 @@
 
 namespace {
 
-// Queue specific to MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE.
 class SendMessageScopeImpl : public FrameSwapMessageQueue::SendMessageScope {
  public:
   SendMessageScopeImpl(base::Lock* lock) : auto_lock_(*lock) {}
@@ -82,38 +79,10 @@
   DISALLOW_COPY_AND_ASSIGN(VisualStateQueue);
 };
 
-// Queue specific to MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP.
-class SwapQueue : public FrameSwapMessageSubQueue {
- public:
-  SwapQueue() {}
-  bool Empty() const override { return queue_.empty(); }
-
-  void QueueMessage(int source_frame_number,
-                    std::unique_ptr<IPC::Message> msg,
-                    bool* is_first) override {
-    if (is_first)
-      *is_first = Empty();
-    queue_.push_back(std::move(msg));
-  }
-
-  void DrainMessages(
-      int source_frame_number,
-      std::vector<std::unique_ptr<IPC::Message>>* messages) override {
-    std::move(queue_.begin(), queue_.end(), std::back_inserter(*messages));
-    queue_.clear();
-  }
-
- private:
-  std::vector<std::unique_ptr<IPC::Message>> queue_;
-
-  DISALLOW_COPY_AND_ASSIGN(SwapQueue);
-};
-
 }  // namespace
 
 FrameSwapMessageQueue::FrameSwapMessageQueue(int32_t routing_id)
     : visual_state_queue_(new VisualStateQueue()),
-      swap_queue_(new SwapQueue()),
       routing_id_(routing_id) {
   DETACH_FROM_THREAD(impl_thread_checker_);
 }
@@ -123,32 +92,16 @@
 
 bool FrameSwapMessageQueue::Empty() const {
   base::AutoLock lock(lock_);
-  return next_drain_messages_.empty() && visual_state_queue_->Empty() &&
-         swap_queue_->Empty();
-}
-
-FrameSwapMessageSubQueue* FrameSwapMessageQueue::GetSubQueue(
-    MessageDeliveryPolicy policy) {
-  switch (policy) {
-    case MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP:
-      return swap_queue_.get();
-      break;
-    case MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE:
-      return visual_state_queue_.get();
-      break;
-  }
-  NOTREACHED();
-  return nullptr;
+  return next_drain_messages_.empty() && visual_state_queue_->Empty();
 }
 
 void FrameSwapMessageQueue::QueueMessageForFrame(
-    MessageDeliveryPolicy policy,
     int source_frame_number,
     std::unique_ptr<IPC::Message> msg,
     bool* is_first) {
   base::AutoLock lock(lock_);
-  GetSubQueue(policy)
-      ->QueueMessage(source_frame_number, std::move(msg), is_first);
+  visual_state_queue_->QueueMessage(source_frame_number, std::move(msg),
+                                    is_first);
 }
 
 void FrameSwapMessageQueue::DidActivate(int source_frame_number) {
@@ -158,8 +111,6 @@
 }
 
 void FrameSwapMessageQueue::DidSwap(int source_frame_number) {
-  base::AutoLock lock(lock_);
-  swap_queue_->DrainMessages(0, &next_drain_messages_);
 }
 
 void FrameSwapMessageQueue::DidNotSwap(
@@ -171,7 +122,6 @@
     case cc::SwapPromise::SWAP_FAILS:
     case cc::SwapPromise::COMMIT_NO_UPDATE:
       DrainMessages(messages);
-      swap_queue_->DrainMessages(source_frame_number, messages);
       visual_state_queue_->DrainMessages(source_frame_number, messages);
       break;
     case cc::SwapPromise::COMMIT_FAILS:
@@ -200,7 +150,7 @@
 // static
 void FrameSwapMessageQueue::TransferMessages(
     std::vector<std::unique_ptr<IPC::Message>>* source,
-    vector<IPC::Message>* dest) {
+    std::vector<IPC::Message>* dest) {
   for (const auto& msg : *source) {
     dest->push_back(*msg.get());
   }
diff --git a/content/renderer/gpu/frame_swap_message_queue.h b/content/renderer/gpu/frame_swap_message_queue.h
index 54ad846..a1e1de85 100644
--- a/content/renderer/gpu/frame_swap_message_queue.h
+++ b/content/renderer/gpu/frame_swap_message_queue.h
@@ -16,7 +16,6 @@
 #include "base/threading/thread_checker.h"
 #include "cc/trees/swap_promise.h"
 #include "content/common/content_export.h"
-#include "content/renderer/message_delivery_policy.h"
 
 namespace IPC {
 class Message;
@@ -42,13 +41,11 @@
 
   // Queues message to be returned on a matching DrainMessages call.
   //
-  // |policy| determines how messages are matched with DrainMessages calls.
   // |source_frame_number| frame number to queue |msg| for.
   // |msg| - message to queue. The method takes ownership of |msg|.
   // |is_first| - output parameter. Set to true if this was the first message
   //              enqueued for the given source_frame_number.
-  void QueueMessageForFrame(MessageDeliveryPolicy policy,
-                            int source_frame_number,
+  void QueueMessageForFrame(int source_frame_number,
                             std::unique_ptr<IPC::Message> msg,
                             bool* is_first);
 
@@ -105,13 +102,10 @@
  private:
   friend class base::RefCountedThreadSafe<FrameSwapMessageQueue>;
 
-  FrameSwapMessageSubQueue* GetSubQueue(MessageDeliveryPolicy policy);
-
   ~FrameSwapMessageQueue();
 
   mutable base::Lock lock_;
   std::unique_ptr<FrameSwapMessageSubQueue> visual_state_queue_;
-  std::unique_ptr<FrameSwapMessageSubQueue> swap_queue_;
   std::vector<std::unique_ptr<IPC::Message>> next_drain_messages_;
   int32_t routing_id_ = 0;
   bool frames_are_discarded_ = false;
diff --git a/content/renderer/gpu/frame_swap_message_queue_unittest.cc b/content/renderer/gpu/frame_swap_message_queue_unittest.cc
index ce18fba..7c3c95f7 100644
--- a/content/renderer/gpu/frame_swap_message_queue_unittest.cc
+++ b/content/renderer/gpu/frame_swap_message_queue_unittest.cc
@@ -21,26 +21,22 @@
 
  protected:
   void QueueNextSwapMessage(std::unique_ptr<IPC::Message> msg) {
-    queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 0,
-                                 std::move(msg), nullptr);
+    queue_->QueueMessageForFrame(0, std::move(msg), nullptr);
   }
 
   void QueueNextSwapMessage(std::unique_ptr<IPC::Message> msg, bool* first) {
-    queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 0,
-                                 std::move(msg), first);
+    queue_->QueueMessageForFrame(0, std::move(msg), first);
   }
 
   void QueueVisualStateMessage(int source_frame_number,
                                std::unique_ptr<IPC::Message> msg) {
-    queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE,
-                                 source_frame_number, std::move(msg), nullptr);
+    queue_->QueueMessageForFrame(source_frame_number, std::move(msg), nullptr);
   }
 
   void QueueVisualStateMessage(int source_frame_number,
                                std::unique_ptr<IPC::Message> msg,
                                bool* first) {
-    queue_->QueueMessageForFrame(MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE,
-                                 source_frame_number, std::move(msg), first);
+    queue_->QueueMessageForFrame(source_frame_number, std::move(msg), first);
   }
 
   void DrainMessages(int source_frame_number,
diff --git a/content/renderer/gpu/queue_message_swap_promise_unittest.cc b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
index e6b911b..551cef8 100644
--- a/content/renderer/gpu/queue_message_swap_promise_unittest.cc
+++ b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
@@ -63,11 +63,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestSyncMessageFilter);
 };
 
-struct QueueMessageData {
-  MessageDeliveryPolicy policy;
-  int source_frame_number;
-};
-
 class QueueMessageSwapPromiseTest : public testing::Test {
  public:
   QueueMessageSwapPromiseTest()
@@ -78,11 +73,10 @@
 
   std::unique_ptr<cc::SwapPromise> QueueMessageImpl(
       IPC::Message* msg,
-      MessageDeliveryPolicy policy,
       int source_frame_number) {
-    return RenderWidget::QueueMessageImpl(
-        msg, policy, frame_swap_message_queue_.get(), sync_message_filter_,
-        source_frame_number);
+    return RenderWidget::QueueMessageImpl(msg, frame_swap_message_queue_.get(),
+                                          sync_message_filter_,
+                                          source_frame_number);
   }
 
   const std::vector<std::unique_ptr<IPC::Message>>& DirectSendMessages() {
@@ -122,13 +116,12 @@
     return ContainsMessage(NextSwapMessages(), message);
   }
 
-  void QueueMessages(QueueMessageData data[], size_t count) {
+  void QueueMessages(int source_frame_numbers[], size_t count) {
     for (size_t i = 0; i < count; ++i) {
       messages_.push_back(
           IPC::Message(0, i + 1, IPC::Message::PRIORITY_NORMAL));
       promises_.push_back(QueueMessageImpl(new IPC::Message(messages_[i]),
-                                           data[i].policy,
-                                           data[i].source_frame_number));
+                                           source_frame_numbers[i]));
     }
   }
 
@@ -161,11 +154,8 @@
 };
 
 TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySchedulesMessageForNextSwap) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   ASSERT_TRUE(promises_[0].get());
   promises_[0]->DidActivate();
@@ -178,12 +168,8 @@
 }
 
 TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyNeedsAtMostOnePromise) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1, 1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   ASSERT_TRUE(promises_[0].get());
   ASSERT_FALSE(promises_[1].get());
@@ -192,11 +178,8 @@
 }
 
 TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnNoUpdate) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE);
   EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0]));
@@ -205,11 +188,8 @@
 }
 
 TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnSwapFails) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   promises_[0]->DidNotSwap(cc::SwapPromise::SWAP_FAILS);
   EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0]));
@@ -217,28 +197,23 @@
   EXPECT_TRUE(frame_swap_message_queue_->Empty());
 }
 
-TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyRetainsMessageOnCommitFails) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP, 1},
-  };
-  QueueMessages(data, arraysize(data));
+TEST_F(QueueMessageSwapPromiseTest,
+       NextActivatePolicyRetainsMessageOnCommitFails) {
+  int source_frame_numbers[] = {1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_FAILS);
   EXPECT_TRUE(DirectSendMessages().empty());
   EXPECT_TRUE(LastSwapMessages().empty());
   EXPECT_FALSE(frame_swap_message_queue_->Empty());
-  frame_swap_message_queue_->DidSwap(2);
+  frame_swap_message_queue_->DidActivate(2);
   EXPECT_TRUE(NextSwapHasMessage(messages_[0]));
 }
 
 TEST_F(QueueMessageSwapPromiseTest,
        VisualStateQueuesMessageWhenCommitRequested) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   ASSERT_TRUE(promises_[0].get());
   EXPECT_TRUE(DirectSendMessages().empty());
@@ -250,12 +225,8 @@
 
 TEST_F(QueueMessageSwapPromiseTest,
        VisualStateQueuesMessageWhenOtherMessageAlreadyQueued) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1, 1};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   EXPECT_TRUE(DirectSendMessages().empty());
   EXPECT_FALSE(frame_swap_message_queue_->Empty());
@@ -265,13 +236,8 @@
 }
 
 TEST_F(QueueMessageSwapPromiseTest, VisualStateSwapPromiseDidActivate) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 2},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1, 1, 2};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   promises_[0]->DidActivate();
   promises_[0]->WillSwap(&dummy_metadata_);
@@ -298,13 +264,8 @@
 
 void QueueMessageSwapPromiseTest::VisualStateSwapPromiseDidNotSwap(
     cc::SwapPromise::DidNotSwapReason reason) {
-  QueueMessageData data[] = {
-    /* { policy, source_frame_number } */
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 1},
-    {MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE, 2},
-  };
-  QueueMessages(data, arraysize(data));
+  int source_frame_numbers[] = {1, 1, 2};
+  QueueMessages(source_frame_numbers, base::size(source_frame_numbers));
 
   // If we fail to swap with COMMIT_FAILS or ACTIVATE_FAILS, then
   // messages are delivered by the RenderFrameHostImpl destructor,
diff --git a/content/renderer/input/frame_input_handler_impl.cc b/content/renderer/input/frame_input_handler_impl.cc
index 206f518..175e4aa 100644
--- a/content/renderer/input/frame_input_handler_impl.cc
+++ b/content/renderer/input/frame_input_handler_impl.cc
@@ -214,7 +214,7 @@
   if (!render_frame_)
     return;
   blink::WebLocalFrame* frame = render_frame_->GetWebFrame();
-  if (frame->HasSelection())
+  if (!frame->HasSelection())
     frame->SelectWordAroundCaret();
   frame->ReplaceSelection(blink::WebString::FromUTF16(word));
   render_frame_->SyncSelectionIfRequired();
diff --git a/content/renderer/message_delivery_policy.h b/content/renderer/message_delivery_policy.h
deleted file mode 100644
index 4ad9b2f1..0000000
--- a/content/renderer/message_delivery_policy.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_MESSAGE_DELIVERY_POLICY_H_
-#define CONTENT_RENDERER_MESSAGE_DELIVERY_POLICY_H_
-
-namespace content {
-
-enum MessageDeliveryPolicy {
-  // If a commit was requested before the message was enqueued the message
-  // will be delivered with the swap corresponding to that commit. Otherwise
-  // the message will be sent immediately using regular IPC.
-  // If the commit is aborted or optimized out the message is sent using regular
-  // IPC.
-  MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE,
-  // The message will be delivered with the next swap.
-  // If the swap is optimized out, the message is sent using regular IPC.
-  // If the swap fails the message remains enqueued.
-  MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP,
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MESSAGE_DELIVERY_POLICY_H_
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4562cbbd59..4a92195ce 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2352,8 +2352,7 @@
 
 void RenderFrameImpl::OnVisualStateRequest(uint64_t id) {
   GetRenderWidget()->QueueMessage(
-      new FrameHostMsg_VisualStateResponse(routing_id_, id),
-      MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
+      new FrameHostMsg_VisualStateResponse(routing_id_, id));
 }
 
 void RenderFrameImpl::OnSetAccessibilityMode(ui::AXMode new_mode) {
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 90d554a..4e7534a0 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -269,8 +269,7 @@
     params.surface_id = compositing_helper_->primary_surface_id();
     params.ignored_for_hittest = web_frame_->IsIgnoredForHitTest();
     render_widget_->QueueMessage(
-        new FrameHostMsg_HittestData(render_widget_->routing_id(), params),
-        MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
+        new FrameHostMsg_HittestData(render_widget_->routing_id(), params));
   }
 }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index b4415bb0..bb618b1 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1566,8 +1566,7 @@
 
 void RenderWidget::DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) {
   if (layout_type == blink::WebMeaningfulLayout::kVisuallyNonEmpty) {
-    QueueMessage(new ViewHostMsg_DidFirstVisuallyNonEmptyPaint(routing_id_),
-                 MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
+    QueueMessage(new ViewHostMsg_DidFirstVisuallyNonEmptyPaint(routing_id_));
   }
 
   for (auto& observer : render_frames_)
@@ -1577,14 +1576,12 @@
 // static
 std::unique_ptr<cc::SwapPromise> RenderWidget::QueueMessageImpl(
     IPC::Message* msg,
-    MessageDeliveryPolicy policy,
     FrameSwapMessageQueue* frame_swap_message_queue,
     scoped_refptr<IPC::SyncMessageFilter> sync_message_filter,
     int source_frame_number) {
   bool first_message_for_frame = false;
-  frame_swap_message_queue->QueueMessageForFrame(policy, source_frame_number,
-                                                 base::WrapUnique(msg),
-                                                 &first_message_for_frame);
+  frame_swap_message_queue->QueueMessageForFrame(
+      source_frame_number, base::WrapUnique(msg), &first_message_for_frame);
   if (first_message_for_frame) {
     std::unique_ptr<cc::SwapPromise> promise(new QueueMessageSwapPromise(
         sync_message_filter, frame_swap_message_queue, source_frame_number));
@@ -1597,8 +1594,7 @@
   input_handler_->set_handling_input_event(handling_input_event);
 }
 
-void RenderWidget::QueueMessage(IPC::Message* msg,
-                                MessageDeliveryPolicy policy) {
+void RenderWidget::QueueMessage(IPC::Message* msg) {
   // RenderThreadImpl::current() is NULL in some tests.
   if (!layer_tree_view_ || !RenderThreadImpl::current()) {
     Send(msg);
@@ -1606,7 +1602,7 @@
   }
 
   std::unique_ptr<cc::SwapPromise> swap_promise =
-      QueueMessageImpl(msg, policy, frame_swap_message_queue_.get(),
+      QueueMessageImpl(msg, frame_swap_message_queue_.get(),
                        RenderThreadImpl::current()->sync_message_filter(),
                        layer_tree_view_->GetSourceFrameNumber());
 
@@ -3044,8 +3040,7 @@
 
 void RenderWidget::OnWaitNextFrameForTests(int routing_id) {
   // Sends an ACK to the browser process during the next compositor frame.
-  QueueMessage(new ViewHostMsg_WaitForNextFrameForTests_ACK(routing_id),
-               MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
+  QueueMessage(new ViewHostMsg_WaitForNextFrameForTests_ACK(routing_id));
 }
 
 const ScreenInfo& RenderWidget::GetWebScreenInfo() const {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 8fa8378..95afdb6a 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -42,7 +42,6 @@
 #include "content/renderer/input/main_thread_event_queue.h"
 #include "content/renderer/input/render_widget_input_handler.h"
 #include "content/renderer/input/render_widget_input_handler_delegate.h"
-#include "content/renderer/message_delivery_policy.h"
 #include "content/renderer/mouse_lock_dispatcher.h"
 #include "content/renderer/render_widget_mouse_lock_dispatcher.h"
 #include "ipc/ipc_listener.h"
@@ -387,17 +386,13 @@
 
   void SetHandlingInputEvent(bool handling_input_event);
 
-  // Deliveres |message| together with compositor state change updates. The
-  // exact behavior depends on |policy|.
+  // Delivers |message| together with compositor state change updates.
   // This mechanism is not a drop-in replacement for IPC: messages sent this way
   // will not be automatically available to BrowserMessageFilter, for example.
-  // FIFO ordering is preserved between messages enqueued with the same
-  // |policy|, the ordering between messages enqueued for different policies is
-  // undefined.
+  // FIFO ordering is preserved between messages enqueued.
   //
   // |msg| message to send, ownership of |msg| is transferred.
-  // |policy| see the comment on MessageDeliveryPolicy.
-  void QueueMessage(IPC::Message* msg, MessageDeliveryPolicy policy);
+  void QueueMessage(IPC::Message* msg);
 
   // Handle start and finish of IME event guard.
   void OnImeEventGuardStart(ImeEventGuard* guard);
@@ -702,7 +697,6 @@
   // testing.
   static std::unique_ptr<cc::SwapPromise> QueueMessageImpl(
       IPC::Message* msg,
-      MessageDeliveryPolicy policy,
       FrameSwapMessageQueue* frame_swap_message_queue,
       scoped_refptr<IPC::SyncMessageFilter> sync_message_filter,
       int source_frame_number);
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index f0d8b42..e0ed46f 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -37,6 +37,7 @@
 #include "content/shell/browser/shell_login_dialog.h"
 #include "content/shell/browser/shell_net_log.h"
 #include "content/shell/browser/shell_quota_permission_context.h"
+#include "content/shell/browser/shell_url_request_context_getter.h"
 #include "content/shell/browser/shell_web_contents_view_delegate_creator.h"
 #include "content/shell/common/shell_messages.h"
 #include "content/shell/common/shell_switches.h"
@@ -292,6 +293,10 @@
   }
 }
 
+std::string ShellContentBrowserClient::GetAcceptLangs(BrowserContext* context) {
+  return ShellURLRequestContextGetter::GetAcceptLanguages();
+}
+
 void ShellContentBrowserClient::ResourceDispatcherHostCreated() {
   resource_dispatcher_host_delegate_.reset(
       new ResourceDispatcherHostDelegate());
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index e61a2b1..733af42 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -49,6 +49,7 @@
       base::StringPiece name) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
+  std::string GetAcceptLangs(BrowserContext* context) override;
   void ResourceDispatcherHostCreated() override;
   std::string GetDefaultDownloadName() override;
   WebContentsViewDelegate* GetWebContentsViewDelegate(
diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc
index 9b7a435..b4f59f36 100644
--- a/content/shell/browser/shell_url_request_context_getter.cc
+++ b/content/shell/browser/shell_url_request_context_getter.cc
@@ -83,6 +83,10 @@
   url_request_context_ = nullptr;  // deletes it
 }
 
+std::string ShellURLRequestContextGetter::GetAcceptLanguages() {
+  return "en-us,en";
+}
+
 std::unique_ptr<net::NetworkDelegate>
 ShellURLRequestContextGetter::CreateNetworkDelegate() {
   return std::make_unique<ShellNetworkDelegate>();
@@ -126,7 +130,7 @@
     cookie_store->SetChannelIDServiceID(channel_id_service->GetUniqueID());
     builder.SetCookieAndChannelIdStores(std::move(cookie_store),
                                         std::move(channel_id_service));
-    builder.set_accept_language("en-us,en");
+    builder.set_accept_language(GetAcceptLanguages());
     builder.set_user_agent(GetShellUserAgent());
 
     builder.SetCertVerifier(GetCertVerifier());
diff --git a/content/shell/browser/shell_url_request_context_getter.h b/content/shell/browser/shell_url_request_context_getter.h
index fff1231..5ce10487 100644
--- a/content/shell/browser/shell_url_request_context_getter.h
+++ b/content/shell/browser/shell_url_request_context_getter.h
@@ -6,6 +6,7 @@
 #define CONTENT_SHELL_BROWSER_SHELL_URL_REQUEST_CONTEXT_GETTER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
@@ -47,6 +48,8 @@
 
   void NotifyContextShuttingDown();
 
+  static std::string GetAcceptLanguages();
+
  protected:
   ~ShellURLRequestContextGetter() override;
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 389cd6c..2084ab7 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -804,6 +804,7 @@
     "../browser/presentation/presentation_browsertest.cc",
     "../browser/renderer_host/input/composited_scrolling_browsertest.cc",
     "../browser/renderer_host/input/compositor_event_ack_browsertest.cc",
+    "../browser/renderer_host/input/autoscroll_browsertest.cc",
     "../browser/renderer_host/input/fling_browsertest.cc",
     "../browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc",
     "../browser/renderer_host/input/main_thread_event_queue_browsertest.cc",
@@ -1598,6 +1599,7 @@
     "../common/indexed_db/indexed_db_key_unittest.cc",
     "../common/input/event_with_latency_info_unittest.cc",
     "../common/input/gesture_event_stream_validator_unittest.cc",
+    "../common/input/synthetic_web_input_event_builders_unittest.cc",
     "../common/input/touch_event_stream_validator_unittest.cc",
     "../common/inter_process_time_ticks_converter_unittest.cc",
     "../common/mac/attributed_string_coder_unittest.mm",
diff --git a/content/test/data/frame_tree/page_with_janky_frame.html b/content/test/data/frame_tree/page_with_janky_frame.html
deleted file mode 100644
index 25c8737b..0000000
--- a/content/test/data/frame_tree/page_with_janky_frame.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html>
-<style>
-.container {
-  touch-action: pan-y;
-}
-iframe {
-  position:absolute;
-  top: 50px;
-  left: 50px;
-  width: 100px;
-  height: 100px;
-}
-</style>
-<html>
-<body>
-<div class="container">
-  <iframe src="/cross-site/baz.com/page_with_touch_start_janking_main_thread.html"></iframe>
-</div>
-This page contains a positioned cross-origin iframe.
-</body>
-</html>
diff --git a/content/test/data/page_with_touch_start_janking_main_thread.html b/content/test/data/page_with_touch_start_janking_main_thread.html
deleted file mode 100644
index 7480d1fd..0000000
--- a/content/test/data/page_with_touch_start_janking_main_thread.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<html>
-<style>
-#janktest{
-  width: 1000px;
-  height: 10000px;
-}
-</style>
-<body onload='setup()'>
- <div id="janktest"></div>
-</body>
-<script>
-function setup() {
-  janktest.ontouchstart = function() {
-    var end = performance.now() + 1200;
-    while (performance.now() < end) ;
-  };
-}
-</script>
-</html>
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index ef010573..bb402a22 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -58,14 +58,14 @@
     # TODO(kbr): flakily timing out on this configuration.
     self.Flaky('*', ['linux', 'intel', 'debug'], bug=648369)
 
-    self.Flaky('Pixel_Video_MP4', ['android', 'nvidia'], bug=716564)
-    self.Flaky('Pixel_Video_MP4', ['linux', 'nvidia'], bug=819635)
+    # self.Flaky('Pixel_Video_MP4', ['android', 'nvidia'], bug=716564)
+    # self.Flaky('Pixel_Video_MP4', ['linux', 'nvidia'], bug=819635)
 
     # TODO(junov): rebaselining
-    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositing',
-              ['mac', 'linux', 'win', 'android', 'chromeos'], bug=788439)
-    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
-              ['mac', 'linux', 'win', 'android', 'chromeos'], bug=788439)
+    # self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositing',
+    #          ['mac', 'linux', 'win', 'android', 'chromeos'], bug=788439)
+    # self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
+    #          ['mac', 'linux', 'win', 'android', 'chromeos'], bug=788439)
 
     # Flaky for unknown reasons only on macOS. Not planning to investigate
     # further.
@@ -82,14 +82,14 @@
         ['android', ('qualcomm', 'Adreno (TM) 330')], bug=773293)
 
     # Failing on Mac Intel HighSierra
-    self.Fail('Pixel_Video_MP4',
-        ['highsierra', ('intel', 0xa2e)], bug=774809)
-    self.Fail('Pixel_Video_VP9',
-        ['highsierra', ('intel', 0xa2e)], bug=774809)
+    # self.Fail('Pixel_Video_MP4',
+    #    ['highsierra', ('intel', 0xa2e)], bug=774809)
+    # self.Fail('Pixel_Video_VP9',
+    #     ['highsierra', ('intel', 0xa2e)], bug=774809)
     self.Fail('Pixel_WebGLGreenTriangle_NonChromiumImage_NoAA_NoAlpha',
         ['highsierra', ('intel', 0xa2e)], bug=774809)
-    self.Flaky('Pixel_OffscreenCanvasTransferBeforeStyleResize',
-        ['highsierra', ('intel', 0xa2e)], bug=857578)
+    # self.Flaky('Pixel_OffscreenCanvasTransferBeforeStyleResize',
+    #    ['highsierra', ('intel', 0xa2e)], bug=857578)
 
     # Failing on NVIDIA Shield TV; not sure why yet.
     self.Fail('Pixel_WebGL_PremultipliedAlpha_False',
@@ -103,8 +103,8 @@
               ['linux', 'mac', 'win'], bug=744658)
 
     # TODO(fserb): temporarily suppress this test.
-    self.Flaky('Pixel_OffscreenCanvas2DResizeOnWorker',
-        ['linux', 'mac'], bug=840394)
+    # self.Flaky('Pixel_OffscreenCanvas2DResizeOnWorker',
+    #    ['linux', 'mac'], bug=840394)
     self.Fail('Pixel_WorkerRAF_OOPD', ['android', 'nvidia'], bug=833902)
     self.Fail('Pixel_WorkerRAF_OOPD', ['mac'], bug=851213)
 
@@ -113,8 +113,32 @@
     self.Fail('Pixel_WebGLSadCanvas', ['android'], bug=575305)
 
     # Flaky on Android: crbug.com/860548
-    self.Flaky('Pixel_Video_VP9', ['android'], bug=860548)
+    # self.Flaky('Pixel_Video_VP9', ['android'], bug=860548)
 
     self.Fail('Pixel_CanvasLowLatencyWebGL', ['android', 'nvidia'], bug=868596)
     self.Fail('Pixel_OffscreenCanvasWebGLPaintAfterResize',
               ['android', 'nvidia'], bug=868596)
+
+    # Rebaseline tests for enabling use-zoom-for-dsf on Android
+    self.Fail('Pixel_2DCanvasWebGL', bug=737777)
+    self.Fail('Pixel_CSS3DBlueBox', bug=737777)
+    self.Fail('Pixel_Canvas2DRedBox', bug=737777)
+    self.Fail('Pixel_CanvasDisplayLinearRGBAccelerated2D', bug=737777)
+    self.Fail('Pixel_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing', bug=737777)
+    self.Fail('Pixel_OffscreenCanvas2DResizeOnWorker', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasTransferAfterStyleResize', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasTransferBeforeStyleResize', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasTransferToImageBitmap', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasTransferToImageBitmapWorker', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositing', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasWebGLDefault', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasWebGLDefaultWorker', bug=737777)
+    self.Fail('Pixel_OffscreenCanvasWebglResizeOnWorker', bug=737777)
+    self.Fail('Pixel_Video_MP4', bug=737777)
+    self.Fail('Pixel_Video_VP9', bug=737777)
+    self.Fail('Pixel_WebGLGreenTriangle_AA_Alpha', bug=737777)
+    self.Fail('Pixel_WebGLGreenTriangle_AA_NoAlpha', bug=737777)
+    self.Fail('Pixel_WebGLGreenTriangle_NoAA_Alpha', bug=737777)
+    self.Fail('Pixel_WebGLGreenTriangle_NoAA_NoAlpha', bug=737777)
+    self.Fail('Pixel_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear', bug=737777)
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 4a30ed0..0f523fa7 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -96,7 +96,7 @@
       'pixel_canvas2d.html',
       base_name + '_Canvas2DRedBox',
       test_rect=[0, 0, 300, 300],
-      revision=7),
+      revision=8),
 
     PixelTestPage(
       'pixel_canvas2d_untagged.html',
@@ -108,37 +108,37 @@
       'pixel_css3d.html',
       base_name + '_CSS3DBlueBox',
       test_rect=[0, 0, 300, 300],
-      revision=18),
+      revision=19),
 
     PixelTestPage(
       'pixel_webgl_aa_alpha.html',
       base_name + '_WebGLGreenTriangle_AA_Alpha',
       test_rect=[0, 0, 300, 300],
-      revision=4),
+      revision=5),
 
     PixelTestPage(
       'pixel_webgl_noaa_alpha.html',
       base_name + '_WebGLGreenTriangle_NoAA_Alpha',
       test_rect=[0, 0, 300, 300],
-      revision=1),
+      revision=2),
 
     PixelTestPage(
       'pixel_webgl_aa_noalpha.html',
       base_name + '_WebGLGreenTriangle_AA_NoAlpha',
       test_rect=[0, 0, 300, 300],
-      revision=5),
+      revision=6),
 
     PixelTestPage(
       'pixel_webgl_noaa_noalpha.html',
       base_name + '_WebGLGreenTriangle_NoAA_NoAlpha',
       test_rect=[0, 0, 300, 300],
-      revision=1),
+      revision=2),
 
     PixelTestPage(
       'pixel_webgl_noalpha_implicit_clear.html',
       base_name + '_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear',
       test_rect=[0, 0, 300, 300],
-      revision=1),
+      revision=2),
 
     PixelTestPage(
       'pixel_webgl_sad_canvas.html',
@@ -180,7 +180,7 @@
       'pixel_canvas2d_webgl.html',
       base_name + '_2DCanvasWebGL',
       test_rect=[0, 0, 300, 300],
-      revision=7),
+      revision=8),
 
     PixelTestPage(
       'pixel_background.html',
@@ -192,13 +192,13 @@
       'pixel_video_mp4.html',
       base_name + '_Video_MP4',
       test_rect=[0, 0, 300, 300],
-      revision=8),
+      revision=9),
 
     PixelTestPage(
       'pixel_video_vp9.html',
       base_name + '_Video_VP9',
       test_rect=[0, 0, 300, 300],
-      revision=8),
+      revision=9),
 
     PixelTestPage(
       'pixel_webgl_premultiplied_alpha_false.html',
@@ -404,14 +404,14 @@
       'pixel_offscreenCanvas_transfer_after_style_resize.html',
       base_name + '_OffscreenCanvasTransferAfterStyleResize',
       test_rect=[0, 0, 350, 350],
-      revision=6,
+      revision=7,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_transfer_before_style_resize.html',
       base_name + '_OffscreenCanvasTransferBeforeStyleResize',
       test_rect=[0, 0, 350, 350],
-      revision=6,
+      revision=7,
       browser_args=browser_args),
 
     PixelTestPage(
@@ -450,28 +450,28 @@
       'pixel_offscreenCanvas_transferToImageBitmap_main.html',
       base_name + '_OffscreenCanvasTransferToImageBitmap',
       test_rect=[0, 0, 300, 300],
-      revision=2,
+      revision=3,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_transferToImageBitmap_worker.html',
       base_name + '_OffscreenCanvasTransferToImageBitmapWorker',
       test_rect=[0, 0, 300, 300],
-      revision=2,
+      revision=3,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_main.html',
       base_name + '_OffscreenCanvasWebGLDefault',
       test_rect=[0, 0, 360, 200],
-      revision=8,
+      revision=9,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_commit_worker.html',
       base_name + '_OffscreenCanvasWebGLDefaultWorker',
       test_rect=[0, 0, 360, 200],
-      revision=8,
+      revision=9,
       browser_args=browser_args),
 
     PixelTestPage(
@@ -520,35 +520,35 @@
       'pixel_offscreenCanvas_2d_commit_main.html',
       base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositing',
       test_rect=[0, 0, 360, 200],
-      revision=12,
+      revision=13,
       browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_commit_worker.html',
       base_name + '_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
       test_rect=[0, 0, 360, 200],
-      revision=12,
+      revision=13,
       browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
 
     PixelTestPage(
       'pixel_offscreenCanvas_2d_resize_on_worker.html',
       base_name + '_OffscreenCanvas2DResizeOnWorker',
       test_rect=[0, 0, 200, 200],
-      revision=4,
+      revision=5,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_offscreenCanvas_webgl_resize_on_worker.html',
       base_name + '_OffscreenCanvasWebglResizeOnWorker',
       test_rect=[0, 0, 200, 200],
-      revision=6,
+      revision=7,
       browser_args=browser_args),
 
     PixelTestPage(
       'pixel_canvas_display_linear-rgb.html',
       base_name + '_CanvasDisplayLinearRGBAccelerated2D',
       test_rect=[0, 0, 140, 140],
-      revision=1,
+      revision=2,
       browser_args=browser_args),
 
     PixelTestPage(
@@ -562,7 +562,7 @@
       'pixel_canvas_display_linear-rgb.html',
       base_name + '_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing',
       test_rect=[0, 0, 140, 140],
-      revision=1,
+      revision=2,
       browser_args=browser_args + ['--disable-accelerated-2d-canvas']),
 
     PixelTestPage(
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
index 216520a..2d67c65 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
@@ -254,7 +254,10 @@
       '--disable-gpu-watchdog',
       # TODO(http://crbug.com/832952): Remove this when WebXR spec is more
       # stable and setCompatibleXRDevice is part of the conformance test.
-      '--disable-blink-features=WebXR'
+      '--disable-blink-features=WebXR',
+      # TODO(crbug.com/830901): see whether disabling this feature
+      # makes the WebGL video upload tests reliable again.
+      '--disable-features=UseSurfaceLayerForVideo',
     ]
     # Note that the overriding of the default --js-flags probably
     # won't interact well with RestartBrowserIfNecessaryWithArgs, but
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 0469214e..1c49a97 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -574,6 +574,7 @@
     decoder_.reset();
   }
   command_buffer_.reset();
+  transfer_buffer_manager_.reset();
   surface_ = nullptr;
 
   context_ = nullptr;
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 3e357ff..49c648d 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -1646,7 +1646,7 @@
       mixins: "lkgr-ci"
     }
     builders {
-      name: "Win x64"
+      name: "win-rel"
       dimensions: "os:Windows-10"
       dimensions: "cores:32"
       mixins: "chromium-ci"
@@ -1673,7 +1673,7 @@
       mixins: "android-ci"
     }
     builders {
-      name: "Android"
+      name: "android-rel"
       dimensions: "os:Ubuntu-14.04"
       dimensions: "cores:32"
       mixins: "chromium-ci"
@@ -1861,6 +1861,12 @@
       mixins: "fyi-ci"
     }
     builders {
+      name: "linux-tcmalloc-rel"
+      dimensions: "os:Ubuntu-14.04"
+      dimensions: "cores:"
+      mixins: "fyi-ci"
+    }
+    builders {
       name: "Mac deterministic (dbg)"
       dimensions: "os:Mac-10.13"
       dimensions: "cores:"
@@ -1913,7 +1919,7 @@
       mixins: "android-fyi-ci"
     }
     builders {
-      name: "Linux x64"
+      name: "linux-rel"
       dimensions: "os:Ubuntu-14.04"
       dimensions: "cores:32"
       mixins: "chromium-ci"
@@ -2044,7 +2050,7 @@
       mixins: "android-ci"
     }
     builders {
-      name: "Mac"
+      name: "mac-rel"
       dimensions: "os:Mac-10.13"
       mixins: "chromium-ci"
     }
@@ -2137,7 +2143,7 @@
       mixins: "swarm-ci"
     }
     builders {
-      name: "Win"
+      name: "win32-rel"
       dimensions: "os:Windows-10"
       dimensions: "cores:32"
       mixins: "chromium-ci"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 6603184..17d5dbe 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -314,27 +314,27 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbucket/luci.chromium.ci/Win"
+    name: "buildbucket/luci.chromium.ci/win32-rel"
     category: "chromium|win"
     short_name: "32"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Win x64"
+    name: "buildbucket/luci.chromium.ci/win-rel"
     category: "chromium|win"
     short_name: "64"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac"
+    name: "buildbucket/luci.chromium.ci/mac-rel"
     category: "chromium"
     short_name: "mac"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux x64"
+    name: "buildbucket/luci.chromium.ci/linux-rel"
     category: "chromium"
     short_name: "lin"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Android"
+    name: "buildbucket/luci.chromium.ci/android-rel"
     category: "chromium"
     short_name: "and"
   }
@@ -1438,24 +1438,24 @@
   manifest_name: "REVISION"
   include_experimental_builds: true
   builders {
-    name: "buildbucket/luci.chromium.ci/Android"
+    name: "buildbucket/luci.chromium.ci/android-rel"
     short_name: "an"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux x64"
+    name: "buildbucket/luci.chromium.ci/linux-rel"
     short_name: "lnx"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac"
+    name: "buildbucket/luci.chromium.ci/mac-rel"
     short_name: "mac"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Win"
+    name: "buildbucket/luci.chromium.ci/win32-rel"
     category: "win"
     short_name: "32"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Win x64"
+    name: "buildbucket/luci.chromium.ci/win-rel"
     category: "win"
     short_name: "64"
   }
@@ -2295,6 +2295,10 @@
     category: "linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/linux-tcmalloc-rel"
+    category: "linux"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Mojo Android"
     category: "mojo"
     short_name: "and"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 644e00c..f21baaa 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -85,7 +85,7 @@
   triggers: "Android arm64 Builder (dbg)"
   triggers: "Android x64 Builder (dbg)"
   triggers: "Android x86 Builder (dbg)"
-  triggers: "Android"
+  triggers: "android-rel"
   triggers: "Cast Android (dbg)"
   triggers: "Cast Audio Linux"
   triggers: "Cast Linux"
@@ -161,7 +161,7 @@
   triggers: "Linux remote_run Builder"
   triggers: "Linux x64 Goma Canary (clobber)"
   triggers: "Linux x64 Goma Canary LocalOutputCache"
-  triggers: "Linux x64"
+  triggers: "linux-rel"
   triggers: "MSAN Release (chained origins)"
   triggers: "MSAN Release (no origins)"
   triggers: "Mac ASAN Debug"
@@ -179,7 +179,7 @@
   triggers: "Mac Swarm"
   triggers: "Mac deterministic (dbg)"
   triggers: "Mac deterministic"
-  triggers: "Mac"
+  triggers: "mac-rel"
   triggers: "Mojo Android"
   triggers: "Mojo ChromiumOS"
   triggers: "Mojo Linux"
@@ -191,6 +191,7 @@
   triggers: "UBSan Release"
   triggers: "UBSan vptr Release"
   triggers: "VR Linux"
+  triggers: "linux-tcmalloc-rel"
   triggers: "Win 10 Fast Ring"
   triggers: "Win ASan Release Media"
   triggers: "Win ASan Release"
@@ -202,8 +203,8 @@
   triggers: "Win cl.exe Goma Canary LocalOutputCache"
   triggers: "Win x64 Builder (dbg)"
   triggers: "Win x64 Builder"
-  triggers: "Win x64"
-  triggers: "Win"
+  triggers: "win-rel"
+  triggers: "win32-rel"
   triggers: "Win7 Builder (dbg) Goma Canary"
   triggers: "Win7 Builder Goma Canary"
   triggers: "WinMSVC64 Goma Canary"
@@ -2267,12 +2268,12 @@
 }
 
 job {
-  id: "Android"
+  id: "android-rel"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Android"
+    builder: "android-rel"
   }
 }
 
@@ -2824,12 +2825,12 @@
 }
 
 job {
-  id: "Linux x64"
+  id: "linux-rel"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Linux x64"
+    builder: "linux-rel"
   }
 }
 
@@ -2880,12 +2881,12 @@
 }
 
 job {
-  id: "Mac"
+  id: "mac-rel"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Mac"
+    builder: "mac-rel"
   }
 }
 
@@ -3215,6 +3216,16 @@
 }
 
 job {
+  id: "linux-tcmalloc-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-tcmalloc-rel"
+  }
+}
+
+job {
   id: "WebKit Linux Trusty ASAN"
   acl_sets: "default"
   buildbucket: {
@@ -3287,12 +3298,12 @@
 }
 
 job {
-  id: "Win"
+  id: "win32-rel"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Win"
+    builder: "win32-rel"
   }
 }
 
@@ -3373,12 +3384,12 @@
 }
 
 job {
-  id: "Win x64"
+  id: "win-rel"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Win x64"
+    builder: "win-rel"
   }
 }
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 08a1f8b..5372955 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -297,9 +297,6 @@
       <message name="IDS_IOS_AUTOFILL_STATE" desc="Title of the field of a profile address representing the state/county/district/oblast of the address. [Length: 15em] [iOS only]">
         State / County
       </message>
-      <message name="IDS_IOS_AUTOFILL_USE_WALLET_DATA" desc="The text for the checkbox that controls the Autofill/Google Pay integration feature. 'Google Pay' should not be translated as it is the product name. [Length: 50em] [iOS only]">
-        Show addresses and credit cards from Google Pay
-      </message>
       <message name="IDS_IOS_AUTOFILL_WALLET_SERVER_NAME" desc="The name of the data source for non-local card and address info. Title case. 'Google Pay' should not be translated as it is the product name. [Length: 15em] [iOS only]">
         Google Pay
       </message>
diff --git a/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h b/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h
index fb08128..e6dd289c 100644
--- a/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h
+++ b/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h
@@ -43,6 +43,9 @@
 // The maximum number of lines of the secondary text. Default is 1.
 @property(nonatomic, assign) NSInteger numberOfDetailTextLines;
 
+// Command to trigger when the cell is tapped. The default value is 0.
+@property(nonatomic, assign) NSInteger commandID;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COLLECTION_VIEW_CELLS_COLLECTION_VIEW_TEXT_ITEM_H_
diff --git a/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.mm b/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.mm
index 8164744..d3db43f 100644
--- a/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.mm
+++ b/ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.mm
@@ -24,6 +24,7 @@
 @synthesize detailTextFont = _detailTextFont;
 @synthesize detailTextColor = _detailTextColor;
 @synthesize numberOfDetailTextLines = _numberOfDetailTextLines;
+@synthesize commandID = _commandID;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/print/print_controller_egtest.mm b/ios/chrome/browser/ui/print/print_controller_egtest.mm
index f71b9154..23707d4 100644
--- a/ios/chrome/browser/ui/print/print_controller_egtest.mm
+++ b/ios/chrome/browser/ui/print/print_controller_egtest.mm
@@ -70,6 +70,13 @@
         @"UIRefresh flag is enabled.");
   }
 
+#if !TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS11OrLater()) {
+    // TODO(crbug.com/869477): Re-enable this test on device.
+    EARL_GREY_TEST_DISABLED(@"Fails on iOS 10.0 devices.");
+  }
+#endif
+
   GURL url = web::test::HttpServer::MakeUrl(kHTMLURL);
   std::map<GURL, std::string> responses;
   std::string response = "Test";
@@ -90,6 +97,12 @@
         @"Dispatcher-based printing does not work on iOS11 when the "
         @"UIRefresh flag is enabled.");
   }
+#if !TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS11OrLater()) {
+    // TODO(crbug.com/869477): Re-enable this test on device.
+    EARL_GREY_TEST_DISABLED(@"Fails on iOS 10.0 devices.");
+  }
+#endif
 
   web::test::SetUpFileBasedHttpServer();
   GURL url = web::test::HttpServer::MakeUrl(kPDFURL);
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
index 1a94409..e9ebf44 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
@@ -584,10 +584,12 @@
 // switched off and the correct button indicating that the torch is off is shown
 // when the scanner is opened again.
 - (void)testTorchButtonIsResetWhenQRScannerIsReopened {
-  // TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad.
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
   if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10.");
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
   }
+#endif
 
   id cameraControllerMock =
       [self getCameraControllerMockWithAuthorizationStatus:
@@ -622,6 +624,12 @@
 // Tests that the torch button is disabled when the camera reports that torch
 // became unavailable.
 - (void)testTorchButtonIsDisabledWhenTorchBecomesUnavailable {
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
+  }
+#endif
   id cameraControllerMock =
       [self getCameraControllerMockWithAuthorizationStatus:
                 AVAuthorizationStatusAuthorized];
@@ -669,6 +677,12 @@
 // Tests that a UIAlertController is presented by the QRScannerViewController if
 // the camera state changes after the QRScannerViewController is presented.
 - (void)testDialogIsDisplayedIfCameraStateChanges {
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
+  }
+#endif
   id cameraControllerMock =
       [self getCameraControllerMockWithAuthorizationStatus:
                 AVAuthorizationStatusAuthorized];
@@ -732,10 +746,12 @@
 
 // Tests that an error dialog is dismissed if the camera becomes available.
 - (void)testDialogDismissedIfCameraBecomesAvailable {
-  // TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad.
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
   if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10.");
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
   }
+#endif
 
   id cameraControllerMock =
       [self getCameraControllerMockWithAuthorizationStatus:
@@ -807,6 +823,12 @@
 
 // Test that the correct page is loaded if the scanner result is a URL.
 - (void)testReceivingQRScannerURLResult {
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
+  if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
+  }
+#endif
   [self doTestReceivingResult:_testURL.GetContent()
                      response:kTestURLResponse
                          edit:nil];
@@ -815,10 +837,12 @@
 // Test that the correct page is loaded if the scanner result is a URL which is
 // then manually edited.
 - (void)testReceivingQRScannerURLResultAndEditingTheURL {
-  // TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad.
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
   if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10.");
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
   }
+#endif
   // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
   // grey_typeText works on iOS 11.
   if (base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
@@ -839,10 +863,12 @@
 // Test that the correct page is loaded if the scanner result is a search query
 // which is then manually edited.
 - (void)testReceivingQRScannerSearchQueryResultAndEditingTheQuery {
-  // TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad.
+// TODO(crbug.com/869176): Re-enable this test on iOS 10 iPad device.
+#if !TARGET_IPHONE_SIMULATOR
   if (!base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10.");
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 10 iPad device.");
   }
+#endif
   // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
   // grey_typeText works on iOS 11.
   if (base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
index 7acb6cb..3f5a5c88 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
@@ -391,7 +391,7 @@
     NSInteger sectionIndex =
         [model sectionForSectionIdentifier:SectionIdentifierRead];
     [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
-             withRowAnimation:UITableViewRowAnimationAutomatic];
+             withRowAnimation:UITableViewRowAnimationMiddle];
     [model removeSectionWithIdentifier:SectionIdentifierRead];
   };
   void (^completion)(BOOL) = ^(BOOL) {
@@ -692,7 +692,7 @@
     [model setHeader:[self headerForSection:sectionID]
         forSectionWithIdentifier:sectionID];
     [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
-                  withRowAnimation:UITableViewRowAnimationAutomatic];
+                  withRowAnimation:UITableViewRowAnimationMiddle];
   };
   [self performBatchTableViewUpdates:updates completion:nil];
 
@@ -810,7 +810,7 @@
         // view.
         NSInteger sectionIndex = [model sectionForSectionIdentifier:section];
         [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
-                 withRowAnimation:UITableViewRowAnimationAutomatic];
+                 withRowAnimation:UITableViewRowAnimationFade];
         [model removeSectionWithIdentifier:section];
       }
     }
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h b/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
index bed38d23c..723d74f 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_consumer.h
@@ -30,14 +30,14 @@
 // Updates contents of a cell for a given item.
 - (void)updateCellsForItem:(ListItem*)item;
 
+// Indicate to user that data has been cleared.
+- (void)showBrowsingHistoryRemovedDialog;
+
 // Only necessary for ClearBrowsingDataCollectionView to implement
-// IsNewClearBrowsingDataUIEnabled experiment and to show a dialog about other
-// forms of browsing history.
+// IsNewClearBrowsingDataUIEnabled experiment.
 @optional
 // Updates item of |itemType| with |detailText|.
 - (void)updateCounter:(NSInteger)itemType detailText:(NSString*)detailText;
-// Indicate to user that data has been cleared.
-- (void)showBrowsingHistoryRemovedDialog;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CLEAR_BROWSING_DATA_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
index 4c58155..104f8dd4 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_table_view_controller.mm
@@ -6,7 +6,9 @@
 
 #include "base/mac/foundation_util.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remove_mask.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
+#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h"
 #include "ios/chrome/browser/ui/settings/clear_browsing_data_local_commands.h"
@@ -16,6 +18,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_button_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_link_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -50,10 +53,14 @@
 // dialog.
 @property(nonatomic, strong) UIButton* clearBrowsingDataButton;
 
+// Modal alert for Browsing history removed dialog.
+@property(nonatomic, strong) AlertCoordinator* alertCoordinator;
+
 @end
 
 @implementation ClearBrowsingDataTableViewController
 @synthesize actionSheetCoordinator = _actionSheetCoordinator;
+@synthesize alertCoordinator = _alertCoordinator;
 @synthesize browserState = _browserState;
 @synthesize clearBrowsingDataButton = _clearBrowsingDataButton;
 @synthesize dataManager = _dataManager;
@@ -108,6 +115,7 @@
 }
 
 - (void)dismiss {
+  [self.alertCoordinator stop];
   [self.localDispatcher dismissClearBrowsingDataWithCompletion:nil];
 }
 
@@ -213,6 +221,36 @@
                                      completionBlock:completionBlock];
 }
 
+- (void)showBrowsingHistoryRemovedDialog {
+  NSString* title =
+      l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_TITLE);
+  NSString* message = l10n_util::GetNSString(
+      IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_DESCRIPTION);
+
+  self.alertCoordinator =
+      [[AlertCoordinator alloc] initWithBaseViewController:self
+                                                     title:title
+                                                   message:message];
+
+  __weak ClearBrowsingDataTableViewController* weakSelf = self;
+  [self.alertCoordinator
+      addItemWithTitle:
+          l10n_util::GetNSString(
+              IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OPEN_HISTORY_BUTTON)
+                action:^{
+                  [weakSelf.localDispatcher openURL:GURL(kGoogleMyAccountURL)];
+                }
+                 style:UIAlertActionStyleDefault];
+
+  [self.alertCoordinator
+      addItemWithTitle:l10n_util::GetNSString(
+                           IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OK_BUTTON)
+                action:nil
+                 style:UIAlertActionStyleCancel];
+
+  [self.alertCoordinator start];
+}
+
 #pragma mark - Private Helpers
 
 - (void)showClearBrowsingDataAlertController:(id)sender {
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_command_handler.h b/ios/chrome/browser/ui/settings/google_services_settings_command_handler.h
index c3c623b..b0f3f1f 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_command_handler.h
+++ b/ios/chrome/browser/ui/settings/google_services_settings_command_handler.h
@@ -35,6 +35,12 @@
   GoogleServicesSettingsCommandIDToggleImproveChromeService,
   // Enable/disabble better search and browsing service.
   GoogleServicesSettingsCommandIDToggleBetterSearchAndBrowsingService,
+  // Opens the Google activity controls dialog.
+  GoogleServicesSettingsCommandIDOpenGoogleActivityPage,
+  // Opens the encryption dialog.
+  GoogleServicesSettingsCommandIDOpenEncryptionDialog,
+  // Opens manage synced data page.
+  GoogleServicesSettingsCommandIDOpenManageSyncedDataPage,
 };
 
 // Protocol to handle Google services settings commands.
@@ -74,6 +80,15 @@
 // GoogleServicesSettingsCommandIDToggleBetterSearchAndBrowsingService is
 // triggered.
 - (void)toggleBetterSearchAndBrowsingServiceWithValue:(BOOL)on;
+// Called when GoogleServicesSettingsCommandIDOpenGoogleActivityPage is
+// triggered.
+- (void)openGoogleActivityPage;
+// Called when GoogleServicesSettingsCommandIDOpenEncryptionDialog is
+// triggered.
+- (void)openEncryptionDialog;
+// Called when GoogleServicesSettingsCommandIDOpenManageSyncedDataPage is
+// triggered.
+- (void)openManageSyncedDataPage;
 
 @end
 
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
index e59bb01..e622d1b6 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
@@ -248,6 +248,7 @@
       IDS_IOS_GOOGLE_SERVICES_SETTINGS_GOOGLE_ACTIVITY_CONTROL_DETAIL);
   item.numberOfDetailTextLines = 0;
   item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
+  item.commandID = GoogleServicesSettingsCommandIDOpenGoogleActivityPage;
   return item;
 }
 
@@ -261,6 +262,7 @@
     item.textColor = [[MDCPalette greyPalette] tint500];
   item.numberOfDetailTextLines = 0;
   item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
+  item.commandID = GoogleServicesSettingsCommandIDOpenEncryptionDialog;
   return item;
 }
 
@@ -273,6 +275,7 @@
   item.numberOfTextLines = 0;
   if (!self.isAuthenticated)
     item.textColor = [[MDCPalette greyPalette] tint500];
+  item.commandID = GoogleServicesSettingsCommandIDOpenManageSyncedDataPage;
   return item;
 }
 
@@ -441,4 +444,16 @@
   // Needs to be implemented.
 }
 
+- (void)openGoogleActivityPage {
+  // Needs to be implemented.
+}
+
+- (void)openEncryptionDialog {
+  // Needs to be implemented.
+}
+
+- (void)openManageSyncedDataPage {
+  // Needs to be implemented.
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_view_controller.mm b/ios/chrome/browser/ui/settings/google_services_settings_view_controller.mm
index d289f6e..c9ec989 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_view_controller.mm
@@ -102,6 +102,9 @@
       static_cast<GoogleServicesSettingsCommandID>(syncSwitchItem.commandID);
   switch (commandID) {
     case GoogleServicesSettingsCommandIDNoOp:
+    case GoogleServicesSettingsCommandIDOpenGoogleActivityPage:
+    case GoogleServicesSettingsCommandIDOpenEncryptionDialog:
+    case GoogleServicesSettingsCommandIDOpenManageSyncedDataPage:
       NOTREACHED();
       break;
     case GoogleServicesSettingsCommandIDToggleSyncEverything:
@@ -207,7 +210,16 @@
       shouldHighlightItemAtIndexPath:indexPath];
   CollectionViewItem* item =
       [self.collectionViewModel itemAtIndexPath:indexPath];
-  return ![item isKindOfClass:[SyncSwitchItem class]];
+  if ([item isKindOfClass:[SyncSwitchItem class]]) {
+    return NO;
+  } else if ([item isKindOfClass:[SettingsCollapsibleItem class]]) {
+    return YES;
+  } else if ([item isKindOfClass:[CollectionViewTextItem class]]) {
+    CollectionViewTextItem* textItem =
+        base::mac::ObjCCast<CollectionViewTextItem>(item);
+    return textItem.commandID != 0;
+  }
+  return NO;
 }
 
 - (void)collectionView:(UICollectionView*)collectionView
@@ -217,6 +229,39 @@
       [self.collectionViewModel itemAtIndexPath:indexPath];
   if ([item isKindOfClass:[SettingsCollapsibleItem class]]) {
     [self toggleSectionWithIndexPath:indexPath];
+    return;
+  }
+  CollectionViewTextItem* textItem =
+      base::mac::ObjCCastStrict<CollectionViewTextItem>(
+          [self.collectionViewModel itemAtIndexPath:indexPath]);
+  GoogleServicesSettingsCommandID commandID =
+      static_cast<GoogleServicesSettingsCommandID>(textItem.commandID);
+  switch (commandID) {
+    case GoogleServicesSettingsCommandIDOpenGoogleActivityPage:
+      [self.commandHandler openGoogleActivityPage];
+      break;
+    case GoogleServicesSettingsCommandIDOpenEncryptionDialog:
+      [self.commandHandler openEncryptionDialog];
+      break;
+    case GoogleServicesSettingsCommandIDOpenManageSyncedDataPage:
+      [self.commandHandler openManageSyncedDataPage];
+      break;
+    case GoogleServicesSettingsCommandIDNoOp:
+    case GoogleServicesSettingsCommandIDToggleSyncEverything:
+    case GoogleServicesSettingsCommandIDToggleBookmarkSync:
+    case GoogleServicesSettingsCommandIDToggleHistorySync:
+    case GoogleServicesSettingsCommandIDTogglePasswordsSync:
+    case GoogleServicesSettingsCommandIDToggleOpenTabsSync:
+    case GoogleServicesSettingsCommandIDToggleAutofillSync:
+    case GoogleServicesSettingsCommandIDToggleSettingsSync:
+    case GoogleServicesSettingsCommandIDToggleReadingListSync:
+    case GoogleServicesSettingsCommandIDToggleActivityAndInteractionsService:
+    case GoogleServicesSettingsCommandIDToggleAutocompleteSearchesService:
+    case GoogleServicesSettingsCommandIDTogglePreloadPagesService:
+    case GoogleServicesSettingsCommandIDToggleImproveChromeService:
+    case GoogleServicesSettingsCommandIDToggleBetterSearchAndBrowsingService:
+      NOTREACHED();
+      break;
   }
 }
 
diff --git a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
index d07f164..0908efc 100644
--- a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller.mm
@@ -417,10 +417,11 @@
 }
 
 - (CollectionViewItem*)switchItemForAutofillWalletImport {
+  NSString* title = l10n_util::GetNSString(
+      IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL);
   SyncSwitchItem* autofillWalletImportItem =
       [self switchItemWithType:ItemTypeAutofillWalletImport
-                         title:l10n_util::GetNSString(
-                                   IDS_IOS_AUTOFILL_USE_WALLET_DATA)
+                         title:title
                       subTitle:nil];
   autofillWalletImportItem.on = [self isAutofillWalletImportOn];
   autofillWalletImportItem.enabled = [self isAutofillWalletImportItemEnabled];
diff --git a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller_unittest.mm
index 440d1c03..d90312b 100644
--- a/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/sync_settings_collection_view_controller_unittest.mm
@@ -11,6 +11,7 @@
 #include "components/autofill/core/common/autofill_pref_names.h"
 #include "components/browser_sync/profile_sync_service_mock.h"
 #include "components/google/core/common/google_util.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "ios/chrome/browser/application_context.h"
@@ -277,8 +278,9 @@
   }
 
   SyncSwitchItem* autofillWalletImportItem = GetCollectionViewItem(1, item);
-  EXPECT_NSEQ(autofillWalletImportItem.text,
-              l10n_util::GetNSString(IDS_IOS_AUTOFILL_USE_WALLET_DATA));
+  NSString* title = l10n_util::GetNSString(
+      IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL);
+  EXPECT_NSEQ(autofillWalletImportItem.text, title);
 
   TextAndErrorItem* encryptionItem = GetCollectionViewItem(2, 0);
   EXPECT_NSEQ(encryptionItem.text,
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
index ee68337e..000d610f 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -50,6 +50,9 @@
 @property(nonatomic, copy) NSString* selectedItemID;
 // Index of the selected item in |items|.
 @property(nonatomic, readonly) NSUInteger selectedIndex;
+// ID of the last item to be inserted. This is used to track if the active tab
+// was newly created when building the animation layout for transitions.
+@property(nonatomic, copy) NSString* lastInsertedItemID;
 // The gesture recognizer used for interactive item reordering.
 @property(nonatomic, strong)
     UILongPressGestureRecognizer* itemReorderRecognizer;
@@ -79,6 +82,7 @@
 @synthesize collectionView = _collectionView;
 @synthesize items = _items;
 @synthesize selectedItemID = _selectedItemID;
+@synthesize lastInsertedItemID = _lastInsertedItemID;
 @synthesize itemReorderRecognizer = _itemReorderRecognizer;
 @synthesize itemReorderTouchPoint = _itemReorderTouchPoint;
 @synthesize emptyStateAnimator = _emptyStateAnimator;
@@ -150,6 +154,7 @@
   // Update the delegate, in case it wasn't set when |items| was populated.
   [self.delegate gridViewController:self didChangeItemCount:self.items.count];
   [self animateEmptyStateOut];
+  self.lastInsertedItemID = nil;
 }
 
 - (void)viewWillDisappear:(BOOL)animated {
@@ -218,6 +223,10 @@
       activeItem = [GridTransitionActiveItem itemWithCell:activeCell
                                                    center:attributes.center
                                                      size:attributes.size];
+      // If the active item is the last inserted item, it needs to be animated
+      // differently.
+      if ([cell.itemIdentifier isEqualToString:self.lastInsertedItemID])
+        activeItem.isAppearing = YES;
       selectionItem = [GridTransitionItem
           itemWithCell:[GridTransitionSelectionCell transitionCellFromCell:cell]
                 center:attributes.center];
@@ -357,6 +366,7 @@
   auto modelUpdates = ^{
     [self.items insertObject:item atIndex:index];
     self.selectedItemID = selectedItemID;
+    self.lastInsertedItemID = item.identifier;
     [self.delegate gridViewController:self didChangeItemCount:self.items.count];
   };
   auto collectionViewUpdates = ^{
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index f6b8f4b9..fa2bdb6 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -71,6 +71,8 @@
 // Child view controllers.
 @property(nonatomic, strong) GridViewController* regularTabsViewController;
 @property(nonatomic, strong) GridViewController* incognitoTabsViewController;
+// Array holding the child page view controllers.
+@property(nonatomic, strong) NSArray<UIViewController*>* pageViewControllers;
 // Other UI components.
 @property(nonatomic, weak) UIScrollView* scrollView;
 @property(nonatomic, weak) UIView* scrollContentView;
@@ -88,8 +90,13 @@
 @property(nonatomic, assign) TabGridConfiguration configuration;
 // Setting the current page will adjust the scroll view to the correct position.
 @property(nonatomic, assign) TabGridPage currentPage;
+// The UIViewController corresponding with |currentPage|.
+@property(nonatomic, readonly) UIViewController* currentPageViewController;
 // The frame of |self.view| when it initially appeared.
 @property(nonatomic, assign) CGRect initialFrame;
+// Whether the scroll view is animating its content offset to the current page.
+@property(nonatomic, assign, getter=isScrollViewAnimatingContentOffset)
+    BOOL scrollViewAnimatingContentOffset;
 @end
 
 @implementation TabGridViewController
@@ -107,6 +114,7 @@
 @synthesize regularTabsViewController = _regularTabsViewController;
 @synthesize incognitoTabsViewController = _incognitoTabsViewController;
 @synthesize remoteTabsViewController = _remoteTabsViewController;
+@synthesize pageViewControllers = _pageViewControllers;
 @synthesize scrollView = _scrollView;
 @synthesize scrollContentView = _scrollContentView;
 @synthesize topToolbar = _topToolbar;
@@ -119,12 +127,18 @@
 @synthesize configuration = _configuration;
 @synthesize currentPage = _currentPage;
 @synthesize initialFrame = _initialFrame;
+@synthesize scrollViewAnimatingContentOffset =
+    _scrollViewAnimatingContentOffset;
 
 - (instancetype)init {
   if (self = [super init]) {
     _regularTabsViewController = [[GridViewController alloc] init];
     _incognitoTabsViewController = [[GridViewController alloc] init];
     _remoteTabsViewController = [[RecentTabsTableViewController alloc] init];
+    _pageViewControllers = @[
+      _incognitoTabsViewController, _regularTabsViewController,
+      _remoteTabsViewController
+    ];
   }
   return self;
 }
@@ -167,6 +181,7 @@
   [super viewDidAppear:animated];
   self.initialFrame = self.view.frame;
   [self modifyChildViewControllerInsetsAndScrollViewOffets];
+  [self updatePageViewAccessibilityVisibility];
 }
 
 - (void)viewWillDisappear:(BOOL)animated {
@@ -233,16 +248,23 @@
   // tapping on the page control during scrolling can result in erratic
   // scrolling.
   self.topToolbar.pageControl.userInteractionEnabled = NO;
+  [self updatePageViewAccessibilityVisibility];
 }
 
 - (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
                   willDecelerate:(BOOL)decelerate {
   // Re-enable the page control since the user isn't dragging anymore.
   self.topToolbar.pageControl.userInteractionEnabled = YES;
+  [self updatePageViewAccessibilityVisibility];
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
+  [self updatePageViewAccessibilityVisibility];
 }
 
 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView {
   _currentPage = GetPageFromScrollView(scrollView);
+  self.scrollViewAnimatingContentOffset = NO;
   [self broadcastIncognitoContentVisibility];
   [self configureButtonsForActiveAndCurrentPage];
 }
@@ -438,11 +460,38 @@
     [self.scrollView setContentOffset:offset animated:NO];
     _currentPage = currentPage;
   } else {
-    [self.scrollView setContentOffset:offset animated:YES];
-    // _currentPage is set in scrollViewDidEndScrollingAnimation:
+    // Only set |scrollViewAnimatingContentOffset| to YES if there's an actual
+    // change in the contentOffset, as |-scrollViewDidEndScrollingAnimation:| is
+    // never called if the animation does not occur.
+    if (!CGPointEqualToPoint(self.scrollView.contentOffset, offset)) {
+      self.scrollViewAnimatingContentOffset = YES;
+      [self.scrollView setContentOffset:offset animated:YES];
+      // _currentPage is set in scrollViewDidEndScrollingAnimation:
+    } else {
+      _currentPage = currentPage;
+    }
   }
 }
 
+- (UIViewController*)currentPageViewController {
+  switch (self.currentPage) {
+    case TabGridPageIncognitoTabs:
+      return self.incognitoTabsViewController;
+    case TabGridPageRegularTabs:
+      return self.regularTabsViewController;
+    case TabGridPageRemoteTabs:
+      return self.remoteTabsViewController;
+  }
+}
+
+- (void)setScrollViewAnimatingContentOffset:
+    (BOOL)scrollViewAnimatingContentOffset {
+  if (_scrollViewAnimatingContentOffset == scrollViewAnimatingContentOffset)
+    return;
+  _scrollViewAnimatingContentOffset = scrollViewAnimatingContentOffset;
+  [self updatePageViewAccessibilityVisibility];
+}
+
 // Adds the scroll view and sets constraints.
 - (void)setupScrollView {
   UIScrollView* scrollView = [[UIScrollView alloc] init];
@@ -774,6 +823,19 @@
       kTabGridCloseAllButtonIdentifier;
 }
 
+// Updates the visibility of the pages' accessibility elements.  When
+// |scrollView| is scrolling, all pages should be visible.  When stationary,
+// however, the accessibility elements of off-screen pages should be hidden.
+- (void)updatePageViewAccessibilityVisibility {
+  BOOL scrolling = self.scrollView.dragging || self.scrollView.decelerating ||
+                   self.scrollViewAnimatingContentOffset;
+  UIViewController* currentPageViewController = self.currentPageViewController;
+  for (UIViewController* pageViewController in self.pageViewControllers) {
+    pageViewController.view.accessibilityElementsHidden =
+        !scrolling && pageViewController != currentPageViewController;
+  }
+}
+
 // Shows (by setting the alpha to 1.0) the two toolbar views and the floating
 // button. Suitable for use in animations.
 - (void)showToolbars {
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
index 339c4c5d..19c19e6a 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
@@ -61,7 +61,6 @@
   [containerView addSubview:presentedView];
   presentedView.frame =
       [transitionContext finalFrameForViewController:presentedViewController];
-  presentedView.alpha = 0.0;
 
   // Get the layout of the grid for the transition.
   GridTransitionLayout* layout =
@@ -103,6 +102,10 @@
       [self.stateProvider proxyPositionForTransitionContext:transitionContext];
   [proxyContainer insertSubview:self.animation aboveSubview:viewBehindProxies];
 
+  // Make the presented view alpha-zero; this should happen after all snapshots
+  // are taken.
+  presentedView.alpha = 0.1;
+
   [self.animation.animator addCompletion:^(UIViewAnimatingPosition position) {
     BOOL finished = (position == UIViewAnimatingPositionEnd);
     [self gridTransitionAnimationDidFinish:finished];
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
index d0ab6bf1..75d07546 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
@@ -262,9 +262,13 @@
   // is shown.
 
   UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
-  // The top and main tab views start at zero alpha but are crossfaded in.
-  activeCell.mainTabView.alpha = 0.0;
+  // The top tab view starts at zero alpha but is crossfaded in.
   activeCell.topTabView.alpha = 0.0;
+  // If the active item is appearing, the main tab view is shown. If not, it's
+  // hidden, and may be faded in if it's expected to be different in content
+  // from the existing cell snapshot.
+  if (!self.layout.activeItem.isAppearing)
+    activeCell.mainTabView.alpha = 0.0;
 
   // A: Zoom the active cell into position.
   UIViewPropertyAnimator* zoomActiveCell =
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h
index b966d5d0..f2cb3bb 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h
@@ -75,6 +75,9 @@
 // The size of |cell| in the grid.
 @property(nonatomic, readonly) CGSize size;
 
+// YES if the item is "appearing" in the grid as part of this animation.
+@property(nonatomic, assign) BOOL isAppearing;
+
 // Creates a new active item instance with |cell|, |center| and |size|.
 + (instancetype)itemWithCell:(UIView<GridToTabTransitionView>*)cell
                       center:(CGPoint)center
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm
index c07118b1..1371c3e7 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm
@@ -65,6 +65,7 @@
 @implementation GridTransitionActiveItem
 @dynamic cell;
 @synthesize size = _size;
+@synthesize isAppearing = _isAppearing;
 
 + (instancetype)itemWithCell:(UIView<GridToTabTransitionView>*)cell
                       center:(CGPoint)center
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
index 7b303afa..d61678d 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
@@ -72,10 +72,16 @@
     self.titleLabel = [[UILabel alloc] init];
     self.titleLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+    [self.titleLabel
+        setContentCompressionResistancePriority:UILayoutPriorityRequired
+                                        forAxis:UILayoutConstraintAxisVertical];
     self.subtitleLabel = [[UILabel alloc] init];
     self.subtitleLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     self.subtitleLabel.textColor = [UIColor lightGrayColor];
+    [self.subtitleLabel
+        setContentCompressionResistancePriority:UILayoutPriorityRequired
+                                        forAxis:UILayoutConstraintAxisVertical];
 
     // Vertical StackView.
     UIStackView* verticalStack = [[UIStackView alloc]
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.mm
index b216b5856..b117c29 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.mm
@@ -76,26 +76,41 @@
     [containerView addSubview:verticalStack];
     [self.contentView addSubview:containerView];
 
-    // If performBatchUpdates is used with this header there might be a need to
-    // change the padding constraints priority. See https://crbug.com/854117 for
-    // more information.
+    // Lower the padding constraints priority. UITableView might try to set
+    // the header view height/width to 0 breaking the constraints. See
+    // https://crbug.com/854117 for more information.
+    NSLayoutConstraint* heightConstraint =
+        [self.contentView.heightAnchor constraintGreaterThanOrEqualToConstant:
+                                           kTableViewHeaderFooterViewHeight];
+    // Set this constraint to UILayoutPriorityDefaultHigh + 1 in order to guard
+    // against some elements that might UILayoutPriorityDefaultHigh to expand
+    // beyond the margins.
+    heightConstraint.priority = UILayoutPriorityDefaultHigh + 1;
+    NSLayoutConstraint* topAnchorConstraint = [containerView.topAnchor
+        constraintGreaterThanOrEqualToAnchor:self.contentView.topAnchor
+                                    constant:kTableViewVerticalSpacing];
+    topAnchorConstraint.priority = UILayoutPriorityDefaultHigh;
+    NSLayoutConstraint* bottomAnchorConstraint = [containerView.bottomAnchor
+        constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
+                                 constant:-kTableViewVerticalSpacing];
+    bottomAnchorConstraint.priority = UILayoutPriorityDefaultHigh;
+    NSLayoutConstraint* leadingAnchorConstraint = [containerView.leadingAnchor
+        constraintEqualToAnchor:self.contentView.leadingAnchor
+                       constant:kTableViewHorizontalSpacing];
+    leadingAnchorConstraint.priority = UILayoutPriorityDefaultHigh;
+    NSLayoutConstraint* trailingAnchorConstraint = [containerView.trailingAnchor
+        constraintEqualToAnchor:self.contentView.trailingAnchor
+                       constant:-kTableViewHorizontalSpacing];
+    trailingAnchorConstraint.priority = UILayoutPriorityDefaultHigh;
+
     // Set and activate constraints.
     [NSLayoutConstraint activateConstraints:@[
       // Container Constraints.
-      [self.contentView.heightAnchor constraintGreaterThanOrEqualToConstant:
-                                         kTableViewHeaderFooterViewHeight],
-      [containerView.leadingAnchor
-          constraintEqualToAnchor:self.contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
-      [containerView.trailingAnchor
-          constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kTableViewHorizontalSpacing],
-      [containerView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:self.contentView.topAnchor
-                                      constant:kTableViewVerticalSpacing],
-      [containerView.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                   constant:-kTableViewVerticalSpacing],
+      heightConstraint,
+      topAnchorConstraint,
+      bottomAnchorConstraint,
+      leadingAnchorConstraint,
+      trailingAnchorConstraint,
       [containerView.centerYAnchor
           constraintEqualToAnchor:self.contentView.centerYAnchor],
       // Vertical StackView Constraints.
diff --git a/net/base/escape.cc b/net/base/escape.cc
index e848512..a3fd0f9f 100644
--- a/net/base/escape.cc
+++ b/net/base/escape.cc
@@ -223,27 +223,25 @@
       code_point == 0x1F513 ||  // OPEN LOCK            (%F0%9F%94%93)
 
       // Spaces are also banned, as they can be used to scroll text out of view.
-      (!(rules & UnescapeRule::NONASCII_SPACES) &&
-       (code_point == 0x0085 ||  // NEXT LINE                  (%C2%85)
-        code_point == 0x00A0 ||  // NO-BREAK SPACE             (%C2%A0)
-        code_point == 0x1680 ||  // OGHAM SPACE MARK           (%E1%9A%80)
-        code_point == 0x2000 ||  // EN QUAD                    (%E2%80%80)
-        code_point == 0x2001 ||  // EM QUAD                    (%E2%80%81)
-        code_point == 0x2002 ||  // EN SPACE                   (%E2%80%82)
-        code_point == 0x2003 ||  // EM SPACE                   (%E2%80%83)
-        code_point == 0x2004 ||  // THREE-PER-EM SPACE         (%E2%80%84)
-        code_point == 0x2005 ||  // FOUR-PER-EM SPACE          (%E2%80%85)
-        code_point == 0x2006 ||  // SIX-PER-EM SPACE           (%E2%80%86)
-        code_point == 0x2007 ||  // FIGURE SPACE               (%E2%80%87)
-        code_point == 0x2008 ||  // PUNCTUATION SPACE          (%E2%80%88)
-        code_point == 0x2009 ||  // THIN SPACE                 (%E2%80%89)
-        code_point == 0x200A ||  // HAIR SPACE                 (%E2%80%8A)
-        code_point == 0x2028 ||  // LINE SEPARATOR             (%E2%80%A8)
-        code_point == 0x2029 ||  // PARAGRAPH SEPARATOR        (%E2%80%A9)
-        code_point == 0x202F ||  // NARROW NO-BREAK SPACE      (%E2%80%AF)
-        code_point == 0x205F ||  // MEDIUM MATHEMATICAL SPACE  (%E2%81%9F)
-        code_point == 0x3000     // IDEOGRAPHIC SPACE          (%E3%80%80)
-        )));
+      code_point == 0x0085 ||  // NEXT LINE                  (%C2%85)
+      code_point == 0x00A0 ||  // NO-BREAK SPACE             (%C2%A0)
+      code_point == 0x1680 ||  // OGHAM SPACE MARK           (%E1%9A%80)
+      code_point == 0x2000 ||  // EN QUAD                    (%E2%80%80)
+      code_point == 0x2001 ||  // EM QUAD                    (%E2%80%81)
+      code_point == 0x2002 ||  // EN SPACE                   (%E2%80%82)
+      code_point == 0x2003 ||  // EM SPACE                   (%E2%80%83)
+      code_point == 0x2004 ||  // THREE-PER-EM SPACE         (%E2%80%84)
+      code_point == 0x2005 ||  // FOUR-PER-EM SPACE          (%E2%80%85)
+      code_point == 0x2006 ||  // SIX-PER-EM SPACE           (%E2%80%86)
+      code_point == 0x2007 ||  // FIGURE SPACE               (%E2%80%87)
+      code_point == 0x2008 ||  // PUNCTUATION SPACE          (%E2%80%88)
+      code_point == 0x2009 ||  // THIN SPACE                 (%E2%80%89)
+      code_point == 0x200A ||  // HAIR SPACE                 (%E2%80%8A)
+      code_point == 0x2028 ||  // LINE SEPARATOR             (%E2%80%A8)
+      code_point == 0x2029 ||  // PARAGRAPH SEPARATOR        (%E2%80%A9)
+      code_point == 0x202F ||  // NARROW NO-BREAK SPACE      (%E2%80%AF)
+      code_point == 0x205F ||  // MEDIUM MATHEMATICAL SPACE  (%E2%81%9F)
+      code_point == 0x3000);   // IDEOGRAPHIC SPACE          (%E3%80%80)
 }
 
 // Unescapes |escaped_text| according to |rules|, returning the resulting
diff --git a/net/base/escape.h b/net/base/escape.h
index 0e768bd..4754cdea 100644
--- a/net/base/escape.h
+++ b/net/base/escape.h
@@ -102,9 +102,6 @@
 
     // URL queries use "+" for space. This flag controls that replacement.
     REPLACE_PLUS_WITH_SPACE = 1 << 4,
-
-    // Unescapes space characters that appears as plain blank in visual agents.
-    NONASCII_SPACES = 1 << 5,
   };
 };
 
diff --git a/net/base/escape_unittest.cc b/net/base/escape_unittest.cc
index d59d6de..830ff58b 100644
--- a/net/base/escape_unittest.cc
+++ b/net/base/escape_unittest.cc
@@ -246,17 +246,6 @@
        UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
        "%01%02%03%04%05%06%07%08%09 %"},
       {"Hello%20%13%10%02", UnescapeRule::SPACES, "Hello %13%10%02"},
-      // Ideographic space unescaped only if the NONASCII_SPACES flag is set.
-      {"日本語%E3%80%80日本語", UnescapeRule::NONASCII_SPACES,
-       "日本語 日本語"},
-      // Ideographic space remains escaped if the NONASCII_SPACES flag is not
-      // specified.
-      {"日本語%E3%80%80日本語", UnescapeRule::NORMAL, "日本語%E3%80%80日本語"},
-      {"日本語%E3%80%80日本語",
-       UnescapeRule::SPACES | UnescapeRule::PATH_SEPARATORS |
-           UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
-           UnescapeRule::REPLACE_PLUS_WITH_SPACE,
-       "日本語%E3%80%80日本語"},
 
       // '/' and '\\' should only be unescaped by PATH_SEPARATORS.
       {"%2F%5C", UnescapeRule::PATH_SEPARATORS, "/\\"},
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index d3d4849..d01be9f 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -29,7 +29,7 @@
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "net/base/completion_callback.h"
+#include "net/base/completion_once_callback.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_address.h"
@@ -125,7 +125,7 @@
   virtual ~DnsAttempt() = default;
   // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously
   // and calls |callback| upon completion.
-  virtual int Start(const CompletionCallback& callback) = 0;
+  virtual int Start(CompletionOnceCallback callback) = 0;
 
   // Returns the query of this attempt.
   virtual const DnsQuery* GetQuery() const = 0;
@@ -184,9 +184,9 @@
 
   // DnsAttempt methods.
 
-  int Start(const CompletionCallback& callback) override {
+  int Start(CompletionOnceCallback callback) override {
     DCHECK_EQ(STATE_NONE, next_state_);
-    callback_ = callback;
+    callback_ = std::move(callback);
     start_time_ = base::TimeTicks::Now();
     next_state_ = STATE_SEND_QUERY;
     return DoLoop(OK);
@@ -305,7 +305,7 @@
   void OnIOComplete(int rv) {
     rv = DoLoop(rv);
     if (rv != ERR_IO_PENDING)
-      callback_.Run(rv);
+      std::move(callback_).Run(rv);
   }
 
   State next_state_;
@@ -316,7 +316,7 @@
 
   std::unique_ptr<DnsResponse> response_;
 
-  CompletionCallback callback_;
+  CompletionOnceCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt);
 };
@@ -395,14 +395,14 @@
 
   // DnsAttempt overrides.
 
-  int Start(const CompletionCallback& callback) override {
+  int Start(CompletionOnceCallback callback) override {
     if (DNSDomainToString(query_->qname()).compare(request_->url().host()) ==
         0) {
       // Fast failing looking up a server with itself.
       return ERR_DNS_HTTP_FAILED;
     }
 
-    callback_ = callback;
+    callback_ = std::move(callback);
     request_->Start();
     return ERR_IO_PENDING;
   }
@@ -502,7 +502,7 @@
  private:
   void ResponseCompleted(int net_error) {
     request_.reset();
-    callback_.Run(CompleteResponse(net_error));
+    std::move(callback_).Run(CompleteResponse(net_error));
   }
 
   int CompleteResponse(int net_error) {
@@ -529,7 +529,7 @@
 
   scoped_refptr<GrowableIOBuffer> buffer_;
   std::unique_ptr<DnsQuery> query_;
-  CompletionCallback callback_;
+  CompletionOnceCallback callback_;
   std::unique_ptr<DnsResponse> response_;
   std::unique_ptr<URLRequest> request_;
   NetLogWithSource net_log_;
@@ -552,9 +552,9 @@
         response_length_(0) {}
 
   // DnsAttempt:
-  int Start(const CompletionCallback& callback) override {
+  int Start(CompletionOnceCallback callback) override {
     DCHECK_EQ(STATE_NONE, next_state_);
-    callback_ = callback;
+    callback_ = std::move(callback);
     start_time_ = base::TimeTicks::Now();
     next_state_ = STATE_CONNECT_COMPLETE;
     int rv = socket_->Connect(
@@ -755,7 +755,7 @@
   void OnIOComplete(int rv) {
     rv = DoLoop(rv);
     if (rv != ERR_IO_PENDING)
-      callback_.Run(rv);
+      std::move(callback_).Run(rv);
   }
 
   int ReadIntoBuffer() {
@@ -775,7 +775,7 @@
   uint16_t response_length_;
   std::unique_ptr<DnsResponse> response_;
 
-  CompletionCallback callback_;
+  CompletionOnceCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(DnsTCPAttempt);
 };
diff --git a/net/http/http_auth_handler_mock.cc b/net/http/http_auth_handler_mock.cc
index 52cb3e8..771c5317 100644
--- a/net/http/http_auth_handler_mock.cc
+++ b/net/http/http_auth_handler_mock.cc
@@ -41,7 +41,6 @@
 
 HttpAuthHandlerMock::HttpAuthHandlerMock()
     : state_(State::WAIT_FOR_INIT),
-      resolve_(RESOLVE_INIT),
       generate_async_(false),
       generate_rv_(OK),
       auth_token_(NULL),
@@ -53,48 +52,6 @@
 
 HttpAuthHandlerMock::~HttpAuthHandlerMock() = default;
 
-void HttpAuthHandlerMock::SetResolveExpectation(Resolve resolve) {
-  EXPECT_EQ(RESOLVE_INIT, resolve_);
-  resolve_ = resolve;
-}
-
-bool HttpAuthHandlerMock::NeedsCanonicalName() {
-  switch (resolve_) {
-    case RESOLVE_SYNC:
-    case RESOLVE_ASYNC:
-      return true;
-    case RESOLVE_SKIP:
-      resolve_ = RESOLVE_TESTED;
-      return false;
-    default:
-      NOTREACHED();
-      return false;
-  }
-}
-
-int HttpAuthHandlerMock::ResolveCanonicalName(
-    HostResolver* host_resolver, const CompletionCallback& callback) {
-  EXPECT_NE(RESOLVE_TESTED, resolve_);
-  int rv = OK;
-  switch (resolve_) {
-    case RESOLVE_SYNC:
-      resolve_ = RESOLVE_TESTED;
-      break;
-    case RESOLVE_ASYNC:
-      EXPECT_TRUE(callback_.is_null());
-      rv = ERR_IO_PENDING;
-      callback_ = callback;
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&HttpAuthHandlerMock::OnResolveCanonicalName,
-                                weak_factory_.GetWeakPtr()));
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  return rv;
-}
-
 void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) {
   generate_async_ = async;
   generate_rv_ = rv;
@@ -172,13 +129,6 @@
   }
 }
 
-void HttpAuthHandlerMock::OnResolveCanonicalName() {
-  EXPECT_EQ(RESOLVE_ASYNC, resolve_);
-  EXPECT_TRUE(!callback_.is_null());
-  resolve_ = RESOLVE_TESTED;
-  base::ResetAndReturn(&callback_).Run(OK);
-}
-
 void HttpAuthHandlerMock::OnGenerateAuthToken() {
   EXPECT_TRUE(generate_async_);
   EXPECT_TRUE(!callback_.is_null());
diff --git a/net/http/http_auth_handler_mock.h b/net/http/http_auth_handler_mock.h
index 6a1e58e..691d9e5 100644
--- a/net/http/http_auth_handler_mock.h
+++ b/net/http/http_auth_handler_mock.h
@@ -17,8 +17,6 @@
 
 namespace net {
 
-class HostResolver;
-
 // MockAuthHandler is used in tests to reliably trigger edge cases.
 class HttpAuthHandlerMock : public HttpAuthHandler {
  public:
@@ -30,14 +28,6 @@
     DONE
   };
 
-  enum Resolve {
-    RESOLVE_INIT,
-    RESOLVE_SKIP,
-    RESOLVE_SYNC,
-    RESOLVE_ASYNC,
-    RESOLVE_TESTED,
-  };
-
   // The Factory class returns handlers in the order they were added via
   // AddMockHandler.
   class Factory : public HttpAuthHandlerFactory {
@@ -71,13 +61,6 @@
 
   ~HttpAuthHandlerMock() override;
 
-  void SetResolveExpectation(Resolve resolve);
-
-  virtual bool NeedsCanonicalName();
-
-  virtual int ResolveCanonicalName(HostResolver* host_resolver,
-                                   const CompletionCallback& callback);
-
 
   void SetGenerateExpectation(bool async, int rv);
 
@@ -116,12 +99,9 @@
                             std::string* auth_token) override;
 
  private:
-  void OnResolveCanonicalName();
-
   void OnGenerateAuthToken();
 
   State state_;
-  Resolve resolve_;
   CompletionCallback callback_;
   bool generate_async_;
   int generate_rv_;
diff --git a/net/http/http_network_transaction_ssl_unittest.cc b/net/http/http_network_transaction_ssl_unittest.cc
index 2ff92a20..e4e7e08 100644
--- a/net/http/http_network_transaction_ssl_unittest.cc
+++ b/net/http/http_network_transaction_ssl_unittest.cc
@@ -48,6 +48,11 @@
 
   void GetSSLConfig(SSLConfig* config) override { *config = ssl_config_; }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   SSLConfig ssl_config_;
 };
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 1e9f9f30..9c36801 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -278,6 +278,11 @@
 
   void GetSSLConfig(SSLConfig* config) override { *config = config_; }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   SSLConfig config_;
 };
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc
index c686116..dbe908b 100644
--- a/net/http/http_proxy_client_socket_wrapper_unittest.cc
+++ b/net/http/http_proxy_client_socket_wrapper_unittest.cc
@@ -53,6 +53,11 @@
 
   void GetSSLConfig(SSLConfig* config) override { *config = config_; }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   SSLConfig config_;
 };
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index c261f55..d47261b 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -44810,7 +44810,6 @@
     { "name": "unlogis.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "unterhaltungsbox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "v5wz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "va.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "valentin-ochs.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vdmeij.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vegalayer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54586,6 +54585,7 @@
     { "name": "hyatt.com", "policy": "custom", "mode": "force-https", "include_subdomains": false },
     { "name": "ft.com", "policy": "custom", "mode": "force-https", "include_subdomains": false },
     { "name": "www.ft.com", "policy": "custom", "mode": "force-https", "include_subdomains": true },
+    { "name": "va.gov", "policy": "custom", "mode": "force-https", "include_subdomains": false },
     // HPKP
     { "name": "swehack.org", "policy": "custom", "mode": "force-https", "include_subdomains": true, "pins": "swehackCom" },
     { "name": "ncsccs.com", "policy": "custom", "mode": "force-https", "include_subdomains": true, "pins": "ncsccs" },
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 4a3475a..4e7afc9 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -503,7 +503,7 @@
     session_.reset(new QuicChromiumClientSession(
         connection_, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_,
+        &transport_security_state_, /*ssl_config_service=*/nullptr,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey(kDefaultServerHostName, kDefaultServerPort,
                        PRIVACY_MODE_DISABLED, SocketTag()),
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 6cedcc8b..f7d77a91 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -679,6 +679,7 @@
     QuicCryptoClientStreamFactory* crypto_client_stream_factory,
     quic::QuicClock* clock,
     TransportSecurityState* transport_security_state,
+    SSLConfigService* ssl_config_service,
     std::unique_ptr<QuicServerInfo> server_info,
     const QuicSessionKey& session_key,
     bool require_confirmation,
@@ -720,6 +721,7 @@
       most_recent_write_error_timestamp_(base::TimeTicks()),
       stream_factory_(stream_factory),
       transport_security_state_(transport_security_state),
+      ssl_config_service_(ssl_config_service),
       server_info_(std::move(server_info)),
       pkp_bypassed_(false),
       is_fatal_cert_error_(false),
@@ -1240,7 +1242,8 @@
   }
 
   return SpdySession::CanPool(transport_security_state_, ssl_info,
-                              session_key_.host(), hostname);
+                              *ssl_config_service_, session_key_.host(),
+                              hostname);
 }
 
 bool QuicChromiumClientSession::ShouldCreateIncomingDynamicStream(
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index 43af292..c206486 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -54,6 +54,7 @@
 class QuicCryptoClientStreamFactory;
 class QuicServerInfo;
 class QuicStreamFactory;
+class SSLConfigService;
 class SSLInfo;
 class TransportSecurityState;
 
@@ -374,6 +375,7 @@
       QuicCryptoClientStreamFactory* crypto_client_stream_factory,
       quic::QuicClock* clock,
       TransportSecurityState* transport_security_state,
+      SSLConfigService* ssl_config_service,
       std::unique_ptr<QuicServerInfo> server_info,
       const QuicSessionKey& session_key,
       bool require_confirmation,
@@ -742,6 +744,7 @@
   QuicStreamFactory* stream_factory_;
   std::vector<std::unique_ptr<DatagramClientSocket>> sockets_;
   TransportSecurityState* transport_security_state_;
+  SSLConfigService* ssl_config_service_;
   std::unique_ptr<QuicServerInfo> server_info_;
   std::unique_ptr<CertVerifyResult> cert_verify_result_;
   std::unique_ptr<ct::CTVerifyResult> ct_verify_result_;
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc
index 68d0b3c..57134637 100644
--- a/net/quic/chromium/quic_chromium_client_session_test.cc
+++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -148,7 +148,7 @@
     session_.reset(new TestingQuicChromiumClientSession(
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_,
+        &transport_security_state_, /*ssl_config_service=*/nullptr,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)), session_key_,
         /*require_confirmation=*/false, migrate_session_early_v2_,
         /*migrate_session_on_network_change_v2=*/false,
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc
index 94686e0..fbf52ab 100644
--- a/net/quic/chromium/quic_http_stream_test.cc
+++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -313,7 +313,7 @@
     session_.reset(new QuicChromiumClientSession(
         connection_, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_,
+        &transport_security_state_, /*ssl_config_service=*/nullptr,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey(kDefaultServerHostName, kDefaultServerPort,
                        PRIVACY_MODE_DISABLED, SocketTag()),
diff --git a/net/quic/chromium/quic_proxy_client_socket_unittest.cc b/net/quic/chromium/quic_proxy_client_socket_unittest.cc
index dd03e843..0a687a3 100644
--- a/net/quic/chromium/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/chromium/quic_proxy_client_socket_unittest.cc
@@ -204,7 +204,7 @@
     session_.reset(new QuicChromiumClientSession(
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
-        &transport_security_state_,
+        &transport_security_state_, /*ssl_config_service=*/nullptr,
         base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
         QuicSessionKey("mail.example.org", 80, PRIVACY_MODE_DISABLED,
                        SocketTag()),
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index eccfc9f..1e6085e 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -1549,10 +1549,10 @@
 
   *session = new QuicChromiumClientSession(
       connection, std::move(socket), this, quic_crypto_client_stream_factory_,
-      clock_, transport_security_state_, std::move(server_info),
-      key.session_key(), require_confirmation, migrate_sessions_early_v2_,
-      migrate_sessions_on_network_change_v2_, default_network_,
-      max_time_on_non_default_network_,
+      clock_, transport_security_state_, ssl_config_service_,
+      std::move(server_info), key.session_key(), require_confirmation,
+      migrate_sessions_early_v2_, migrate_sessions_on_network_change_v2_,
+      default_network_, max_time_on_non_default_network_,
       max_migrations_to_non_default_network_on_path_degrading_,
       yield_after_packets_, yield_after_duration_,
       headers_include_h2_stream_dependency_, cert_verify_flags, config,
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 1a5aa9f..7cb3f67 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -75,6 +75,11 @@
 
   void GetSSLConfig(SSLConfig* config) override { *config = config_; }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   SSLConfig config_;
 };
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 157f2e5..7c3c50f6 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -7668,6 +7668,7 @@
 #if BUILDFLAG(ENABLE_WEBSOCKETS)
 
 TEST_F(SpdyNetworkTransactionTest, WebSocketOpensNewConnection) {
+  base::HistogramTester histogram_tester;
   NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
   helper.RunPreTestSetup();
 
@@ -7768,12 +7769,18 @@
   // HTTP/2 connection is still open, but WebSocket request did not pool to it.
   ASSERT_TRUE(spdy_session);
 
-  base::RunLoop().RunUntilIdle();
   data1.Resume();
+  base::RunLoop().RunUntilIdle();
   helper.VerifyDataConsumed();
+
+  // Server did not advertise WebSocket support.
+  histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
+                                      /* support_websocket = false */ 0,
+                                      /* expected_count = */ 1);
 }
 
 TEST_F(SpdyNetworkTransactionTest, WebSocketOverHTTP2) {
+  base::HistogramTester histogram_tester;
   auto session_deps = std::make_unique<SpdySessionDependencies>();
   session_deps->enable_websocket_over_http2 = true;
   NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
@@ -7889,6 +7896,11 @@
   ASSERT_THAT(rv, IsOk());
 
   helper.VerifyDataConsumed();
+
+  // Server advertised WebSocket support.
+  histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
+                                      /* support_websocket = true */ 1,
+                                      /* expected_count = */ 1);
 }
 
 TEST_F(SpdyNetworkTransactionTest, WebSocketNegotiatesHttp2) {
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 2049ec2..e328d52 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -716,6 +716,7 @@
 // static
 bool SpdySession::CanPool(TransportSecurityState* transport_security_state,
                           const SSLInfo& ssl_info,
+                          const SSLConfigService& ssl_config_service,
                           const std::string& old_hostname,
                           const std::string& new_hostname) {
   // Pooling is prohibited if the server cert is not valid for the new domain,
@@ -724,8 +725,11 @@
   if (IsCertStatusError(ssl_info.cert_status))
     return false;
 
-  if (ssl_info.client_cert_sent)
+  if (ssl_info.client_cert_sent &&
+      !(ssl_config_service.CanShareConnectionWithClientCerts(old_hostname) &&
+        ssl_config_service.CanShareConnectionWithClientCerts(new_hostname))) {
     return false;
+  }
 
   if (ssl_info.channel_id_sent &&
       ChannelIDService::GetDomainForHost(new_hostname) !=
@@ -772,6 +776,7 @@
     const SpdySessionKey& spdy_session_key,
     HttpServerProperties* http_server_properties,
     TransportSecurityState* transport_security_state,
+    SSLConfigService* ssl_config_service,
     const quic::QuicTransportVersionVector& quic_supported_versions,
     bool enable_sending_initial_data,
     bool enable_ping_based_connection_checking,
@@ -787,6 +792,7 @@
       pool_(NULL),
       http_server_properties_(http_server_properties),
       transport_security_state_(transport_security_state),
+      ssl_config_service_(ssl_config_service),
       stream_hi_water_mark_(kFirstStreamId),
       last_accepted_push_stream_id_(0),
       push_delegate_(push_delegate),
@@ -971,8 +977,8 @@
   if (!GetSSLInfo(&ssl_info))
     return true;  // This is not a secure session, so all domains are okay.
 
-  return CanPool(transport_security_state_, ssl_info, host_port_pair().host(),
-                 domain);
+  return CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
+                 host_port_pair().host(), domain);
 }
 
 void SpdySession::EnqueueStreamWrite(
@@ -1774,8 +1780,8 @@
       }
       SSLInfo ssl_info;
       CHECK(GetSSLInfo(&ssl_info));
-      if (!CanPool(transport_security_state_, ssl_info, associated_url.host(),
-                   gurl.host())) {
+      if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
+                   associated_url.host(), gurl.host())) {
         RecordSpdyPushedStreamFateHistogram(
             SpdyPushedStreamFate::kCertificateMismatch);
         EnqueueResetStreamFrame(stream_id, request_priority,
@@ -2618,6 +2624,8 @@
   DCHECK_LE(bytes_pushed_and_unclaimed_count_, bytes_pushed_count_);
   UMA_HISTOGRAM_COUNTS_1M("Net.SpdySession.PushedAndUnclaimedBytes",
                           bytes_pushed_and_unclaimed_count_);
+  UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.ServerSupportsWebSocket",
+                        support_websocket_);
 }
 
 void SpdySession::RecordProtocolErrorHistogram(
@@ -3144,8 +3152,8 @@
     SSLInfo ssl_info;
     if (!GetSSLInfo(&ssl_info))
       return;
-    if (!CanPool(transport_security_state_, ssl_info, host_port_pair().host(),
-                 gurl.host())) {
+    if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
+                 host_port_pair().host(), gurl.host())) {
       return;
     }
     scheme_host_port = url::SchemeHostPort(gurl);
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 0ae7817..88ee4d7b 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -284,6 +284,7 @@
   // |old_hostname| associated with |ssl_info|.
   static bool CanPool(TransportSecurityState* transport_security_state,
                       const SSLInfo& ssl_info,
+                      const SSLConfigService& ssl_config_service,
                       const std::string& old_hostname,
                       const std::string& new_hostname);
 
@@ -294,6 +295,7 @@
   SpdySession(const SpdySessionKey& spdy_session_key,
               HttpServerProperties* http_server_properties,
               TransportSecurityState* transport_security_state,
+              SSLConfigService* ssl_config_service,
               const quic::QuicTransportVersionVector& quic_supported_versions,
               bool enable_sending_initial_data,
               bool enable_ping_based_connection_checking,
@@ -934,6 +936,7 @@
   HttpServerProperties* http_server_properties_;
 
   TransportSecurityState* transport_security_state_;
+  SSLConfigService* ssl_config_service_;
 
   // The socket handle for this session.
   std::unique_ptr<ClientSocketHandle> connection_;
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 8cdd35ab..ee368e0 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -107,10 +107,11 @@
 
   auto new_session = std::make_unique<SpdySession>(
       key, http_server_properties_, transport_security_state_,
-      quic_supported_versions_, enable_sending_initial_data_,
-      enable_ping_based_connection_checking_, support_ietf_format_quic_altsvc_,
-      is_trusted_proxy, session_max_recv_window_size_, initial_settings_,
-      time_func_, push_delegate_, net_log.net_log());
+      ssl_config_service_, quic_supported_versions_,
+      enable_sending_initial_data_, enable_ping_based_connection_checking_,
+      support_ietf_format_quic_altsvc_, is_trusted_proxy,
+      session_max_recv_window_size_, initial_settings_, time_func_,
+      push_delegate_, net_log.net_log());
 
   new_session->InitializeWithSocket(std::move(connection), this);
 
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index ccba2eda..564c2625 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -6434,6 +6434,38 @@
            MapNetErrorToGoAwayStatus(ERR_UNEXPECTED));
 }
 
+namespace {
+
+class TestSSLConfigService : public SSLConfigService {
+ public:
+  TestSSLConfigService() {}
+  ~TestSSLConfigService() override = default;
+
+  void GetSSLConfig(SSLConfig* config) override { *config = config_; }
+
+  // Returns true if |hostname| is in domains_for_pooling_. This is a simpler
+  // implementation than the production implementation in SSLConfigServiceMojo.
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    for (const std::string& domain : domains_for_pooling_) {
+      if (domain == hostname) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void SetDomainsForPooling(const std::vector<std::string>& domains) {
+    domains_for_pooling_ = domains;
+  }
+
+ private:
+  SSLConfig config_;
+  std::vector<std::string> domains_for_pooling_;
+};
+
+}  // namespace
+
 TEST(CanPoolTest, CanPool) {
   // Load a cert that is valid for:
   //   www.example.org
@@ -6441,18 +6473,19 @@
   //   mail.example.com
 
   TransportSecurityState tss;
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
 
-  EXPECT_TRUE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "www.example.org"));
-  EXPECT_TRUE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
-  EXPECT_TRUE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.com"));
-  EXPECT_FALSE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.google.com"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "www.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.com"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.google.com"));
 }
 
 TEST(CanPoolTest, CanPoolExpectCT) {
@@ -6465,6 +6498,7 @@
   //   mail.example.com
 
   TransportSecurityState tss;
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
@@ -6473,8 +6507,8 @@
       ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
   ssl_info.is_issued_by_known_root = true;
 
-  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                   "www.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "www.example.org"));
 
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
@@ -6483,19 +6517,19 @@
 
   // A different Expect-CT enabled host should not be allowed to pool.
   tss.AddExpectCT("mail.example.org", expiry, true, GURL());
-  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                    "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.org"));
   // A report-only Expect-CT configuration should not prevent pooling.
   tss.AddExpectCT("mail.example.org", expiry, false,
                   GURL("https://report.test"));
-  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                   "mail.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
   // If Expect-CT becomes enabled for the same host for which the connection was
   // already made, subsequent connections to that host should not be allowed to
   // pool.
   tss.AddExpectCT("www.example.org", expiry, true, GURL());
-  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                    "www.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "www.example.org"));
 }
 
 TEST(CanPoolTest, CanNotPoolWithCertErrors) {
@@ -6505,13 +6539,14 @@
   //   mail.example.com
 
   TransportSecurityState tss;
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
   ssl_info.cert_status = CERT_STATUS_REVOKED;
 
-  EXPECT_FALSE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanNotPoolWithClientCerts) {
@@ -6521,13 +6556,14 @@
   //   mail.example.com
 
   TransportSecurityState tss;
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
   ssl_info.client_cert_sent = true;
 
-  EXPECT_FALSE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanNotPoolAcrossETLDsWithChannelID) {
@@ -6537,15 +6573,16 @@
   //   mail.example.com
 
   TransportSecurityState tss;
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
   ssl_info.channel_id_sent = true;
 
-  EXPECT_TRUE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
-  EXPECT_FALSE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "www.example.com"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "www.example.com"));
 }
 
 TEST(CanPoolTest, CanNotPoolWithBadPins) {
@@ -6555,14 +6592,15 @@
   TransportSecurityState tss;
   test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin);
 
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
   ssl_info.is_issued_by_known_root = true;
   ssl_info.public_key_hashes.push_back(test::GetTestHashValue(bad_pin));
 
-  EXPECT_FALSE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanNotPoolWithBadCTWhenCTRequired) {
@@ -6570,6 +6608,7 @@
   using CTRequirementLevel =
       TransportSecurityState::RequireCTDelegate::CTRequirementLevel;
 
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
@@ -6588,8 +6627,8 @@
   TransportSecurityState tss;
   tss.SetRequireCTDelegate(&require_ct_delegate);
 
-  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                    "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanPoolWithBadCTWhenCTNotRequired) {
@@ -6597,6 +6636,7 @@
   using CTRequirementLevel =
       TransportSecurityState::RequireCTDelegate::CTRequirementLevel;
 
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
@@ -6615,8 +6655,8 @@
   TransportSecurityState tss;
   tss.SetRequireCTDelegate(&require_ct_delegate);
 
-  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                   "mail.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanPoolWithGoodCTWhenCTRequired) {
@@ -6624,6 +6664,7 @@
   using CTRequirementLevel =
       TransportSecurityState::RequireCTDelegate::CTRequirementLevel;
 
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
@@ -6642,8 +6683,8 @@
   TransportSecurityState tss;
   tss.SetRequireCTDelegate(&require_ct_delegate);
 
-  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, "www.example.org",
-                                   "mail.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
 }
 
 TEST(CanPoolTest, CanPoolWithAcceptablePins) {
@@ -6652,14 +6693,39 @@
   TransportSecurityState tss;
   test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin);
 
+  TestSSLConfigService ssl_config_service;
   SSLInfo ssl_info;
   ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(),
                                      "spdy_pooling.pem");
   ssl_info.is_issued_by_known_root = true;
   ssl_info.public_key_hashes.push_back(test::GetTestHashValue(primary_pin));
 
-  EXPECT_TRUE(SpdySession::CanPool(
-      &tss, ssl_info, "www.example.org", "mail.example.org"));
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
+}
+
+TEST(CanPoolTest, CanPoolWithClientCertsAndPolicy) {
+  TransportSecurityState tss;
+  SSLInfo ssl_info;
+  ssl_info.cert =
+      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+  ssl_info.client_cert_sent = true;
+
+  // Configure ssl_config_service so that CanShareConnectionWithClientCerts
+  // returns true for www.example.org and mail.example.org.
+  TestSSLConfigService ssl_config_service;
+  ssl_config_service.SetDomainsForPooling(
+      {"www.example.org", "mail.example.org"});
+
+  // Test that CanPool returns true when client certs are enabled and
+  // CanShareConnectionWithClientCerts returns true for both hostnames, but not
+  // just one hostname.
+  EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                   "www.example.org", "mail.example.org"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "www.example.org", "mail.example.com"));
+  EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service,
+                                    "mail.example.com", "www.example.org"));
 }
 
 TEST(RecordPushedStreamHistogramTest, VaryResponseHeader) {
diff --git a/net/ssl/ssl_config_service.cc b/net/ssl/ssl_config_service.cc
index 4fa61b6b..c53a449d 100644
--- a/net/ssl/ssl_config_service.cc
+++ b/net/ssl/ssl_config_service.cc
@@ -104,9 +104,10 @@
 }
 
 void SSLConfigService::ProcessConfigUpdate(const SSLConfig& old_config,
-                                           const SSLConfig& new_config) {
+                                           const SSLConfig& new_config,
+                                           bool force_notification) {
   // Do nothing if the configuration hasn't changed.
-  if (!SSLConfigsAreEqual(old_config, new_config))
+  if (!SSLConfigsAreEqual(old_config, new_config) || force_notification)
     NotifySSLConfigChange();
 }
 
diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h
index 3fd298301..21e686c 100644
--- a/net/ssl/ssl_config_service.h
+++ b/net/ssl/ssl_config_service.h
@@ -46,6 +46,32 @@
   // May not be thread-safe, should only be called on the IO thread.
   virtual void GetSSLConfig(SSLConfig* config) = 0;
 
+  // Returns true if connections to |hostname| can reuse, or are permitted to
+  // reuse, connections on which a client cert has been negotiated. Note that
+  // this must return true for both hostnames being pooled - that is to say this
+  // function must return true for both the hostname of the existing connection
+  // and the potential hostname to pool before allowing the connection to be
+  // reused.
+  //
+  // NOTE: Pooling connections with ambient authority can create security issues
+  // with that ambient authority and privacy issues in that embedders (and
+  // users) may not have been consulted to send a client cert to |hostname|.
+  // Implementations of this method should only return true if they have
+  // received affirmative consent (e.g. through preferences or Enterprise
+  // policy).
+  //
+  // NOTE: For Web Platform clients, this violates the Fetch Standard's policies
+  // around connection pools: https://fetch.spec.whatwg.org/#connections.
+  // Implementations that return true should take steps to limit the Web
+  // Platform visibility of this, such as only allowing it to be used for
+  // Enterprise or internal configurations.
+  //
+  // DEPRECATED: For the reasons above, this method is temporary and will be
+  // removed in a future release. Please leave a comment on
+  // https://crbug.com/855690 if you believe this is needed.
+  virtual bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const = 0;
+
   // Sets the current global CRL set to |crl_set|, if and only if the passed CRL
   // set has a higher sequence number (as reported by CRLSet::sequence()) than
   // the current set (or there is no current set). Can be called concurrently
@@ -75,9 +101,12 @@
                                            const net::SSLConfig& config2);
 
  protected:
-  // Process before/after config update.
+  // Process before/after config update. If |force_notification| is true,
+  // NotifySSLConfigChange will be called regardless of whether |orig_config|
+  // and |new_config| are equal.
   void ProcessConfigUpdate(const SSLConfig& orig_config,
-                           const SSLConfig& new_config);
+                           const SSLConfig& new_config,
+                           bool force_notification);
 
   static void SetCRLSet(scoped_refptr<CRLSet> crl_set, bool if_newer);
 
diff --git a/net/ssl/ssl_config_service_defaults.cc b/net/ssl/ssl_config_service_defaults.cc
index a46db2d..f594f71 100644
--- a/net/ssl/ssl_config_service_defaults.cc
+++ b/net/ssl/ssl_config_service_defaults.cc
@@ -13,4 +13,9 @@
   *config = default_config_;
 }
 
+bool SSLConfigServiceDefaults::CanShareConnectionWithClientCerts(
+    const std::string& hostname) const {
+  return false;
+}
+
 }  // namespace net
diff --git a/net/ssl/ssl_config_service_defaults.h b/net/ssl/ssl_config_service_defaults.h
index f949637..ae7c477 100644
--- a/net/ssl/ssl_config_service_defaults.h
+++ b/net/ssl/ssl_config_service_defaults.h
@@ -22,6 +22,9 @@
   // Store default SSL config settings in |config|.
   void GetSSLConfig(SSLConfig* config) override;
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override;
+
  private:
   // Default value of prefs.
   const SSLConfig default_config_;
diff --git a/net/ssl/ssl_config_service_unittest.cc b/net/ssl/ssl_config_service_unittest.cc
index 9253acc..391d663 100644
--- a/net/ssl/ssl_config_service_unittest.cc
+++ b/net/ssl/ssl_config_service_unittest.cc
@@ -21,14 +21,21 @@
   // SSLConfigService implementation
   void GetSSLConfig(SSLConfig* config) override { *config = config_; }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
   // Sets the SSLConfig to be returned by GetSSLConfig and processes any
   // updates.
   void SetSSLConfig(const SSLConfig& config) {
     SSLConfig old_config = config_;
     config_ = config;
-    ProcessConfigUpdate(old_config, config_);
+    ProcessConfigUpdate(old_config, config_, /*force_notification*/ false);
   }
 
+  using SSLConfigService::ProcessConfigUpdate;
+
  private:
   SSLConfig config_;
 };
@@ -60,6 +67,23 @@
   mock_service.RemoveObserver(&observer);
 }
 
+TEST(SSLConfigServiceTest, ForceNotificationNotifiesObservers) {
+  SSLConfig initial_config;
+  initial_config.rev_checking_enabled = true;
+  initial_config.false_start_enabled = false;
+  initial_config.version_min = SSL_PROTOCOL_VERSION_TLS1;
+  initial_config.version_max = SSL_PROTOCOL_VERSION_TLS1_2;
+
+  MockSSLConfigService mock_service(initial_config);
+  MockSSLConfigServiceObserver observer;
+  mock_service.AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1);
+  mock_service.ProcessConfigUpdate(initial_config, initial_config, true);
+
+  mock_service.RemoveObserver(&observer);
+}
+
 TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) {
   SSLConfig initial_config;
   initial_config.rev_checking_enabled = true;
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index be69cc9..aee8755 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -1585,9 +1585,6 @@
       if (used_quic) {
         UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpJob.TotalTime.Secure.Quic",
                                    total_time);
-      } else {
-        UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpJob.TotalTime.Secure.NotQuic",
-                                   total_time);
       }
     }
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 06d9473..235dcff 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4126,8 +4126,6 @@
   return std::move(http_response);
 }
 
-}  // namespace
-
 class TestSSLConfigService : public SSLConfigService {
  public:
   TestSSLConfigService(bool online_rev_checking,
@@ -4157,6 +4155,11 @@
     }
   }
 
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override {
+    return false;
+  }
+
  private:
   const bool online_rev_checking_;
   const bool rev_checking_required_local_anchors_;
@@ -4165,6 +4168,8 @@
   uint16_t max_version_;
 };
 
+}  // namespace
+
 // TODO(svaldez): Update tests to use EmbeddedTestServer.
 #if !defined(OS_IOS)
 class TokenBindingURLRequestTest : public URLRequestTestHTTP {
diff --git a/net/url_request/view_cache_helper.cc b/net/url_request/view_cache_helper.cc
index f7b5aa7..43cfb54 100644
--- a/net/url_request/view_cache_helper.cc
+++ b/net/url_request/view_cache_helper.cc
@@ -4,6 +4,8 @@
 
 #include "net/url_request/view_cache_helper.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/strings/stringprintf.h"
@@ -61,15 +63,16 @@
 int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
                                       const URLRequestContext* context,
                                       std::string* out,
-                                      const CompletionCallback& callback) {
-  return GetInfoHTML(key, context, std::string(), out, callback);
+                                      CompletionOnceCallback callback) {
+  return GetInfoHTML(key, context, std::string(), out, std::move(callback));
 }
 
 int ViewCacheHelper::GetContentsHTML(const URLRequestContext* context,
                                      const std::string& url_prefix,
                                      std::string* out,
-                                     const CompletionCallback& callback) {
-  return GetInfoHTML(std::string(), context, url_prefix, out, callback);
+                                     CompletionOnceCallback callback) {
+  return GetInfoHTML(std::string(), context, url_prefix, out,
+                     std::move(callback));
 }
 
 // static
@@ -118,7 +121,7 @@
                                  const URLRequestContext* context,
                                  const std::string& url_prefix,
                                  std::string* out,
-                                 const CompletionCallback& callback) {
+                                 CompletionOnceCallback callback) {
   DCHECK(callback_.is_null());
   DCHECK(context);
   key_ = key;
@@ -129,7 +132,7 @@
   int rv = DoLoop(OK);
 
   if (rv == ERR_IO_PENDING)
-    callback_ = callback;
+    callback_ = std::move(callback);
 
   return rv;
 }
@@ -138,8 +141,7 @@
   DCHECK_NE(ERR_IO_PENDING, rv);
   DCHECK(!callback_.is_null());
 
-  callback_.Run(rv);
-  callback_.Reset();
+  std::move(callback_).Run(rv);
 }
 
 void ViewCacheHelper::HandleResult(int rv) {
diff --git a/net/url_request/view_cache_helper.h b/net/url_request/view_cache_helper.h
index 89183e3c..8a76c9c6 100644
--- a/net/url_request/view_cache_helper.h
+++ b/net/url_request/view_cache_helper.h
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "net/base/completion_callback.h"
+#include "net/base/completion_once_callback.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_export.h"
 #include "net/disk_cache/disk_cache.h"
@@ -32,7 +32,7 @@
   int GetEntryInfoHTML(const std::string& key,
                        const URLRequestContext* context,
                        std::string* out,
-                       const CompletionCallback& callback);
+                       CompletionOnceCallback callback);
 
   // Formats the cache contents as HTML. Returns a net error code.
   // If this method returns ERR_IO_PENDING, |callback| will be notified when the
@@ -42,7 +42,7 @@
   int GetContentsHTML(const URLRequestContext* context,
                       const std::string& url_prefix,
                       std::string* out,
-                      const CompletionCallback& callback);
+                      CompletionOnceCallback callback);
 
   // Lower-level helper to produce a textual representation of binary data.
   // The results are appended to |result| and can be used in HTML pages
@@ -69,7 +69,7 @@
                   const URLRequestContext* context,
                   const std::string& url_prefix,
                   std::string* out,
-                  const CompletionCallback& callback);
+                  CompletionOnceCallback callback);
 
   // This is a helper function used to trigger a completion callback. It may
   // only be called if callback_ is non-null.
@@ -109,7 +109,7 @@
   std::string key_;
   std::string url_prefix_;
   std::string* data_;
-  CompletionCallback callback_;
+  CompletionOnceCallback callback_;
 
   State next_state_;
 
diff --git a/remoting/ios/app/host_setup_view_controller.mm b/remoting/ios/app/host_setup_view_controller.mm
index 15c43e6..55c3250 100644
--- a/remoting/ios/app/host_setup_view_controller.mm
+++ b/remoting/ios/app/host_setup_view_controller.mm
@@ -18,8 +18,7 @@
 #include "remoting/base/string_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
-// TODO(yuweih): Change to google.com/remotedesktop when ready.
-static NSString* const kInstallationLink = @"chrome.google.com/remotedesktop";
+static NSString* const kInstallationLink = @"remotedesktop.google.com/access";
 
 static NSString* const kHostSetupViewCellIdentifierItem =
     @"HostSetupViewCellIdentifier";
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 0fe27d9..45cd19a 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -799,8 +799,8 @@
         Welcome to Chrome Remote Desktop
       </message>
       <message desc="Step 1 to set up a computer to be remotely accessible." name="IDS_HOST_SETUP_STEP_1">On the computer you want to access remotely, open Chrome and visit <ph name="INSTALLATION_LINK">$1<ex>google.com/remotedesktop</ex></ph></message>
-      <message desc="Step 2 to set up a computer to be remotely accessible." name="IDS_HOST_SETUP_STEP_2">Install Chrome Remote Desktop software and follow the instructions to complete setup</message>
-      <message desc="Step 3 to set up a computer to be remotely accessible." name="IDS_HOST_SETUP_STEP_3">After setup is complete, you'll be able to access the computer from this page by entering the PIN or access code</message>
+      <message desc="Step 2 to set up a computer to be remotely accessible." name="IDS_HOST_SETUP_STEP_2">Follow the instructions to set up your computer for remote access</message>
+      <message desc="Step 3 to set up a computer to be remotely accessible." name="IDS_HOST_SETUP_STEP_3">After setup is complete, refresh this page, then you'll be able to access the computer by choosing your device and entering the PIN</message>
       <message desc="Label for a button for sending the user an email with link and instructions for setting up a computer for remote access." name="IDS_EMAIL_LINKS_AND_INSTRUCTIONS">
         Email these instructions
       </message>
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 0c59764..4472861 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -801,9 +801,11 @@
 }
 
 void NetworkContext::PreconnectSockets(uint32_t num_streams,
-                                       const GURL& url,
+                                       const GURL& original_url,
                                        int32_t load_flags,
                                        bool privacy_mode_enabled) {
+  GURL url = GetHSTSRedirect(original_url);
+
   // |PreconnectSockets| may receive arguments from the renderer, which is not
   // guaranteed to validate them.
   if (num_streams == 0)
@@ -1223,4 +1225,20 @@
   return result;
 }
 
+GURL NetworkContext::GetHSTSRedirect(const GURL& original_url) {
+  // TODO(lilyhoughton) This needs to be gotten rid of once explicit
+  // construction with a URLRequestContext is no longer supported.
+  if (!url_request_context_->transport_security_state() ||
+      !original_url.SchemeIs("http") ||
+      !url_request_context_->transport_security_state()->ShouldUpgradeToSSL(
+          original_url.host())) {
+    return original_url;
+  }
+
+  url::Replacements<char> replacements;
+  const char kNewScheme[] = "https";
+  replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme)));
+  return original_url.ReplaceComponents(replacements);
+}
+
 }  // namespace network
diff --git a/services/network/network_context.h b/services/network/network_context.h
index bbc506e..7ae758df 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -241,6 +241,8 @@
 
   URLRequestContextOwner MakeURLRequestContext();
 
+  GURL GetHSTSRedirect(const GURL& original_url);
+
   NetworkService* const network_service_;
 
   std::unique_ptr<ResourceScheduler> resource_scheduler_;
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 00ff468..dba4e4a 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -59,6 +59,7 @@
 #include "net/proxy_resolution/proxy_config.h"
 #include "net/proxy_resolution/proxy_info.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
+#include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/ssl/channel_id_store.h"
@@ -209,6 +210,39 @@
     return value;
   }
 
+  // Looks up a value with the given name from the NetworkContext's
+  // SSLSocketPool info dictionary.
+  int GetSSLSocketPoolInfo(NetworkContext* context, base::StringPiece name) {
+    int value;
+    context->url_request_context()
+        ->http_transaction_factory()
+        ->GetSession()
+        ->GetSSLSocketPool(
+            net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL)
+        ->GetInfoAsValue("", "", false)
+        ->GetInteger(name, &value);
+    return value;
+  }
+
+  int GetSocketCount(NetworkContext* network_context) {
+    return GetSocketPoolInfo(network_context, "idle_socket_count") +
+           GetSocketPoolInfo(network_context, "connecting_socket_count") +
+           GetSocketPoolInfo(network_context, "handed_out_socket_count");
+  }
+
+  int GetSSLSocketCount(NetworkContext* network_context) {
+    return GetSSLSocketPoolInfo(network_context, "idle_socket_count") +
+           GetSSLSocketPoolInfo(network_context, "connecting_socket_count") +
+           GetSSLSocketPoolInfo(network_context, "handed_out_socket_count");
+  }
+
+  GURL GetHttpUrlFromHttps(const GURL& https_url) {
+    url::Replacements<char> replacements;
+    const char http[] = "http";
+    replacements.SetScheme(http, url::Component(0, strlen(http)));
+    return https_url.ReplaceComponents(replacements);
+  }
+
  protected:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<NetworkService> network_service_;
@@ -2499,7 +2533,9 @@
     : public net::test_server::EmbeddedTestServerConnectionListener {
  public:
   ConnectionListener()
-      : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      : total_sockets_seen_(0),
+        total_sockets_waited_for_(0),
+        task_runner_(base::ThreadTaskRunnerHandle::Get()),
         num_accepted_connections_needed_(0),
         num_accepted_connections_loop_(nullptr) {}
 
@@ -2513,6 +2549,7 @@
     EXPECT_TRUE(sockets_.find(socket) == sockets_.end());
 
     sockets_[socket] = SOCKET_ACCEPTED;
+    total_sockets_seen_++;
     CheckAccepted();
   }
 
@@ -2529,7 +2566,7 @@
     base::RunLoop run_loop;
     {
       base::AutoLock lock(lock_);
-      EXPECT_GE(num_connections, sockets_.size());
+      EXPECT_GE(num_connections, sockets_.size() - total_sockets_waited_for_);
       num_accepted_connections_loop_ = &run_loop;
       num_accepted_connections_needed_ = num_connections;
       CheckAccepted();
@@ -2541,7 +2578,8 @@
     // Grab the mutex again and make sure that the number of accepted sockets is
     // indeed |num_connections|.
     base::AutoLock lock(lock_);
-    EXPECT_EQ(num_connections, sockets_.size());
+    total_sockets_waited_for_ += num_connections;
+    EXPECT_EQ(total_sockets_seen_, total_sockets_waited_for_);
   }
 
   // Helper function to stop the waiting for sockets to be accepted for
@@ -2555,7 +2593,8 @@
     DCHECK(num_accepted_connections_loop_ ||
            num_accepted_connections_needed_ == 0);
     if (!num_accepted_connections_loop_ ||
-        num_accepted_connections_needed_ != sockets_.size()) {
+        num_accepted_connections_needed_ !=
+            sockets_.size() - total_sockets_waited_for_) {
       return;
     }
 
@@ -2580,6 +2619,9 @@
     return address.port();
   }
 
+  int total_sockets_seen_;
+  int total_sockets_waited_for_;
+
   enum SocketStatus { SOCKET_ACCEPTED, SOCKET_READ_FROM };
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -2613,6 +2655,39 @@
   connection_listener.WaitForAcceptedConnections(1u);
 }
 
+TEST_F(NetworkContextTest, PreconnectHSTS) {
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(CreateContextParams());
+
+  ConnectionListener connection_listener;
+  net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetConnectionListener(&connection_listener);
+  ASSERT_TRUE(test_server.Start());
+
+  const GURL server_http_url = GetHttpUrlFromHttps(test_server.base_url());
+  network_context->PreconnectSockets(1, server_http_url, net::LOAD_NORMAL,
+                                     true);
+  connection_listener.WaitForAcceptedConnections(1u);
+
+  int num_sockets = GetSocketCount(network_context.get());
+  EXPECT_EQ(num_sockets, 1);
+  int num_ssl_sockets = GetSSLSocketCount(network_context.get());
+  EXPECT_EQ(num_ssl_sockets, 0);
+
+  const base::Time expiry =
+      base::Time::Now() + base::TimeDelta::FromSeconds(1000);
+  network_context->url_request_context()->transport_security_state()->AddHSTS(
+      server_http_url.host(), expiry, false);
+  network_context->PreconnectSockets(1, server_http_url, net::LOAD_NORMAL,
+                                     true);
+  connection_listener.WaitForAcceptedConnections(1u);
+
+  num_sockets = GetSocketCount(network_context.get());
+  EXPECT_EQ(num_sockets, 2);
+  num_ssl_sockets = GetSSLSocketCount(network_context.get());
+  EXPECT_EQ(num_ssl_sockets, 1);
+}
+
 TEST_F(NetworkContextTest, PreconnectZero) {
   std::unique_ptr<NetworkContext> network_context =
       CreateContextWithParams(CreateContextParams());
diff --git a/services/network/public/cpp/simple_url_loader_unittest.cc b/services/network/public/cpp/simple_url_loader_unittest.cc
index 8eb6f97..804b2df8 100644
--- a/services/network/public/cpp/simple_url_loader_unittest.cc
+++ b/services/network/public/cpp/simple_url_loader_unittest.cc
@@ -758,6 +758,13 @@
              base::RunLoop* run_loop, const net::RedirectInfo& redirect_info,
              const network::ResourceResponseHead& response_head,
              std::vector<std::string>* to_be_removed_headers) {
+            test_helper.reset();
+            // Access the parameters to trigger a memory error if they have been
+            // deleted. (ASAN build should catch it)
+            EXPECT_FALSE(response_head.request_start.is_null());
+            EXPECT_FALSE(redirect_info.new_url.is_empty());
+            EXPECT_NE(to_be_removed_headers, nullptr);
+
             run_loop->Quit();
           },
           base::Passed(std::move(test_helper)), &run_loop));
diff --git a/services/network/public/mojom/ssl_config.mojom b/services/network/public/mojom/ssl_config.mojom
index b3b11b34..dcbafa6d 100644
--- a/services/network/public/mojom/ssl_config.mojom
+++ b/services/network/public/mojom/ssl_config.mojom
@@ -38,6 +38,14 @@
   // Ex: To disable TLS_RSA_WITH_RC4_128_MD5, specify 0x0004, while to
   // disable TLS_ECDH_ECDSA_WITH_RC4_128_SHA, specify 0xC002.
   array<uint16> disabled_cipher_suites;
+
+  // Patterns for matching hostnames to determine when to allow connection
+  // coalescing when client certificates are also in use. Patterns follow
+  // the rules for host matching from the URL Blacklist filter format:
+  // "example.com" matches "example.com" and all subdomains, while
+  // ".example.net" matches exactly "example.net". Hostnames must be
+  // canonicalized according to the rules used by GURL.
+  array<string> client_cert_pooling_policy;
 };
 
 // Receives SSL configuration updates.
diff --git a/services/network/ssl_config_service_mojo.cc b/services/network/ssl_config_service_mojo.cc
index dd54ad6..df77ad0 100644
--- a/services/network/ssl_config_service_mojo.cc
+++ b/services/network/ssl_config_service_mojo.cc
@@ -4,18 +4,42 @@
 
 #include "services/network/ssl_config_service_mojo.h"
 
+#include "base/strings/string_piece.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/network/ssl_config_type_converter.h"
 
 namespace network {
 
+namespace {
+
+// Returns true if |hostname| is a subdomain of |pattern| (including if they are
+// equal).
+bool IsSubdomain(const base::StringPiece hostname,
+                 const base::StringPiece pattern) {
+  if (hostname == pattern) {
+    return true;
+  }
+  if (hostname.length() <= (pattern.length() + 1)) {
+    return false;
+  }
+  if (!hostname.ends_with(pattern)) {
+    return false;
+  }
+  return hostname[hostname.length() - pattern.length() - 1] == '.';
+}
+
+}  // namespace
+
 SSLConfigServiceMojo::SSLConfigServiceMojo(
     mojom::SSLConfigPtr initial_config,
     mojom::SSLConfigClientRequest ssl_config_client_request)
     : binding_(this),
       ssl_config_(initial_config ? mojo::ConvertTo<net::SSLConfig>(
                                        std::move(initial_config))
-                                 : net::SSLConfig()) {
+                                 : net::SSLConfig()),
+      client_cert_pooling_policy_(
+          initial_config ? initial_config->client_cert_pooling_policy
+                         : std::vector<std::string>()) {
   if (ssl_config_client_request)
     binding_.Bind(std::move(ssl_config_client_request));
 }
@@ -25,11 +49,41 @@
 void SSLConfigServiceMojo::OnSSLConfigUpdated(mojom::SSLConfigPtr ssl_config) {
   net::SSLConfig old_config = ssl_config_;
   ssl_config_ = mojo::ConvertTo<net::SSLConfig>(std::move(ssl_config));
-  ProcessConfigUpdate(old_config, ssl_config_);
+  bool force_notification =
+      client_cert_pooling_policy_ != ssl_config->client_cert_pooling_policy;
+  client_cert_pooling_policy_ = ssl_config->client_cert_pooling_policy;
+  ProcessConfigUpdate(old_config, ssl_config_, force_notification);
 }
 
 void SSLConfigServiceMojo::GetSSLConfig(net::SSLConfig* ssl_config) {
   *ssl_config = ssl_config_;
 }
 
+bool SSLConfigServiceMojo::CanShareConnectionWithClientCerts(
+    const std::string& hostname) const {
+  // Hostnames (and the patterns configured for this class) must be
+  // canonicalized before comparison, or the comparison will fail.
+  for (const std::string& pattern : client_cert_pooling_policy_) {
+    if (pattern.empty()) {
+      continue;
+    }
+    // If the pattern starts with a '.', |hostname| must match it exactly
+    // (except for the leading dot) for the pattern to be a match.
+    if (pattern[0] == '.') {
+      if (pattern.compare(1, std::string::npos, hostname) == 0) {
+        return true;
+      } else {
+        continue;
+      }
+    }
+    // Patterns that don't start with a dot match subdomains as well as an exact
+    // pattern match. For example, a pattern of "example.com" should match a
+    // hostname of "example.com", "sub.example.com", but not "notexample.com".
+    if (IsSubdomain(hostname, pattern)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace network
diff --git a/services/network/ssl_config_service_mojo.h b/services/network/ssl_config_service_mojo.h
index aa38ebe..28bd7dc 100644
--- a/services/network/ssl_config_service_mojo.h
+++ b/services/network/ssl_config_service_mojo.h
@@ -28,14 +28,21 @@
   // mojom::SSLConfigClient implementation:
   void OnSSLConfigUpdated(const mojom::SSLConfigPtr ssl_config) override;
 
-  // net::SSLConfigClient implementation:
+  // net::SSLConfigService implementation:
   void GetSSLConfig(net::SSLConfig* ssl_config) override;
+  bool CanShareConnectionWithClientCerts(
+      const std::string& hostname) const override;
 
  private:
   mojo::Binding<mojom::SSLConfigClient> binding_;
 
   net::SSLConfig ssl_config_;
 
+  // The list of domains and subdomains from enterprise policy where connection
+  // coalescing is allowed when client certs are in use if the hosts being
+  // coalesced match this list.
+  std::vector<std::string> client_cert_pooling_policy_;
+
   DISALLOW_COPY_AND_ASSIGN(SSLConfigServiceMojo);
 };
 
diff --git a/services/network/ssl_config_service_mojo_unittest.cc b/services/network/ssl_config_service_mojo_unittest.cc
index df853df0..1bd8f08 100644
--- a/services/network/ssl_config_service_mojo_unittest.cc
+++ b/services/network/ssl_config_service_mojo_unittest.cc
@@ -295,5 +295,48 @@
   RunConversionTests(*mojo_config, expected_net_config);
 }
 
+TEST_F(NetworkServiceSSLConfigServiceTest, CanShareConnectionWithClientCerts) {
+  // Create an SSLConfigServiceMojo and test that
+  // CanShareConnectionWithClientCerts returns false.
+  mojom::SSLConfigClientRequest client_request =
+      mojo::MakeRequest(&ssl_config_client_);
+  SSLConfigServiceMojo config_service(mojom::SSLConfig::New(),
+                                      std::move(client_request));
+
+  EXPECT_FALSE(config_service.CanShareConnectionWithClientCerts("example.com"));
+  EXPECT_FALSE(config_service.CanShareConnectionWithClientCerts("example.net"));
+
+  // Configure policy to allow example.com (but no subdomains), and example.net
+  // (including subdomains), update the config, and test that pooling is allowed
+  // with this policy.
+  mojom::SSLConfigPtr mojo_config = mojom::SSLConfig::New();
+  mojo_config->client_cert_pooling_policy = {".example.com", "example.net"};
+  TestSSLConfigServiceObserver observer(&config_service);
+  ssl_config_client_->OnSSLConfigUpdated(std::move(mojo_config));
+  observer.WaitForChange();
+
+  EXPECT_TRUE(config_service.CanShareConnectionWithClientCerts("example.com"));
+  EXPECT_FALSE(
+      config_service.CanShareConnectionWithClientCerts("sub.example.com"));
+
+  EXPECT_TRUE(config_service.CanShareConnectionWithClientCerts("example.net"));
+  EXPECT_TRUE(
+      config_service.CanShareConnectionWithClientCerts("sub.example.net"));
+  EXPECT_TRUE(
+      config_service.CanShareConnectionWithClientCerts("sub.sub.example.net"));
+  EXPECT_FALSE(
+      config_service.CanShareConnectionWithClientCerts("notexample.net"));
+
+  EXPECT_FALSE(config_service.CanShareConnectionWithClientCerts("example.org"));
+
+  // Reset the configuration to the default and check that pooling is no longer
+  // allowed.
+  ssl_config_client_->OnSSLConfigUpdated(mojom::SSLConfig::New());
+  observer.WaitForChange();
+
+  EXPECT_FALSE(config_service.CanShareConnectionWithClientCerts("example.com"));
+  EXPECT_FALSE(config_service.CanShareConnectionWithClientCerts("example.net"));
+}
+
 }  // namespace
 }  // namespace network
diff --git a/services/preferences/tracked/tracked_preferences_migration.cc b/services/preferences/tracked/tracked_preferences_migration.cc
index 9bce8035..92e67b8 100644
--- a/services/preferences/tracked/tracked_preferences_migration.cc
+++ b/services/preferences/tracked/tracked_preferences_migration.cc
@@ -23,6 +23,8 @@
 class TrackedPreferencesMigrator
     : public base::RefCounted<TrackedPreferencesMigrator> {
  public:
+  enum PrefFilterID { UNPROTECTED_PREF_FILTER, PROTECTED_PREF_FILTER };
+
   TrackedPreferencesMigrator(
       const std::set<std::string>& unprotected_pref_names,
       const std::set<std::string>& protected_pref_names,
@@ -39,13 +41,6 @@
       InterceptablePrefFilter* unprotected_pref_filter,
       InterceptablePrefFilter* protected_pref_filter);
 
- private:
-  friend class base::RefCounted<TrackedPreferencesMigrator>;
-
-  enum PrefFilterID { UNPROTECTED_PREF_FILTER, PROTECTED_PREF_FILTER };
-
-  ~TrackedPreferencesMigrator();
-
   // Stores the data coming in from the filter identified by |id| into this
   // class and then calls MigrateIfReady();
   void InterceptFilterOnLoad(
@@ -54,6 +49,11 @@
           finalize_filter_on_load,
       std::unique_ptr<base::DictionaryValue> prefs);
 
+ private:
+  friend class base::RefCounted<TrackedPreferencesMigrator>;
+
+  ~TrackedPreferencesMigrator();
+
   // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_|
   // have been set.
   void MigrateIfReady();
@@ -215,14 +215,6 @@
           register_on_successful_protected_store_write_callback),
       unprotected_pref_hash_store_(std::move(unprotected_pref_hash_store)),
       protected_pref_hash_store_(std::move(protected_pref_hash_store)) {
-  // The callbacks bound below will own this TrackedPreferencesMigrator by
-  // reference.
-  unprotected_pref_filter->InterceptNextFilterOnLoad(
-      base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad, this,
-                 UNPROTECTED_PREF_FILTER));
-  protected_pref_filter->InterceptNextFilterOnLoad(
-      base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad, this,
-                 PROTECTED_PREF_FILTER));
 }
 
 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
@@ -322,13 +314,21 @@
     std::unique_ptr<PrefHashStore> protected_pref_hash_store,
     InterceptablePrefFilter* unprotected_pref_filter,
     InterceptablePrefFilter* protected_pref_filter) {
-  scoped_refptr<TrackedPreferencesMigrator> prefs_migrator(
-      new TrackedPreferencesMigrator(
-          unprotected_pref_names, protected_pref_names,
-          unprotected_store_cleaner, protected_store_cleaner,
-          register_on_successful_unprotected_store_write_callback,
-          register_on_successful_protected_store_write_callback,
-          std::move(unprotected_pref_hash_store),
-          std::move(protected_pref_hash_store), unprotected_pref_filter,
-          protected_pref_filter));
+  auto prefs_migrator = base::MakeRefCounted<TrackedPreferencesMigrator>(
+      unprotected_pref_names, protected_pref_names, unprotected_store_cleaner,
+      protected_store_cleaner,
+      register_on_successful_unprotected_store_write_callback,
+      register_on_successful_protected_store_write_callback,
+      std::move(unprotected_pref_hash_store),
+      std::move(protected_pref_hash_store), unprotected_pref_filter,
+      protected_pref_filter);
+
+  // The callbacks bound below will own this TrackedPreferencesMigrator by
+  // reference.
+  unprotected_pref_filter->InterceptNextFilterOnLoad(base::Bind(
+      &TrackedPreferencesMigrator::InterceptFilterOnLoad, prefs_migrator,
+      TrackedPreferencesMigrator::UNPROTECTED_PREF_FILTER));
+  protected_pref_filter->InterceptNextFilterOnLoad(base::Bind(
+      &TrackedPreferencesMigrator::InterceptFilterOnLoad, prefs_migrator,
+      TrackedPreferencesMigrator::PROTECTED_PREF_FILTER));
 }
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 5b31cf46..453133c2 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -116,7 +116,7 @@
 
 # Internal-facing config for Skia library code.
 config("skia_library_config") {
-  defines = [ "SK_IGNORE_EMOJI_SUBPIXEL_FIX" ]
+  defines = []
 
   # These include directories are only included for Skia code and are not
   # exported to dependents.
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 51d8cc37..1557c419 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -16769,13 +16769,6 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "hidpi": "0",
-              "os": "Mac-10.12.6"
-            }
-          ],
           "shards": 12
         }
       },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 7400c4cf..c6b0b26 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -871,21 +871,6 @@
         }
       },
       {
-        "isolate_name": "webkit_layout_tests_exparchive",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "webkit_layout_tests",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 12
-        }
-      },
-      {
         "isolate_name": "webkit_python_tests",
         "name": "webkit_python_tests",
         "swarming": {
@@ -4820,6 +4805,836 @@
       }
     ]
   },
+  "linux-tcmalloc-rel": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "battor_agent_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_fuzzer_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 15
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor"
+        ],
+        "name": "viz_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_components_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor"
+        ],
+        "name": "viz_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor"
+        ],
+        "name": "viz_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cronet_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cronet_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_extensions_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "filesystem_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "headless_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "headless_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "leveldb_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "message_center_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_core_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "nacl_helper_nonsfi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "shell_dialogs_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "snapshot_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_sync_integration_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "traffic_annotation_auditor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "vr_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "vr_pixeltests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wm_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--test-type=integration"
+        ],
+        "isolate_name": "chromedriver_py_tests",
+        "name": "chromedriver_py_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "content_shell_crash_test",
+        "name": "content_shell_crash_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "devtools_closure_compile",
+        "name": "devtools_closure_compile",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "devtools_eslint",
+        "name": "devtools_eslint",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "metrics_python_tests",
+        "name": "metrics_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "args": [
+          "--additional-driver-flag",
+          "--site-per-process",
+          "--additional-driver-flag",
+          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
+          "--additional-expectations",
+          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "site_per_process_webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 10
+        }
+      },
+      {
+        "isolate_name": "telemetry_gpu_unittests",
+        "name": "telemetry_gpu_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "telemetry_perf_unittests",
+        "name": "telemetry_perf_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 960,
+          "shards": 12
+        }
+      },
+      {
+        "args": [
+          "--jobs=1"
+        ],
+        "isolate_name": "telemetry_unittests",
+        "name": "telemetry_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 4
+        }
+      },
+      {
+        "isolate_name": "views_perftests",
+        "name": "views_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 12
+        }
+      },
+      {
+        "isolate_name": "webkit_python_tests",
+        "name": "webkit_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      }
+    ],
+    "scripts": [
+      {
+        "name": "checkdeps",
+        "script": "checkdeps.py"
+      },
+      {
+        "name": "checkperms",
+        "script": "checkperms.py"
+      },
+      {
+        "name": "webkit_lint",
+        "script": "webkit_lint.py"
+      }
+    ]
+  },
   "mac-views-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 637de4dd..d3edce23 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -2282,7 +2282,6 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "hidpi": "0",
               "os": "Mac-10.12.6"
             }
           ],
@@ -2861,6 +2860,12 @@
         }
       },
       {
+        "alternate_swarming_dimensions": [
+          {
+            "gpu": "8086:0a2e",
+            "os": "Mac-10.12.6"
+          }
+        ],
         "isolate_name": "webkit_layout_tests_exparchive",
         "merge": {
           "args": [
@@ -2875,12 +2880,12 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "hidpi": "0",
-              "os": "Mac-10.12.6"
+              "os": "Mac-10.13"
             }
           ],
           "shards": 12
-        }
+        },
+        "use_multi_dimension_trigger_script": true
       },
       {
         "isolate_name": "webkit_python_tests",
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index c67b47a..141982e 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -36,12 +36,9 @@
 
 # The browser frame is a work in progress.
 -BrowserNonClientFrameViewAshBackButtonTest.*
--BrowserNonClientFrameViewAshTest.ActiveStateOfButtonMatchesWidget/*
 -BrowserNonClientFrameViewAshTest.AvatarDisplayOnTeleportedWindow/*
 -BrowserNonClientFrameViewAshTest.HeaderHeightForSnappedBrowserInSplitView/*
 -BrowserNonClientFrameViewAshTest.ImmersiveModeTopViewInset/*
--BrowserNonClientFrameViewAshTest.IncognitoAvatar/*
--BrowserNonClientFrameViewAshTest.IncognitoMarkedAsAssistantBlocked/*
 -BrowserNonClientFrameViewAshTest.RestoreMinimizedBrowserUpdatesCaption/*
 -BrowserNonClientFrameViewAshTest.ToggleTabletModeOnMinimizedWindow/*
 -BrowserNonClientFrameViewAshTest.ToggleTabletModeRelayout/*
@@ -307,4 +304,8 @@
 -ShelfAppBrowserTestNoDefaultBrowser.AltNumberBrowserTabbing
 -ShelfAppBrowserTestNoDefaultBrowser.BrowserShortcutLauncherItemController
 
+# Excluded from Mash because pointer events from EventGenerator aren't seen.
+# https://crbug.com/814675
+-ChromeOSInfoPrivateTest.StylusSeen
+
 # See comment at top of file regarding adding test exclusions.
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 29bde5e..c6f0ba6 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -749,11 +749,11 @@
       'WebKit Linux root_layer_scrolls Dummy Builder',
       'WebKit Linux slimming_paint_v2 Dummy Builder',
       #chromium
-      'android-rel',
-      'linux-rel',
-      'mac-rel',
-      'win32-rel',
-      'win-rel',
+      'Android',
+      'Linux x64',
+      'Mac',
+      'Win',
+      'Win x64',
     ]
 
   def check_input_file_consistency(self):
@@ -822,6 +822,8 @@
       for removal in removals:
         if removal not in all_bots:
           missing_bots.add(removal)
+
+    missing_bots = missing_bots - set(bots_that_dont_exist)
     if missing_bots:
       raise BBGenErr('The following nonexistent machines were referenced in '
                      'the test suite exceptions: ' + str(missing_bots))
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index e6f1dd2..9ef7054 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1543,6 +1543,8 @@
   },
   'webkit_layout_tests': {
     'remove_from': [
+      # chromium.fyi
+      'Chromium Mac 10.13',
       # chromium.linux
       'Linux Tests (dbg)(1)(32)', # 32-bit linux is unsupported
       # chromium.win
@@ -1690,13 +1692,11 @@
       },
       'Mac10.12 Tests': {
         # TODO(kbr): if the Swarming dimensions were explicitly specified for
-        # all jobs then this wouldn't be needed. However, note that this bot
-        # implicitly specifies gpu:none rather than gpu:8086:0a2e.
+        # all jobs then this wouldn't be needed.
         'swarming': {
           'dimension_sets': [
             {
               'gpu': '8086:0a2e',
-              'hidpi': '0',
               'os': 'Mac-10.12.6',
             },
           ],
@@ -1704,17 +1704,20 @@
         },
       },
       'Mac10.13 Tests': {
-        # TODO(jbudorick,dpranke): Switch this to 10.13.
+        # TODO(crbug.com/853356): Switch this to 10.13.
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [{
+          'gpu': '8086:0a2e',
+          'os': 'Mac-10.12.6',
+        }],
 
         # TODO(kbr): if the Swarming dimensions were explicitly specified for
-        # all jobs then this wouldn't be needed. However, note that this bot
-        # implicitly specifies gpu:none rather than gpu:8086:0a2e.
+        # all jobs then this wouldn't be needed.
         'swarming': {
           'dimension_sets': [
             {
               'gpu': '8086:0a2e',
-              'hidpi': '0',
-              'os': 'Mac-10.12.6',
+              'os': 'Mac-10.13',
             },
           ],
           'shards': 12,
@@ -1725,21 +1728,6 @@
           '--debug',
         ],
       },
-      # chromium.clang
-      'ToTMac': {
-        # TODO(thakis): Remove this once Mac10.13 Tests no longer runs tests on 10.12.
-        'swarming': {
-          'dimension_sets': [
-            {
-              'gpu': '8086:0a2e',
-              'hidpi': '0',
-              'os': 'Mac-10.12.6',
-            },
-          ],
-          'shards': 12,
-        },
-      },
-
       # chromium.webkit.
       'WebKit Linux Trusty ASAN': {
         'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index a82ac762..5882eeb 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2550,6 +2550,13 @@
           'gtest_tests': 'vr_platform_specific_chromium_gtests',
         },
       },
+      'linux-tcmalloc-rel': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_gtests',
+          'isolated_scripts': 'chromium_linux_rel_isolated_scripts',
+          'scripts': 'chromium_linux_scripts',
+        },
+      },
       'WebKit Linux layout_ng Dummy Builder': {
         'swarming': {
           'dimension_sets': [
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 620c5c9..8833deb 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -406,6 +406,7 @@
 ### virtual/gpu/fast/canvas/ blending layout tests are slow
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blend-image.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-color-over-image.html [ Slow ]
+crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-image.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-gradient-over-pattern.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-image-over-gradient.html [ Slow ]
@@ -415,6 +416,7 @@
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-pattern-over-gradient.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-pattern-over-image.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-blending-pattern-over-pattern.html [ Slow ]
+crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/canvas-shadow-source-in.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/color-space/canvas-createImageBitmap-p3.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html [ Slow ]
 crbug.com/866850 [ Linux Mac ] virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 870cbfc..2f2ed65 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2227,29 +2227,15 @@
 crbug.com/613672 [ Mac ] external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/video-surface-layer/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/unified-autoplay/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/video-surface-layer/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/unified-autoplay/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/touch/multi-touch-user-gesture.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/threaded/fast/events/pointerevents/pinch/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html [ Skip ]
 # In addition to having no support on Mac, the following test times out
 # regularly on Windows. crbug.com/850964
 crbug.com/613672 [ Mac Win ] virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/pointerevents/multi-touch-events.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/pointerevents/multi-touch-events.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/multi-touch-events.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scroll.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
@@ -4877,3 +4863,6 @@
 # Sheriff 2018-07-30
 crbug.com/866166 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass Timeout ]
 crbug.com/868706 external/wpt/css/css-layout-api/auto-block-size-inflow.https.html [ Pass Failure ]
+
+# Sheriff 2018-07-31
+crbug.com/869470 external/wpt/background-fetch/get.https.window.html [ Crash Failure ]
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 5daa56a..d9351e2 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -125329,6 +125329,11 @@
      {}
     ]
    ],
+   "css/css-pseudo/interfaces-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-pseudo/marker-and-other-pseudo-elements-ref.html": [
     [
      {}
@@ -158874,6 +158879,11 @@
      {}
     ]
    ],
+   "interfaces/css-pseudo.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/css-regions.idl": [
     [
      {}
@@ -192631,6 +192641,12 @@
      {}
     ]
    ],
+   "css/css-pseudo/interfaces.html": [
+    [
+     "/css/css-pseudo/interfaces.html",
+     {}
+    ]
+   ],
    "css/css-rhythm/line-height-step-dynamic-001.html": [
     [
      "/css/css-rhythm/line-height-step-dynamic-001.html",
@@ -318308,6 +318324,14 @@
    "829ee2c50f5b6198f9cc0b3ae01b208d20ed7702",
    "reftest"
   ],
+  "css/css-pseudo/interfaces-expected.txt": [
+   "0c201030d2470cdf4bd0625123bc03b52c4aa9ef",
+   "support"
+  ],
+  "css/css-pseudo/interfaces.html": [
+   "9317f150deae389549c0626af8224ed3710ae554",
+   "testharness"
+  ],
   "css/css-pseudo/marker-and-other-pseudo-elements-ref.html": [
    "73ab862dcf3131ae7d7166ef06e52db0cd0eb7c7",
    "support"
@@ -342693,11 +342717,11 @@
    "support"
   ],
   "css/cssom-view/interfaces-expected.txt": [
-   "3bac99fc1c209be756dbb3d7bef14c4b37bfd155",
+   "b80976c7a4169a0339e51b34d9c95a570dde6c8f",
    "support"
   ],
   "css/cssom-view/interfaces.html": [
-   "75f26269a0b198f28bf0f659ada6ef6d104275a0",
+   "32883b8919a074d6c301953f3a6a2a7d5d69976a",
    "testharness"
   ],
   "css/cssom-view/matchMedia.xht": [
@@ -378704,6 +378728,10 @@
    "12544faf5051cd77fdd9d74a81a89db9cdc63a6d",
    "support"
   ],
+  "interfaces/css-pseudo.idl": [
+   "cbe6954dd91e8a7a579840d14b8cc7695592c592",
+   "support"
+  ],
   "interfaces/css-regions.idl": [
    "23d01fa1c91539c443b2227df6a89e377b65a393",
    "support"
@@ -378717,7 +378745,7 @@
    "support"
   ],
   "interfaces/cssom-view.idl": [
-   "3f575def818098d376ddf069673692530fcf8896",
+   "f9407f8700beea9d5ab07fbb42f63f97e39942b9",
    "support"
   ],
   "interfaces/cssom.idl": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces-expected.txt
new file mode 100644
index 0000000..c15a25c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces-expected.txt
@@ -0,0 +1,32 @@
+This is a testharness.js-based test.
+PASS CSS Pseudo-Elements interfaces
+PASS Partial interface Window: original interface defined
+FAIL CSSPseudoElement interface: existence and properties of interface object assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface object length assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface object name assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+FAIL CSSPseudoElement interface: attribute type assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for type property on CSSPseudoElement
+FAIL CSSPseudoElement interface: attribute style assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for style property on CSSPseudoElement
+FAIL CSSPseudoElementList interface: existence and properties of interface object assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface object length assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface object name assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+FAIL CSSPseudoElementList interface: attribute length assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+PASS Unscopable handled correctly for length property on CSSPseudoElementList
+FAIL CSSPseudoElementList interface: operation item(unsigned long) assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+PASS Unscopable handled correctly for item(unsigned long) on CSSPseudoElementList
+FAIL CSSPseudoElementList interface: operation getByType(CSSOMString) assert_own_property: self does not have own property "CSSPseudoElementList" expected property "CSSPseudoElementList" missing
+PASS Unscopable handled correctly for getByType(CSSOMString) on CSSPseudoElementList
+FAIL Window interface: operation getPseudoElements(Element, CSSOMString) assert_own_property: global object missing non-static operation expected property "getPseudoElements" missing
+PASS Unscopable handled correctly for getPseudoElements(Element, CSSOMString) on Window
+FAIL Window interface: window must inherit property "getPseudoElements(Element, CSSOMString)" with the proper type assert_own_property: expected property "getPseudoElements" missing
+FAIL Window interface: calling getPseudoElements(Element, CSSOMString) on window with too few arguments must throw TypeError assert_own_property: expected property "getPseudoElements" missing
+PASS WorkerGlobalScope interface: existence and properties of interface object
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces.html
new file mode 100644
index 0000000..572f87f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-pseudo/interfaces.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4"/>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+
+<h1>CSS Pseudo-Elements IDL tests</h1>
+<div id=log></div>
+
+<script>
+'use strict';
+
+idl_test(
+  ['css-pseudo'],
+  ['cssom', 'html', 'dom'],
+  async idlArray => {
+    idlArray.add_objects({
+      Window: ['window'],
+      // TODO: CSSPseudoElement + CSSPseudoElementList
+    });
+  },
+  'CSS Pseudo-Elements interfaces'
+);
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces-expected.txt
index b11003b..c5dafbce 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces-expected.txt
@@ -1,12 +1,14 @@
 This is a testharness.js-based test.
-Found 387 tests; 312 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Test driver
+Found 417 tests; 338 PASS, 79 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS cssom-view interfaces
 PASS Partial interface Window: original interface defined
 PASS Partial interface Document: original interface defined
 PASS Partial interface Element: original interface defined
 PASS Partial interface HTMLElement: original interface defined
 PASS Partial interface HTMLImageElement: original interface defined
 PASS Partial interface Range: original interface defined
+PASS Partial interface MouseEvent: original interface defined
+PASS Partial dictionary MouseEventInit: original dictionary defined
 PASS MediaQueryList interface: existence and properties of interface object
 PASS MediaQueryList interface object length
 PASS MediaQueryList interface object name
@@ -89,6 +91,34 @@
 FAIL CaretPosition interface: document.caretPositionFromPoint(5, 5) must inherit property "offsetNode" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: document.caretPositionFromPoint is not a function"
 FAIL CaretPosition interface: document.caretPositionFromPoint(5, 5) must inherit property "offset" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: document.caretPositionFromPoint is not a function"
 FAIL CaretPosition interface: document.caretPositionFromPoint(5, 5) must inherit property "getClientRect()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: document.caretPositionFromPoint is not a function"
+FAIL CSSPseudoElement interface: operation getBoxQuads(BoxQuadOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for getBoxQuads(BoxQuadOptions) on CSSPseudoElement
+FAIL CSSPseudoElement interface: operation convertQuadFromNode(DOMQuadInit, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for convertQuadFromNode(DOMQuadInit, GeometryNode, ConvertCoordinateOptions) on CSSPseudoElement
+FAIL CSSPseudoElement interface: operation convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions) on CSSPseudoElement
+FAIL CSSPseudoElement interface: operation convertPointFromNode(DOMPointInit, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS Unscopable handled correctly for convertPointFromNode(DOMPointInit, GeometryNode, ConvertCoordinateOptions) on CSSPseudoElement
+PASS MouseEvent interface: attribute screenX
+PASS Unscopable handled correctly for screenX property on MouseEvent
+PASS MouseEvent interface: attribute screenY
+PASS Unscopable handled correctly for screenY property on MouseEvent
+PASS MouseEvent interface: attribute pageX
+PASS Unscopable handled correctly for pageX property on MouseEvent
+PASS MouseEvent interface: attribute pageY
+PASS Unscopable handled correctly for pageY property on MouseEvent
+PASS MouseEvent interface: attribute clientX
+PASS Unscopable handled correctly for clientX property on MouseEvent
+PASS MouseEvent interface: attribute clientY
+PASS Unscopable handled correctly for clientY property on MouseEvent
+PASS MouseEvent interface: attribute x
+PASS Unscopable handled correctly for x property on MouseEvent
+PASS MouseEvent interface: attribute y
+PASS Unscopable handled correctly for y property on MouseEvent
+PASS MouseEvent interface: attribute offsetX
+PASS Unscopable handled correctly for offsetX property on MouseEvent
+PASS MouseEvent interface: attribute offsetY
+PASS Unscopable handled correctly for offsetY property on MouseEvent
 PASS HTMLElement interface: attribute offsetParent
 PASS Unscopable handled correctly for offsetParent property on HTMLElement
 PASS HTMLElement interface: attribute offsetTop
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces.html
index c73663e..c545e32c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/interfaces.html
@@ -21,7 +21,7 @@
 
 idl_test(
   ['cssom-view'],
-  ['html', 'dom', 'uievents', 'cssom'],
+  ['css-pseudo', 'cssom', 'uievents', 'SVG', 'html', 'dom'],
   async idlArray => {
     idlArray.add_objects({
       Window: ['window'],
@@ -41,6 +41,6 @@
 
     await waitForLoad;
   },
-  'Test driver'
+  'cssom-view interfaces'
 );
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/css-pseudo.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/css-pseudo.idl
new file mode 100644
index 0000000..957c019
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/css-pseudo.idl
@@ -0,0 +1,26 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content of this file was automatically extracted from the
+// "CSS Pseudo-Elements Module Level 4" spec.
+// See: https://drafts.csswg.org/css-pseudo-4/
+
+[Exposed=Window]
+interface CSSPseudoElement {
+    readonly attribute CSSOMString type;
+    readonly attribute CSSStyleDeclaration style;
+};
+
+CSSPseudoElement implements EventTarget;
+
+[Exposed=Window]
+interface CSSPseudoElementList {
+    readonly attribute unsigned long length;
+    CSSPseudoElement item(unsigned long index);
+    CSSPseudoElement getByType(CSSOMString type);
+                     // replies null if no pseudo-element exists for
+                     //     the requested type
+};
+
+partial interface Window {
+  CSSPseudoElementList getPseudoElements(Element elt,
+                                       CSSOMString type);
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/cssom-view.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/cssom-view.idl
index 4a70cb99..62be504 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/cssom-view.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/cssom-view.idl
@@ -1,3 +1,8 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content of this file was automatically extracted from the
+// "CSSOM View Module" spec.
+// See: https://drafts.csswg.org/cssom-view/
+
 enum ScrollBehavior { "auto", "instant", "smooth" };
 
 dictionary ScrollOptions {
@@ -131,7 +136,6 @@
   [NewObject] DOMRect getBoundingClientRect();
 };
 
-/* TODO This is commented out because: "Partial interface MouseEvent with no original interface"
 partial interface MouseEvent {
   readonly attribute double screenX;
   readonly attribute double screenY;
@@ -151,7 +155,6 @@
   double clientX = 0.0;
   double clientY = 0.0;
 };
-*/
 
 enum CSSBoxType { "margin", "border", "padding", "content" };
 dictionary BoxQuadOptions {
@@ -164,20 +167,16 @@
   CSSBoxType toBox = "border";
 };
 
-[Exposed=Window,
- NoInterfaceObject]
-interface GeometryUtils {
+interface mixin GeometryUtils {
   sequence<DOMQuad> getBoxQuads(optional BoxQuadOptions options);
   DOMQuad convertQuadFromNode(DOMQuadInit quad, GeometryNode from, optional ConvertCoordinateOptions options);
   DOMQuad convertRectFromNode(DOMRectReadOnly rect, GeometryNode from, optional ConvertCoordinateOptions options);
   DOMPoint convertPointFromNode(DOMPointInit point, GeometryNode from, optional ConvertCoordinateOptions options); // XXX z,w turns into 0
 };
 
-Text implements GeometryUtils; // like Range
-Element implements GeometryUtils;
-/* TODO Commented out because: "CSSPseudoElement implements GeometryUtils, but CSSPseudoElement is undefined."
-CSSPseudoElement implements GeometryUtils;
-*/
-Document implements GeometryUtils;
+Text includes GeometryUtils; // like Range
+Element includes GeometryUtils;
+CSSPseudoElement includes GeometryUtils;
+Document includes GeometryUtils;
 
 typedef (Text or Element or CSSPseudoElement or Document) GeometryNode;
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/color-emoji-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/color-emoji-expected.png
index 7be7fb6..9fa1b0e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/color-emoji-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/emoticons-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/emoticons-expected.png
index c520b7f..cbc4cf8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/emoticons-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/emoticons-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/fallback-traits-fixup-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/fallback-traits-fixup-expected.png
index a5b3d518..6936bee 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/fallback-traits-fixup-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/fallback-traits-fixup-expected.png
Binary files differ
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
index 21f24d3..7c659df 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -711,20 +711,20 @@
   return Response::OK();
 }
 
+// Returns the breakpoint url if a match is found, or WTF::String().
+String InspectorDOMDebuggerAgent::MatchXHRBreakpoints(const String& url) const {
+  if (pause_on_all_xhrs_.Get())
+    return "";
+  for (const WTF::String& breakpoint : xhr_breakpoints_.Keys()) {
+    if (url.Contains(breakpoint))
+      return breakpoint;
+  }
+  return WTF::String();
+}
+
 void InspectorDOMDebuggerAgent::WillSendXMLHttpOrFetchNetworkRequest(
     const String& url) {
-  String breakpoint_url;
-  if (pause_on_all_xhrs_.Get()) {
-    breakpoint_url = "";
-  } else {
-    for (const WTF::String& breakpoint : xhr_breakpoints_.Keys()) {
-      if (url.Contains(breakpoint)) {
-        breakpoint_url = breakpoint;
-        break;
-      }
-    }
-  }
-
+  String breakpoint_url = MatchXHRBreakpoints(url);
   if (breakpoint_url.IsNull())
     return;
 
@@ -754,9 +754,9 @@
 void InspectorDOMDebuggerAgent::DidRemoveBreakpoint() {
   if (!dom_breakpoints_.IsEmpty())
     return;
-  if (!event_listener_breakpoints_.Keys().empty())
+  if (!event_listener_breakpoints_.IsEmpty())
     return;
-  if (!xhr_breakpoints_.Keys().empty())
+  if (!xhr_breakpoints_.IsEmpty())
     return;
   if (pause_on_all_xhrs_.Get())
     return;
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
index 3f03fb11..a1c53b8 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
@@ -133,6 +133,8 @@
       const v8_inspector::StringView& object_group_id);
 
  private:
+  String MatchXHRBreakpoints(const String& url) const;
+
   static void EventListenersInfoForTarget(v8::Isolate*,
                                           v8::Local<v8::Value>,
                                           int depth,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 90209ad0..38dd472 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -37,6 +37,16 @@
 
 namespace {
 
+// Estimate the number of NGInlineItem to minimize the vector expansions.
+unsigned EstimateInlineItemsCount(const LayoutBlockFlow& block) {
+  unsigned count = 0;
+  for (LayoutObject* child = block.FirstChild(); child;
+       child = child->NextSibling()) {
+    ++count;
+  }
+  return count * 4;
+}
+
 // Templated helper function for CollectInlinesInternal().
 template <typename OffsetMappingBuilder>
 void ClearNeedsLayoutIfUpdatingLayout(LayoutObject* node) {
@@ -280,6 +290,7 @@
     // already there in NGInlineNodeData. For efficiency, we should make
     // |builder| not construct items and text content.
     Vector<NGInlineItem> items;
+    items.ReserveCapacity(EstimateInlineItemsCount(*GetLayoutBlockFlow()));
     NGInlineItemsBuilderForOffsetMapping builder(&items);
     CollectInlinesInternal(GetLayoutBlockFlow(), &builder, nullptr);
     String text = builder.ToString();
@@ -313,6 +324,7 @@
 
   String* previous_text =
       previous_data ? &previous_data->text_content : nullptr;
+  data->items.ReserveCapacity(EstimateInlineItemsCount(*block));
   NGInlineItemsBuilder builder(&data->items);
   CollectInlinesInternal(block, &builder, previous_text);
   data->text_content = builder.ToString();
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index ba47694..9cef095 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -930,6 +930,14 @@
                                             resource_request);
   DCHECK(provisional_document_loader_);
 
+  // TODO(csharrison): In M70 when UserActivation v2 should ship, we can remove
+  // the check that the pages are equal, because consumption should not be
+  // shared across pages.
+  if (frame_->IsMainFrame() && origin_document &&
+      frame_->GetPage() == origin_document->GetPage()) {
+    Frame::ConsumeTransientUserActivation(frame_);
+  }
+
   // TODO(dgozman): there is still a possibility of
   // |kNavigationPolicyCurrentTab| when starting a navigation. Perhaps, we can
   // just call CommitNavigation in this case instead, maybe from client side?
@@ -944,14 +952,6 @@
     probe::frameScheduledClientNavigation(frame_);
   }
 
-  // TODO(csharrison): In M70 when UserActivation v2 should ship, we can remove
-  // the check that the pages are equal, because consumption should not be
-  // shared across pages.
-  if (frame_->IsMainFrame() && origin_document &&
-      frame_->GetPage() == origin_document->GetPage()) {
-    Frame::ConsumeTransientUserActivation(frame_);
-  }
-
   TakeObjectSnapshot();
 }
 
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index aefb673..e3e187e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -608,18 +608,6 @@
     if (child->PhysicalFragment().IsFloating())
       continue;
     if (child->PhysicalFragment().IsAtomicInline()) {
-      // legacy_paint_offset is local, so we need to remove the offset to
-      // lineBox.
-      LayoutPoint legacy_paint_offset = paint_offset;
-      const NGPaintFragment* parent = child->Parent();
-      while (parent && (parent->PhysicalFragment().IsBox() ||
-                        parent->PhysicalFragment().IsLineBox())) {
-        legacy_paint_offset -= parent->Offset().ToLayoutPoint();
-        if (parent->PhysicalFragment().IsLineBox())
-          break;
-        parent = parent->Parent();
-      }
-
       PaintAtomicInlineChild(*child, paint_info);
     } else {
       PaintInlineChild(*child, paint_info, paint_offset);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 6700437..d4c7cbb 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1655,6 +1655,8 @@
     "testing/url_test_helpers.cc",
     "testing/url_test_helpers.h",
     "testing/use_mock_scrollbar_settings.h",
+    "testing/viewport_layers_setup.cc",
+    "testing/viewport_layers_setup.h",
     "testing/weburl_loader_mock.cc",
     "testing/weburl_loader_mock.h",
     "testing/weburl_loader_mock_factory_impl.cc",
@@ -2209,6 +2211,7 @@
   visibility = []
   visibility = [ "//third_party/blink/renderer/*" ]
   sources = [
+    "animation/animated_layers_test.cc",
     "graphics/canvas_2d_layer_bridge_test.cc",
     "graphics/canvas_color_params_test.cc",
     "graphics/canvas_resource_test.cc",
diff --git a/third_party/blink/renderer/platform/animation/animated_layers_test.cc b/third_party/blink/renderer/platform/animation/animated_layers_test.cc
new file mode 100644
index 0000000..1b27809
--- /dev/null
+++ b/third_party/blink/renderer/platform/animation/animated_layers_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "cc/layers/picture_layer.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/mutator_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/animation/compositor_target_property.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/viewport_layers_setup.h"
+
+namespace blink {
+
+class AnimatedLayersTest : public testing::Test,
+                           public PaintTestConfigurations {
+ public:
+  AnimatedLayersTest() = default;
+  ~AnimatedLayersTest() = default;
+
+ protected:
+  ViewportLayersSetup layers_;
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+                        AnimatedLayersTest,
+                        testing::Values(0, kBlinkGenPropertyTrees));
+
+class AnimationForTesting : public CompositorAnimationClient {
+ public:
+  AnimationForTesting() {
+    compositor_animation_ = CompositorAnimation::Create();
+  }
+
+  CompositorAnimation* GetCompositorAnimation() const override {
+    return compositor_animation_.get();
+  }
+
+  std::unique_ptr<CompositorAnimation> compositor_animation_;
+};
+
+TEST_P(AnimatedLayersTest, updateLayerShouldFlattenTransformWithAnimations) {
+  // TODO(bokan): This test doesn't yet work in blink-gen-property-trees
+  // because cc::Layers can't set an element id in that mode. We fail at
+  // AttachElement since the element id is invalid. https://crbug.com/836897.
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
+    return;
+
+  cc::Layer* cc_layer = layers_.graphics_layer().CcLayer();
+  cc::MutatorHost* mutator = layers_.layer_tree_host()->mutator_host();
+  EXPECT_FALSE(
+      mutator->HasTickingKeyframeModelForTesting(cc_layer->element_id()));
+
+  std::unique_ptr<CompositorFloatAnimationCurve> curve =
+      CompositorFloatAnimationCurve::Create();
+  curve->AddKeyframe(
+      CompositorFloatKeyframe(0.0, 0.0,
+                              *CubicBezierTimingFunction::Preset(
+                                  CubicBezierTimingFunction::EaseType::EASE)));
+  std::unique_ptr<CompositorKeyframeModel> float_keyframe_model(
+      CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
+                                      0, 0));
+  int keyframe_model_id = float_keyframe_model->Id();
+
+  std::unique_ptr<CompositorAnimationTimeline> compositor_timeline =
+      CompositorAnimationTimeline::Create();
+  AnimationForTesting animation;
+
+  CompositorAnimationHost host(layers_.animation_host());
+
+  host.AddTimeline(*compositor_timeline);
+  compositor_timeline->AnimationAttached(animation);
+
+  cc_layer->SetElementId(CompositorElementId(cc_layer->id()));
+
+  animation.GetCompositorAnimation()->AttachElement(cc_layer->element_id());
+  ASSERT_TRUE(animation.GetCompositorAnimation()->IsElementAttached());
+
+  animation.GetCompositorAnimation()->AddKeyframeModel(
+      std::move(float_keyframe_model));
+
+  EXPECT_TRUE(
+      mutator->HasTickingKeyframeModelForTesting(cc_layer->element_id()));
+
+  layers_.graphics_layer().SetShouldFlattenTransform(false);
+
+  cc_layer = layers_.graphics_layer().CcLayer();
+  ASSERT_TRUE(cc_layer);
+
+  EXPECT_TRUE(
+      mutator->HasTickingKeyframeModelForTesting(cc_layer->element_id()));
+  animation.GetCompositorAnimation()->RemoveKeyframeModel(keyframe_model_id);
+  EXPECT_FALSE(
+      mutator->HasTickingKeyframeModelForTesting(cc_layer->element_id()));
+
+  layers_.graphics_layer().SetShouldFlattenTransform(true);
+
+  cc_layer = layers_.graphics_layer().CcLayer();
+  ASSERT_TRUE(cc_layer);
+
+  EXPECT_FALSE(
+      mutator->HasTickingKeyframeModelForTesting(cc_layer->element_id()));
+
+  animation.GetCompositorAnimation()->DetachElement();
+  ASSERT_FALSE(animation.GetCompositorAnimation()->IsElementAttached());
+
+  compositor_timeline->AnimationDestroyed(animation);
+  host.RemoveTimeline(*compositor_timeline.get());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index ce105d3..0b204d5 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -115,6 +115,11 @@
 void ShapeResult::EnsureGraphemes(const StringView& text) const {
   DCHECK_EQ(NumCharacters(), text.length());
 
+  // Hit-testing, canvas, etc. may still call this function for 0-length text,
+  // or glyphs may be missing at all.
+  if (runs_.IsEmpty())
+    return;
+
   bool is_computed = !runs_.front()->graphemes_.IsEmpty();
 #if DCHECK_IS_ON()
   for (const auto& run : runs_)
diff --git a/third_party/blink/renderer/platform/graphics/DEPS b/third_party/blink/renderer/platform/graphics/DEPS
index 0d9d52f..f20473f 100644
--- a/third_party/blink/renderer/platform/graphics/DEPS
+++ b/third_party/blink/renderer/platform/graphics/DEPS
@@ -23,7 +23,6 @@
     "+media/renderers/video_resource_updater.h",
     "+services/ui/public/cpp/gpu/context_provider_command_buffer.h",
     "+services/viz/public/interfaces",
-    "+third_party/blink/renderer/platform/animation",
     "+third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h",
     "+third_party/blink/renderer/platform/cross_thread_functional.h",
     "+third_party/blink/renderer/platform/drag_image.h",
diff --git a/third_party/blink/renderer/platform/graphics/color_blend.h b/third_party/blink/renderer/platform/graphics/color_blend.h
index dc03dea..a31cb72 100644
--- a/third_party/blink/renderer/platform/graphics/color_blend.h
+++ b/third_party/blink/renderer/platform/graphics/color_blend.h
@@ -26,7 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BLEND_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BLEND_H_
 
-#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/geometry/blend.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
index b4aaada..9d11f208 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
@@ -29,28 +29,14 @@
 #include <utility>
 
 #include "cc/layers/picture_layer.h"
-#include "cc/trees/layer_tree_host.h"
-#include "cc/trees/mutator_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/public/platform/web_thread.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
-#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
-#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
-#include "third_party/blink/renderer/platform/animation/compositor_target_property.h"
-#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
-#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
 #include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
-#include "third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h"
 #include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/viewport_layers_setup.h"
 #include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
 #include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
 #include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
@@ -59,39 +45,9 @@
 
 class GraphicsLayerTest : public testing::Test, public PaintTestConfigurations {
  public:
-  GraphicsLayerTest() {
-    clip_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
-    scroll_elasticity_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
-    page_scale_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
-    graphics_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
-    graphics_layer_->SetDrawsContent(true);
-    clip_layer_->AddChild(scroll_elasticity_layer_.get());
-    scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
-    page_scale_layer_->AddChild(graphics_layer_.get());
-    graphics_layer_->CcLayer()->SetScrollable(clip_layer_->CcLayer()->bounds());
-    cc_layer_ = graphics_layer_->CcLayer();
-    layer_tree_ = std::make_unique<LayerTreeHostEmbedder>();
-    layer_tree_->layer_tree_host()->SetRootLayer(clip_layer_->CcLayer());
-    cc::LayerTreeHost::ViewportLayers viewport_layers;
-    viewport_layers.overscroll_elasticity = scroll_elasticity_layer_->CcLayer();
-    viewport_layers.page_scale = page_scale_layer_->CcLayer();
-    viewport_layers.inner_viewport_container = clip_layer_->CcLayer();
-    viewport_layers.inner_viewport_scroll = graphics_layer_->CcLayer();
-    layer_tree_->layer_tree_host()->RegisterViewportLayers(viewport_layers);
-    layer_tree_->layer_tree_host()->SetViewportSizeAndScale(
-        gfx::Size(1, 1), /*device_scale_factor=*/1.f, viz::LocalSurfaceId());
-
-    graphics_layer_->SetLayerState(PropertyTreeState(PropertyTreeState::Root()),
-                                   IntPoint());
-  }
-
+  GraphicsLayerTest() = default;
   ~GraphicsLayerTest() = default;
 
-  cc::LayerTreeHost* layer_tree_host() {
-    return layer_tree_->layer_tree_host();
-  }
-  cc::AnimationHost* animation_host() { return layer_tree_->animation_host(); }
-
  protected:
   bool PaintWithoutCommit(GraphicsLayer& layer, const IntRect* interest_rect) {
     return layer.PaintWithoutCommit(interest_rect);
@@ -116,15 +72,7 @@
     return layer.paint_controller_.get();
   }
 
-  cc::Layer* cc_layer_;
-  std::unique_ptr<FakeGraphicsLayer> graphics_layer_;
-  std::unique_ptr<FakeGraphicsLayer> page_scale_layer_;
-  std::unique_ptr<FakeGraphicsLayer> scroll_elasticity_layer_;
-  std::unique_ptr<FakeGraphicsLayer> clip_layer_;
-  FakeGraphicsLayerClient client_;
-
- private:
-  std::unique_ptr<LayerTreeHostEmbedder> layer_tree_;
+  ViewportLayersSetup layers_;
 };
 
 INSTANTIATE_TEST_CASE_P(All,
@@ -132,108 +80,27 @@
                         testing::Values(0,
                                         kBlinkGenPropertyTrees));
 
-class AnimationForTesting : public CompositorAnimationClient {
- public:
-  AnimationForTesting() {
-    compositor_animation_ = CompositorAnimation::Create();
-  }
-
-  CompositorAnimation* GetCompositorAnimation() const override {
-    return compositor_animation_.get();
-  }
-
-  std::unique_ptr<CompositorAnimation> compositor_animation_;
-};
-
-TEST_P(GraphicsLayerTest, updateLayerShouldFlattenTransformWithAnimations) {
-  // TODO(bokan): This test doesn't yet work in blink-gen-property-trees
-  // because cc::Layers can't set an element id in that mode. We fail at
-  // AttachElement since the element id is invalid. https://crbug.com/836897.
-  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
-    return;
-
-  cc::MutatorHost* mutator = layer_tree_host()->mutator_host();
-  EXPECT_FALSE(
-      mutator->HasTickingKeyframeModelForTesting(cc_layer_->element_id()));
-
-  std::unique_ptr<CompositorFloatAnimationCurve> curve =
-      CompositorFloatAnimationCurve::Create();
-  curve->AddKeyframe(
-      CompositorFloatKeyframe(0.0, 0.0,
-                              *CubicBezierTimingFunction::Preset(
-                                  CubicBezierTimingFunction::EaseType::EASE)));
-  std::unique_ptr<CompositorKeyframeModel> float_keyframe_model(
-      CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
-                                      0, 0));
-  int keyframe_model_id = float_keyframe_model->Id();
-
-  std::unique_ptr<CompositorAnimationTimeline> compositor_timeline =
-      CompositorAnimationTimeline::Create();
-  AnimationForTesting animation;
-
-  CompositorAnimationHost host(animation_host());
-
-  host.AddTimeline(*compositor_timeline);
-  compositor_timeline->AnimationAttached(animation);
-
-  cc_layer_->SetElementId(CompositorElementId(cc_layer_->id()));
-
-  animation.GetCompositorAnimation()->AttachElement(cc_layer_->element_id());
-  ASSERT_TRUE(animation.GetCompositorAnimation()->IsElementAttached());
-
-  animation.GetCompositorAnimation()->AddKeyframeModel(
-      std::move(float_keyframe_model));
-
-  EXPECT_TRUE(
-      mutator->HasTickingKeyframeModelForTesting(cc_layer_->element_id()));
-
-  graphics_layer_->SetShouldFlattenTransform(false);
-
-  cc_layer_ = graphics_layer_->CcLayer();
-  ASSERT_TRUE(cc_layer_);
-
-  EXPECT_TRUE(
-      mutator->HasTickingKeyframeModelForTesting(cc_layer_->element_id()));
-  animation.GetCompositorAnimation()->RemoveKeyframeModel(keyframe_model_id);
-  EXPECT_FALSE(
-      mutator->HasTickingKeyframeModelForTesting(cc_layer_->element_id()));
-
-  graphics_layer_->SetShouldFlattenTransform(true);
-
-  cc_layer_ = graphics_layer_->CcLayer();
-  ASSERT_TRUE(cc_layer_);
-
-  EXPECT_FALSE(
-      mutator->HasTickingKeyframeModelForTesting(cc_layer_->element_id()));
-
-  animation.GetCompositorAnimation()->DetachElement();
-  ASSERT_FALSE(animation.GetCompositorAnimation()->IsElementAttached());
-
-  compositor_timeline->AnimationDestroyed(animation);
-  host.RemoveTimeline(*compositor_timeline.get());
-}
-
 TEST_P(GraphicsLayerTest, Paint) {
   IntRect interest_rect(1, 2, 3, 4);
-  EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
-  CommitAndFinishCycle(*graphics_layer_);
+  EXPECT_TRUE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
+  CommitAndFinishCycle(layers_.graphics_layer());
 
-  client_.SetNeedsRepaint(true);
-  EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
-  CommitAndFinishCycle(*graphics_layer_);
+  layers_.graphics_layer_client().SetNeedsRepaint(true);
+  EXPECT_TRUE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
+  CommitAndFinishCycle(layers_.graphics_layer());
 
-  client_.SetNeedsRepaint(false);
-  EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+  layers_.graphics_layer_client().SetNeedsRepaint(false);
+  EXPECT_FALSE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
 
   interest_rect.Move(IntSize(10, 20));
-  EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
-  CommitAndFinishCycle(*graphics_layer_);
-  EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+  EXPECT_TRUE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
+  CommitAndFinishCycle(layers_.graphics_layer());
+  EXPECT_FALSE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
 
-  graphics_layer_->SetNeedsDisplay();
-  EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
-  CommitAndFinishCycle(*graphics_layer_);
-  EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+  layers_.graphics_layer().SetNeedsDisplay();
+  EXPECT_TRUE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
+  CommitAndFinishCycle(layers_.graphics_layer());
+  EXPECT_FALSE(PaintWithoutCommit(layers_.graphics_layer(), &interest_rect));
 }
 
 TEST_P(GraphicsLayerTest, PaintRecursively) {
@@ -244,8 +111,10 @@
   auto transform2 =
       CreateTransform(*transform1, TransformationMatrix().Scale(2));
 
-  client_.SetPainter([&](const GraphicsLayer* layer, GraphicsContext& context,
-                         GraphicsLayerPaintingPhase, const IntRect&) {
+  layers_.graphics_layer_client().SetPainter([&](const GraphicsLayer* layer,
+                                                 GraphicsContext& context,
+                                                 GraphicsLayerPaintingPhase,
+                                                 const IntRect&) {
     {
       ScopedPaintChunkProperties properties(context.GetPaintController(),
                                             transform1.get(), *layer,
@@ -267,23 +136,23 @@
                          TransformationMatrix().Translate(20, 30)});
   EXPECT_TRUE(transform1->Changed(transform_root));
   EXPECT_TRUE(transform2->Changed(transform_root));
-  client_.SetNeedsRepaint(true);
-  graphics_layer_->PaintRecursively();
+  layers_.graphics_layer_client().SetNeedsRepaint(true);
+  layers_.graphics_layer().PaintRecursively();
 
   EXPECT_FALSE(transform1->Changed(transform_root));
   EXPECT_FALSE(transform2->Changed(transform_root));
 }
 
 TEST_P(GraphicsLayerTest, SetDrawsContentFalse) {
-  EXPECT_TRUE(graphics_layer_->DrawsContent());
-  graphics_layer_->GetPaintController();
-  EXPECT_NE(nullptr, GetInternalPaintController(*graphics_layer_));
-  EnsureRasterInvalidator(*graphics_layer_);
-  EXPECT_NE(nullptr, GetInternalRasterInvalidator(*graphics_layer_));
+  EXPECT_TRUE(layers_.graphics_layer().DrawsContent());
+  layers_.graphics_layer().GetPaintController();
+  EXPECT_NE(nullptr, GetInternalPaintController(layers_.graphics_layer()));
+  EnsureRasterInvalidator(layers_.graphics_layer());
+  EXPECT_NE(nullptr, GetInternalRasterInvalidator(layers_.graphics_layer()));
 
-  graphics_layer_->SetDrawsContent(false);
-  EXPECT_EQ(nullptr, GetInternalPaintController(*graphics_layer_));
-  EXPECT_EQ(nullptr, GetInternalRasterInvalidator(*graphics_layer_));
+  layers_.graphics_layer().SetDrawsContent(false);
+  EXPECT_EQ(nullptr, GetInternalPaintController(layers_.graphics_layer()));
+  EXPECT_EQ(nullptr, GetInternalRasterInvalidator(layers_.graphics_layer()));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/fake_graphics_layer.h b/third_party/blink/renderer/platform/testing/fake_graphics_layer.h
index d6cd8f8..7199a538 100644
--- a/third_party/blink/renderer/platform/testing/fake_graphics_layer.h
+++ b/third_party/blink/renderer/platform/testing/fake_graphics_layer.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_H_
 
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
 namespace blink {
 
 // A simple GraphicsLayer implementation suitable for use in unit tests.
diff --git a/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h b/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h
index 4d4e7c98..38ce668 100644
--- a/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h
+++ b/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_CLIENT_H_
 
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc b/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc
new file mode 100644
index 0000000..1a130212
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/viewport_layers_setup.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/viewport_layers_setup.h"
+
+#include <memory>
+#include "cc/layers/picture_layer.h"
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h"
+
+namespace blink {
+
+ViewportLayersSetup::ViewportLayersSetup() {
+  clip_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+  scroll_elasticity_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+  page_scale_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+  graphics_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+  graphics_layer_->SetDrawsContent(true);
+  clip_layer_->AddChild(scroll_elasticity_layer_.get());
+  scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
+  page_scale_layer_->AddChild(graphics_layer_.get());
+  graphics_layer_->CcLayer()->SetScrollable(clip_layer_->CcLayer()->bounds());
+  layer_tree_ = std::make_unique<LayerTreeHostEmbedder>();
+  layer_tree_->layer_tree_host()->SetRootLayer(clip_layer_->CcLayer());
+  cc::LayerTreeHost::ViewportLayers viewport_layers;
+  viewport_layers.overscroll_elasticity = scroll_elasticity_layer_->CcLayer();
+  viewport_layers.page_scale = page_scale_layer_->CcLayer();
+  viewport_layers.inner_viewport_container = clip_layer_->CcLayer();
+  viewport_layers.inner_viewport_scroll = graphics_layer_->CcLayer();
+  layer_tree_->layer_tree_host()->RegisterViewportLayers(viewport_layers);
+  layer_tree_->layer_tree_host()->SetViewportSizeAndScale(
+      gfx::Size(1, 1), /*device_scale_factor=*/1.f, viz::LocalSurfaceId());
+
+  graphics_layer_->SetLayerState(PropertyTreeState(PropertyTreeState::Root()),
+                                 IntPoint());
+}
+
+ViewportLayersSetup::~ViewportLayersSetup() = default;
+
+cc::LayerTreeHost* ViewportLayersSetup::layer_tree_host() {
+  return layer_tree_->layer_tree_host();
+}
+
+cc::AnimationHost* ViewportLayersSetup::animation_host() {
+  return layer_tree_->animation_host();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/viewport_layers_setup.h b/third_party/blink/renderer/platform/testing/viewport_layers_setup.h
new file mode 100644
index 0000000..febdac8
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/viewport_layers_setup.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_VIEWPORT_LAYERS_SETUP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_VIEWPORT_LAYERS_SETUP_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+
+namespace cc {
+class AnimationHost;
+class LayerTreeHost;
+}  // namespace cc
+
+namespace blink {
+
+class FakeGraphicsLayer;
+class LayerTreeHostEmbedder;
+
+class ViewportLayersSetup {
+ public:
+  ViewportLayersSetup();
+  ~ViewportLayersSetup();
+
+  FakeGraphicsLayer& graphics_layer() { return *graphics_layer_; }
+  FakeGraphicsLayerClient& graphics_layer_client() { return client_; }
+
+  cc::LayerTreeHost* layer_tree_host();
+  cc::AnimationHost* animation_host();
+
+ private:
+  std::unique_ptr<FakeGraphicsLayer> graphics_layer_;
+  std::unique_ptr<FakeGraphicsLayer> page_scale_layer_;
+  std::unique_ptr<FakeGraphicsLayer> scroll_elasticity_layer_;
+  std::unique_ptr<FakeGraphicsLayer> clip_layer_;
+  FakeGraphicsLayerClient client_;
+  std::unique_ptr<LayerTreeHostEmbedder> layer_tree_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_VIEWPORT_LAYERS_SETUP_H_
diff --git a/third_party/blink/tools/blinkpy/common/net/git_cl.py b/third_party/blink/tools/blinkpy/common/net/git_cl.py
index 887a4cec..6fe7faa 100644
--- a/third_party/blink/tools/blinkpy/common/net/git_cl.py
+++ b/third_party/blink/tools/blinkpy/common/net/git_cl.py
@@ -87,7 +87,15 @@
         return builders_by_bucket
 
     def get_issue_number(self):
-        return self.run(['issue']).split()[2]
+        """Returns the issue number as a string, or "None"."""
+        # Expected output of git cl issue looks like:
+        # "<Optional message> Issue number: 1234 (<url>)".
+        # Note: git cl gets the number from local git config, e.g.
+        #   by running `git config branch.<branchname>.gerritissue`.
+        output = self.run(['issue']).split()
+        if 'number:' in output:
+            return output[output.index('number:') + 1]
+        return 'None'
 
     def _get_cl_status(self):
         return self.run(['status', '--field=status']).strip()
diff --git a/third_party/blink/tools/blinkpy/common/net/git_cl_unittest.py b/third_party/blink/tools/blinkpy/common/net/git_cl_unittest.py
index 96a0fda..e47ef921 100644
--- a/third_party/blink/tools/blinkpy/common/net/git_cl_unittest.py
+++ b/third_party/blink/tools/blinkpy/common/net/git_cl_unittest.py
@@ -121,7 +121,7 @@
 
     def test_get_issue_number(self):
         host = MockHost()
-        host.executive = MockExecutive(output='Issue number: 12345 (http://crrev.com/12345)')
+        host.executive = MockExecutive(output='Foo\nIssue number: 12345 (http://crrev.com/12345)')
         git_cl = GitCL(host)
         self.assertEqual(git_cl.get_issue_number(), '12345')
 
@@ -131,6 +131,12 @@
         git_cl = GitCL(host)
         self.assertEqual(git_cl.get_issue_number(), 'None')
 
+    def test_get_issue_number_nothing_in_output(self):
+        host = MockHost()
+        host.executive = MockExecutive(output='Bogus output')
+        git_cl = GitCL(host)
+        self.assertEqual(git_cl.get_issue_number(), 'None')
+
     def test_wait_for_try_jobs_timeout(self):
         host = MockHost()
         git_cl = GitCL(host)
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
index 587d59a..7bdc8c04 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
@@ -924,8 +924,6 @@
         MISSING: 'missing results',
     }
 
-    NON_TEST_OUTCOME_EXPECTATIONS = (REBASELINE, SKIP, SLOW, WONTFIX)
-
     BUILD_TYPES = ('debug', 'release')
 
     TIMELINES = {
@@ -962,19 +960,26 @@
             expected_results: set of results listed in test_expectations
             test_needs_rebaselining: whether test was marked as REBASELINE
         """
-        if not set(expected_results) - set(TestExpectations.NON_TEST_OUTCOME_EXPECTATIONS):
-            expected_results = set([PASS])
+        local_expected = set(expected_results)
+        if WONTFIX in local_expected:
+            # WontFix should be treated as if we expected a Skip.
+            local_expected.add(SKIP)
 
-        if result in expected_results:
+        # Make sure we have at least one result type that may actually happen.
+        local_expected.discard(WONTFIX)
+        local_expected.discard(REBASELINE)
+        local_expected.discard(SLOW)
+        if not local_expected:
+            local_expected = {PASS}
+
+        if result in local_expected:
             return True
-        if result in (PASS, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, MISSING) and NEEDS_MANUAL_REBASELINE in expected_results:
+        if result in (PASS, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, MISSING) and NEEDS_MANUAL_REBASELINE in local_expected:
             return True
-        if result in (TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO) and FAIL in expected_results:
+        if result in (TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO) and FAIL in local_expected:
             return True
         if result == MISSING and test_needs_rebaselining:
             return True
-        if result == SKIP:
-            return True
         return False
 
     @staticmethod
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py
index 18344a0..ce72aa0 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py
@@ -125,8 +125,8 @@
         self.assertEqual(TestExpectations.result_was_expected(FAIL, set([PASS]), test_needs_rebaselining=False), False)
 
         # test handling of SKIPped tests and results
-        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([CRASH]), test_needs_rebaselining=False), True)
-        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([LEAK]), test_needs_rebaselining=False), True)
+        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([CRASH]), test_needs_rebaselining=False), False)
+        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([LEAK]), test_needs_rebaselining=False), False)
 
         # test handling of MISSING results and the REBASELINE specifier
         self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=True), True)
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_run_results.py b/third_party/blink/tools/blinkpy/web_tests/models/test_run_results.py
index 3e7668df..ad6448de 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_run_results.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_run_results.py
@@ -245,16 +245,28 @@
                     has_unexpected_pass = True
             else:
                 has_expected = True
-        # A test is flaky if it has both expected and unexpected runs (NOT pass
-        # and failure).
+
+        # TODO(crbug.com/855255): This code calls a test flaky if it has both
+        # expected and unexpected runs (NOT pass and failure); this is generally
+        # wrong (really it should just be if there are multiple kinds of results),
+        # but this works in the normal case because a test will only be retried
+        # if a result is unexpected, and if you get an expected result on the
+        # retry, then you did get multiple results. This fails if you get
+        # one kind of unexpected failure initially and another kind of
+        # unexpected failure on the retry (e.g., TIMEOUT CRASH), or if you
+        # explicitly run a test multiple times and get multiple expected results.
         is_flaky = has_expected and has_unexpected
 
-        if len(set(actual)) == 1:
-            actual = [actual[0]]
-            actual_types = [actual_types[0]]
+        test_dict = {}
+        test_dict['expected'] = expected
+        test_dict['actual'] = ' '.join(actual)
+
+        # Fields below are optional. To avoid bloating the output results json
+        # too much, only add them when they are True or non-empty.
 
         if is_flaky:
             num_flaky += 1
+            test_dict['is_flaky'] = True
         elif all_pass or has_unexpected_pass:
             # We count two situations as a "pass":
             # 1. All test runs pass (which is obviously non-flaky, but does not
@@ -268,19 +280,10 @@
             num_passes += 1
             if not has_stderr and only_include_failing:
                 continue
-        elif has_unexpected and result.type != test_expectations.SKIP:
+        elif has_unexpected:
             # Either no retries or all retries failed unexpectedly.
-            # TODO(robertma): When will there be unexpected skip? Do we really
-            # want to ignore them when counting regressions?
             num_regressions += 1
 
-        test_dict = {}
-
-        test_dict['expected'] = expected
-        test_dict['actual'] = ' '.join(actual)
-
-        # Fields below are optional. To avoid bloating the output results json
-        # too much, only add them when they are True or non-empty.
 
         rounded_run_time = round(initial_result.test_run_time, 1)
         if rounded_run_time:
@@ -318,11 +321,15 @@
                                                            port_obj.get_option('pixel_tests') or initial_result.reftest_type,
                                                            port_obj.get_option('enable_sanitizer'))
 
-        # Note: is_unexpected is intended to capture the *last* result. In the
-        # normal use case (stop retrying failures once they pass), this is
-        # equivalent to checking if none of the results is expected.
-        if not any(is_expected(actual_result) for actual_result in actual_types):
+        # Note: is_unexpected and is_regression are intended to reflect the
+        # *last* result. In the normal use case (stop retrying failures
+        # once they pass), this is equivalent to saying that all of the
+        # results were unexpected failures.
+        last_result = actual_types[-1]
+        if not is_expected(last_result):
             test_dict['is_unexpected'] = True
+            if last_result != test_expectations.PASS:
+                test_dict['is_regression'] = True
 
         if initial_result.has_repaint_overlay:
             test_dict['has_repaint_overlay'] = True
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_run_results_unittest.py b/third_party/blink/tools/blinkpy/web_tests/models/test_run_results_unittest.py
index b56872d..5b9689a 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_run_results_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_run_results_unittest.py
@@ -83,7 +83,7 @@
     elif passing:
         skipped_result = get_result('passes/skipped/skip.html')
         skipped_result.type = test_expectations.SKIP
-        initial_results.add(skipped_result, expected, test_is_slow)
+        initial_results.add(skipped_result, True, test_is_slow)
 
         initial_results.add(get_result('passes/text.html', run_time=1), expected, test_is_slow)
         initial_results.add(get_result('failures/expected/audio.html'), expected, test_is_slow)
@@ -285,7 +285,6 @@
         self.port._options.builder_name = 'dummy builder'
         summary = summarized_results(self.port, expected=False, passing=True, flaky=False)
         self.assertTrue(summary['tests']['passes']['text.html'])
-        self.assertTrue('is_unexpected' not in summary['tests']['passes']['text.html'])
         self.assertEqual(summary['num_passes'], 5)
         self.assertEqual(summary['num_regressions'], 0)
         self.assertEqual(summary['num_flaky'], 0)
@@ -347,7 +346,6 @@
     def test_summarized_results_flaky(self):
         summary = summarized_results(self.port, expected=False, passing=False, flaky=True)
 
-        self.assertTrue('is_unexpected' not in summary['tests']['failures']['expected']['crash.html'])
         self.assertEquals(summary['tests']['failures']['expected']['crash.html']['expected'], 'CRASH')
         self.assertEquals(summary['tests']['failures']['expected']['crash.html']['actual'], 'TIMEOUT AUDIO CRASH LEAK')
 
@@ -425,15 +423,15 @@
 
         self.assertTrue(summary['tests']['passes']['text.html']['is_unexpected'])
         self.assertEquals(summary['tests']['passes']['text.html']['expected'], 'PASS')
-        self.assertEquals(summary['tests']['passes']['text.html']['actual'], 'TIMEOUT')
+        self.assertEquals(summary['tests']['passes']['text.html']['actual'], 'TIMEOUT TIMEOUT TIMEOUT TIMEOUT')
 
         self.assertTrue(summary['tests']['failures']['expected']['crash.html']['is_unexpected'])
         self.assertEquals(summary['tests']['failures']['expected']['crash.html']['expected'], 'CRASH')
-        self.assertEquals(summary['tests']['failures']['expected']['crash.html']['actual'], 'TIMEOUT')
+        self.assertEquals(summary['tests']['failures']['expected']['crash.html']['actual'], 'TIMEOUT TIMEOUT TIMEOUT TIMEOUT')
 
         self.assertTrue(summary['tests']['failures']['expected']['leak.html']['is_unexpected'])
         self.assertEquals(summary['tests']['failures']['expected']['leak.html']['expected'], 'LEAK')
-        self.assertEquals(summary['tests']['failures']['expected']['leak.html']['actual'], 'TIMEOUT')
+        self.assertEquals(summary['tests']['failures']['expected']['leak.html']['actual'], 'TIMEOUT TIMEOUT TIMEOUT TIMEOUT')
 
         self.assertTrue(summary['tests']['failures']['expected']['audio.html']['is_unexpected'])
         self.assertEquals(summary['tests']['failures']['expected']['audio.html']['expected'], 'FAIL')
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
index dfa77e55..decf15c 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
@@ -633,16 +633,27 @@
                                      'failures/unexpected/text-image-checksum.html'],
                                     tests_included=True, host=host)
         self.assertEqual(details.exit_code, 2)
-        json_string = host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json')
-        self.assertTrue(json_string.find(
-            '"text-image-checksum.html":{'
-            '"expected":"PASS",'
-            '"text_mismatch":"general text mismatch",'
-            '"actual":"IMAGE+TEXT","is_unexpected":true') != -1)
-        self.assertTrue(json_string.find(
-            '"missing_text.html":{"expected":"PASS","is_missing_text":true,"actual":"MISSING","is_unexpected":true') != -1)
-        self.assertTrue(json_string.find('"num_regressions":2') != -1)
-        self.assertTrue(json_string.find('"num_flaky":0') != -1)
+        results = json.loads(host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json'))
+        self.assertEqual(
+            results['tests']['failures']['unexpected']['text-image-checksum.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'IMAGE+TEXT',
+                'is_unexpected': True,
+                'is_regression': True,
+                'text_mismatch': 'general text mismatch',
+            })
+        self.assertEqual(
+            results['tests']['failures']['unexpected']['missing_text.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'MISSING',
+                'is_unexpected': True,
+                'is_regression': True,
+                'is_missing_text': True,
+            })
+        self.assertEqual(results['num_regressions'], 2)
+        self.assertEqual(results['num_flaky'], 0)
 
     def test_different_failure_on_retry(self):
         # This tests that if a test fails two different ways -- both unexpected
@@ -675,8 +686,8 @@
     def test_crash_with_stderr(self):
         host = MockHost()
         logging_run(['failures/unexpected/crash-with-stderr.html'], tests_included=True, host=host)
-        self.assertTrue(host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json').find(
-            '{"crash-with-stderr.html":{"expected":"PASS","actual":"CRASH","has_stderr":true,"is_unexpected":true') != -1)
+        full_results = json.loads(host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json'))
+        self.assertEqual(full_results['tests']['failures']['unexpected']['crash-with-stderr.html']['has_stderr'], True)
 
     def test_no_image_failure_with_image_diff(self):
         host = MockHost()
@@ -844,11 +855,15 @@
             host.filesystem.exists('/tmp/layout-test-results/retry_3/failures/unexpected/text-image-checksum-actual.png'))
         json_string = host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json')
         results = parse_full_results(json_string)
-        self.assertEqual(results['tests']['failures']['unexpected']['text-image-checksum.html'],
-                         {'expected': 'PASS',
-                          'actual': 'TEXT IMAGE+TEXT IMAGE+TEXT IMAGE+TEXT',
-                          'is_unexpected': True,
-                          'text_mismatch': 'general text mismatch'})
+        self.assertEqual(
+            results['tests']['failures']['unexpected']['text-image-checksum.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'TEXT IMAGE+TEXT IMAGE+TEXT IMAGE+TEXT',
+                'is_regression': True,
+                'is_unexpected': True,
+                'text_mismatch': 'general text mismatch',
+            })
         self.assertFalse(results['pixel_tests_enabled'])
         self.assertTrue(details.enabled_pixel_tests_in_retry)
 
@@ -941,7 +956,7 @@
         host = MockHost()
         logging_run(['--no-show-results', 'reftests/foo/'], tests_included=True, host=host)
         results = parse_full_results(host.filesystem.read_text_file('/tmp/layout-test-results/full_results.json'))
-        self.assertEqual(results['tests']['reftests']['foo']['unlistedtest.html']['actual'], 'MISSING')
+        self.assertEqual(results['tests']['reftests']['foo']['unlistedtest.html']['actual'], 'MISSING MISSING MISSING MISSING')
         self.assertEqual(results['num_regressions'], 5)
         self.assertEqual(results['num_flaky'], 0)
 
@@ -1140,12 +1155,33 @@
         self.assertTrue('multiple-mismatch-success.html' not in results['tests']['reftests']['foo'])
         self.assertTrue('multiple-both-success.html' not in results['tests']['reftests']['foo'])
 
-        self.assertEqual(results['tests']['reftests']['foo']['multiple-match-failure.html'],
-                         {'expected': 'PASS', 'actual': 'IMAGE', 'reftest_type': ['=='], 'is_unexpected': True})
-        self.assertEqual(results['tests']['reftests']['foo']['multiple-mismatch-failure.html'],
-                         {'expected': 'PASS', 'actual': 'IMAGE', 'reftest_type': ['!='], 'is_unexpected': True})
-        self.assertEqual(results['tests']['reftests']['foo']['multiple-both-failure.html'],
-                         {'expected': 'PASS', 'actual': 'IMAGE', 'reftest_type': ['==', '!='], 'is_unexpected': True})
+        self.assertEqual(
+            results['tests']['reftests']['foo']['multiple-match-failure.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'IMAGE IMAGE IMAGE IMAGE',
+                'reftest_type': ['=='],
+                'is_regression': True,
+                'is_unexpected': True,
+            })
+        self.assertEqual(
+            results['tests']['reftests']['foo']['multiple-mismatch-failure.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'IMAGE IMAGE IMAGE IMAGE',
+                'reftest_type': ['!='],
+                'is_regression': True,
+                'is_unexpected': True,
+            })
+        self.assertEqual(
+            results['tests']['reftests']['foo']['multiple-both-failure.html'],
+            {
+                'expected': 'PASS',
+                'actual': 'IMAGE IMAGE IMAGE IMAGE',
+                'reftest_type': ['==', '!='],
+                'is_regression': True,
+                'is_unexpected': True,
+            })
 
 
 class RebaselineTest(unittest.TestCase, StreamTestingMixin):
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index 34d7ea6..bfb1124 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,9 +2,9 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: 0
-Date: Thursday July 26 2018
+Date: Tuesday July 31 2018
 Branch: master
-Commit: 4f1fd9640433a9ee83dfd48c83a3d281fbdfce3d
+Commit: bc484c485277bc19c7a1b273c8cf5472f741b73a
 License: BSD
 License File: source/libaom/LICENSE
 Security Critical: yes
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index dddb7da..d106138 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -12,8 +12,8 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 0
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "224-g4f1fd9640"
+#define VERSION_EXTRA "262-gbc484c485"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "1.0.0-224-g4f1fd9640"
-#define VERSION_STRING " 1.0.0-224-g4f1fd9640"
+#define VERSION_STRING_NOSP "1.0.0-262-gbc484c485"
+#define VERSION_STRING " 1.0.0-262-gbc484c485"
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
index 7dd43f8..062ba8b 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
@@ -27,6 +27,7 @@
 CONFIG_DIST_8X8 equ 1
 CONFIG_ENTROPY_STATS equ 0
 CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
 CONFIG_FP_MB_STATS equ 0
 CONFIG_GCC equ 1
 CONFIG_GCOV equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
index 4c9716cd..29912ec 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
index bb36ad4..97336e82 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
index a7a0155..2874e91 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
@@ -27,6 +27,7 @@
 CONFIG_DIST_8X8 equ 1
 CONFIG_ENTROPY_STATS equ 0
 CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
 CONFIG_FP_MB_STATS equ 0
 CONFIG_GCC equ 1
 CONFIG_GCOV equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
index 4c9716cd..29912ec 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
index 608001c8..0436ebe 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.asm b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
index 79249012..ba712ca 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
@@ -27,6 +27,7 @@
 CONFIG_DIST_8X8 equ 1
 CONFIG_ENTROPY_STATS equ 0
 CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
 CONFIG_FP_MB_STATS equ 0
 CONFIG_GCC equ 1
 CONFIG_GCOV equ 0
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.c b/third_party/libaom/source/config/linux/arm/config/aom_config.c
index 4c9716cd..29912ec 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/armv7-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.h b/third_party/libaom/source/config/linux/arm/config/aom_config.h
index 6aca1e1..d0c5a79 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
index a7a0155..2874e91 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
@@ -27,6 +27,7 @@
 CONFIG_DIST_8X8 equ 1
 CONFIG_ENTROPY_STATS equ 0
 CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
 CONFIG_FP_MB_STATS equ 0
 CONFIG_GCC equ 1
 CONFIG_GCOV equ 0
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.c b/third_party/libaom/source/config/linux/arm64/config/aom_config.c
index b27b4ec3..57319a2 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/arm64-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/arm64-linux-gcc.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.h b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
index 608001c8..0436ebe 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.asm b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
index 6770dd41..b0995db 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
@@ -27,6 +27,7 @@
 CONFIG_DIST_8X8 equ 1
 CONFIG_ENTROPY_STATS equ 0
 CONFIG_FILEOPTIONS equ 1
+CONFIG_FIX_GF_LENGTH equ 1
 CONFIG_FP_MB_STATS equ 0
 CONFIG_GCC equ 1
 CONFIG_GCOV equ 0
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.h b/third_party/libaom/source/config/linux/generic/config/aom_config.h
index 5a1ae02..b040729 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
index 026be28..c3ee8ab 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
@@ -17,6 +17,7 @@
 %define CONFIG_DIST_8X8 1
 %define CONFIG_ENTROPY_STATS 0
 %define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
 %define CONFIG_FP_MB_STATS 0
 %define CONFIG_GCC 1
 %define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.c b/third_party/libaom/source/config/linux/ia32/config/aom_config.c
index cfb85174..9028a6b 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.c
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.h b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
index 339f0ef7..1b0d11a1 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.asm b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
index 30d5316..1e8816c 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
@@ -17,6 +17,7 @@
 %define CONFIG_DIST_8X8 1
 %define CONFIG_ENTROPY_STATS 0
 %define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
 %define CONFIG_FP_MB_STATS 0
 %define CONFIG_GCC 1
 %define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.h b/third_party/libaom/source/config/linux/x64/config/aom_config.h
index 4492e0a0..ece0782a 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 1
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.asm b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
index e020134..b2a1a8a 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
@@ -17,6 +17,7 @@
 %define CONFIG_DIST_8X8 1
 %define CONFIG_ENTROPY_STATS 0
 %define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
 %define CONFIG_FP_MB_STATS 0
 %define CONFIG_GCC 0
 %define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.c b/third_party/libaom/source/config/win/ia32/config/aom_config.c
index cfb85174..9028a6b 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.c
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.c
@@ -9,5 +9,5 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include "aom/aom_codec.h"
-static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/thomasanderson/dev/chromium_1/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
+static const char* const cfg = "-G \"Unix Makefiles\" -DCMAKE_TOOLCHAIN_FILE=\"/usr/local/google/home/urvang/work/chromium/src/third_party/libaom/source/libaom/build/cmake/toolchains/x86-linux.cmake\" -DCONFIG_AV1_ENCODER=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_MAX_DECODE_PROFILE=0 -DCONFIG_NORMAL_TILE_MODE=1 -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384";
 const char *aom_codec_build_config(void) {return cfg;}
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.h b/third_party/libaom/source/config/win/ia32/config/aom_config.h
index 2bce73b..e2314ba 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 0
 #define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.asm b/third_party/libaom/source/config/win/x64/config/aom_config.asm
index cc5488f..5a9cfd8 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.asm
@@ -17,6 +17,7 @@
 %define CONFIG_DIST_8X8 1
 %define CONFIG_ENTROPY_STATS 0
 %define CONFIG_FILEOPTIONS 1
+%define CONFIG_FIX_GF_LENGTH 1
 %define CONFIG_FP_MB_STATS 0
 %define CONFIG_GCC 0
 %define CONFIG_GCOV 0
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.h b/third_party/libaom/source/config/win/x64/config/aom_config.h
index 345702c..c3ad5e5 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.h
@@ -29,6 +29,7 @@
 #define CONFIG_DIST_8X8 1
 #define CONFIG_ENTROPY_STATS 0
 #define CONFIG_FILEOPTIONS 1
+#define CONFIG_FIX_GF_LENGTH 1
 #define CONFIG_FP_MB_STATS 0
 #define CONFIG_GCC 0
 #define CONFIG_GCOV 0
diff --git a/third_party/tcmalloc/chromium/src/base/commandlineflags.h b/third_party/tcmalloc/chromium/src/base/commandlineflags.h
index 49c904f8..b2b58e9b 100644
--- a/third_party/tcmalloc/chromium/src/base/commandlineflags.h
+++ b/third_party/tcmalloc/chromium/src/base/commandlineflags.h
@@ -97,16 +97,15 @@
 #define DEFINE_double(name, value, meaning) \
   DEFINE_VARIABLE(double, name, value, meaning)
 
-// Special case for string, because we have to specify the namespace
-// std::string, which doesn't play nicely with our FLAG__namespace hackery.
+// Special case for string, because of the pointer type.
 #define DECLARE_string(name)                                          \
   namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead {  \
-  extern std::string FLAGS_##name;                                                   \
+  extern const char* FLAGS_##name;                                            \
   }                                                                           \
   using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
 #define DEFINE_string(name, value, meaning) \
   namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead {  \
-  std::string FLAGS_##name(value);                                                   \
+  const char* FLAGS_##name = value;                                           \
   char FLAGS_no##name;                                                        \
   }                                                                           \
   using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
@@ -157,6 +156,8 @@
 // These macros (could be functions, but I don't want to bother with a .cc
 // file), make it easier to initialize flags from the environment.
 
+#if defined(ENABLE_PROFILING)
+
 #define EnvToString(envname, dflt)   \
   (!getenv(envname) ? (dflt) : getenv(envname))
 
@@ -172,4 +173,14 @@
 #define EnvToDouble(envname, dflt)  \
   tcmalloc::commandlineflags::StringToDouble(getenv(envname), dflt)
 
+#else  // defined(ENABLE_PROFILING)
+
+#define EnvToString(envname, dflt) (dflt)
+#define EnvToBool(envname, dflt) (dflt)
+#define EnvToInt(envname, dflt) (dflt)
+#define EnvToInt64(envname, dflt) (dflt)
+#define EnvToDouble(envname, dflt) (dflt)
+
+#endif  // defined(ENABLE_PROFILING)
+
 #endif  // BASE_COMMANDLINEFLAGS_H_
diff --git a/third_party/tcmalloc/chromium/src/base/sysinfo.cc b/third_party/tcmalloc/chromium/src/base/sysinfo.cc
index 36f70679..ceb8e43 100644
--- a/third_party/tcmalloc/chromium/src/base/sysinfo.cc
+++ b/third_party/tcmalloc/chromium/src/base/sysinfo.cc
@@ -228,6 +228,9 @@
 // in their first character!  If that assumption is violated, we'll
 // still get a profile, but one with an unexpected name.
 // TODO(csilvers): set an envvar instead when we can do it reliably.
+//
+// In Chromium this hack is intentionally disabled, because the path is not
+// re-initialized upon fork.
 bool GetUniquePathFromEnv(const char* env_name, char* path) {
   char* envval = getenv(env_name);
   if (envval == NULL || *envval == '\0')
@@ -237,7 +240,9 @@
              envval[0] & 127, envval+1, (unsigned int)(getpid()));
   } else {
     snprintf(path, PATH_MAX, "%s", envval);
+#if 0
     envval[0] |= 128;                     // set high bit for kids to see
+#endif
   }
   return true;
 }
diff --git a/third_party/tcmalloc/chromium/src/heap-profile-table.cc b/third_party/tcmalloc/chromium/src/heap-profile-table.cc
index 7486468c..238d041 100644
--- a/third_party/tcmalloc/chromium/src/heap-profile-table.cc
+++ b/third_party/tcmalloc/chromium/src/heap-profile-table.cc
@@ -461,7 +461,10 @@
 void HeapProfileTable::CleanupOldProfiles(const char* prefix) {
   if (!FLAGS_cleanup_old_heap_profiles)
     return;
-  string pattern = string(prefix) + ".*" + kFileExt;
+  char buf[1000];
+  snprintf(buf, 1000,"%s.%05d.", prefix, getpid());
+  string pattern = string(buf) + ".*" + kFileExt;
+
 #if defined(HAVE_GLOB_H)
   glob_t g;
   const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g);
diff --git a/third_party/tcmalloc/chromium/src/heap-profiler.cc b/third_party/tcmalloc/chromium/src/heap-profiler.cc
index 33a25ac..b037f62 100755
--- a/third_party/tcmalloc/chromium/src/heap-profiler.cc
+++ b/third_party/tcmalloc/chromium/src/heap-profiler.cc
@@ -231,8 +231,8 @@
   // Make file name
   char file_name[1000];
   dump_count++;
-  snprintf(file_name, sizeof(file_name), "%s.%04d%s",
-           filename_prefix, dump_count, HeapProfileTable::kFileExt);
+  snprintf(file_name, sizeof(file_name), "%s.%05d.%04d%s",
+           filename_prefix, getpid(), dump_count, HeapProfileTable::kFileExt);
 
   // Dump the profile
   RAW_VLOG(0, "Dumping heap profile to %s (%s)", file_name, reason);
diff --git a/third_party/tcmalloc/chromium/src/symbolize.cc b/third_party/tcmalloc/chromium/src/symbolize.cc
index 88609ff..ab5bcad 100755
--- a/third_party/tcmalloc/chromium/src/symbolize.cc
+++ b/third_party/tcmalloc/chromium/src/symbolize.cc
@@ -72,11 +72,6 @@
               EnvToString("PPROF_PATH", "pprof"),
               "Path to pprof to call for reporting function names.");
 
-// heap_profile_table_pprof may be referenced after destructors are
-// called (since that's when leak-checking is done), so we make
-// a more-permanent copy that won't ever get destroyed.
-static string* g_pprof_path = new string(FLAGS_symbolize_pprof);
-
 // Returns NULL if we're on an OS where we can't get the invocation name.
 // Using a static var is ok because we're not called from a thread.
 static const char* GetProgramInvocationName() {
@@ -144,7 +139,7 @@
     PrintError("Cannot figure out the name of this executable (argv0)");
     return 0;
   }
-  if (access(g_pprof_path->c_str(), R_OK) != 0) {
+  if (access(FLAGS_symbolize_pprof, R_OK) != 0) {
     PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)");
     return 0;
   }
@@ -206,7 +201,7 @@
       unsetenv("HEAPPROFILE");
       unsetenv("HEAPCHECK");
       unsetenv("PERFTOOLS_VERBOSE");
-      execlp(g_pprof_path->c_str(), g_pprof_path->c_str(),
+      execlp(FLAGS_symbolize_pprof, FLAGS_symbolize_pprof,
              "--symbols", argv0, NULL);
       _exit(3);  // if execvp fails, it's bad news for us
     }
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index efa6e5c..39f2fbb 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -360,6 +360,11 @@
   "ash/app_list/resources/app_list_resources.grd": {
     "structures": [25900],
   },
+
+  "ash/login/resources/login_resources.grd": {
+    "structures": [25910],
+  },
+
   "ui/chromeos/resources/ui_chromeos_resources.grd": {
     "structures": [25920],
   },
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 32e2c2b..684e54f 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -105,9 +105,9 @@
       'CFI Linux CF': 'cfi_full_cfi_icall_cfi_diag_recover_release_static',
       'CFI Linux ToT': 'clang_tot_cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on',
       'CFI Linux (icall)': 'cfi_full_diag_icall_release_static_dcheck_always_on',
-      'CrWinAsan': 'asan_clang_fuzzer_static_v8_heap_x86_full_symbols_release',
-      'CrWinAsan(dll)': 'asan_clang_shared_v8_heap_x86_full_symbols_release',
-      'CrWinAsanCov': 'asan_clang_edge_fuzzer_static_v8_heap_x86_full_symbols_release',
+      'CrWinAsan': 'asan_clang_fuzzer_static_v8_heap_x86_release',
+      'CrWinAsan(dll)': 'asan_clang_shared_v8_heap_x86_release',
+      'CrWinAsanCov': 'asan_clang_edge_fuzzer_static_v8_heap_x86_release',
 
       'CrWinClangLLD': 'clang_tot_official_static_no_lld_x86',
       'CrWinClangLLD64': 'clang_tot_shared_release_no_lld_dcheck',
@@ -132,7 +132,7 @@
       'ToTMac': 'clang_tot_minimal_symbols_shared_release',
       'ToTMacCoverage': 'clang_tot_coverage_minimal_symbols_shared_release',
       'ToTMac (dbg)': 'clang_tot_shared_debug',
-      'ToTMacASan': 'asan_disable_nacl_clang_tot_full_symbols_static_release',
+      'ToTMacASan': 'asan_disable_nacl_clang_tot_static_release',
       'ToTWin': 'clang_tot_official_minimal_symbols_static_release_x86',
       'ToTWin(dbg)': 'clang_tot_shared_debug_x86',
       'ToTWin(dll)': 'clang_tot_minimal_symbols_shared_release_x86_dcheck',
@@ -218,6 +218,7 @@
 
       'Site Isolation Android': 'android_release_bot_minimal_symbols_arm64',
       'VR Linux': 'vr_release_bot',
+      'linux-tcmalloc-rel': 'tcmalloc_release_bot',
       'Win 10 Fast Ring': 'release_trybot',
       'Windows deterministic': 'release_bot_x86_minimal_symbols',
       'Windows Clang deterministic': 'clang_release_bot_minimal_symbols_x86',
@@ -360,7 +361,7 @@
       'Linux ChromiumOS MSan Builder': 'chromeos_msan_release_bot',
       'Linux MSan Builder': 'msan_release_bot',
       'Linux TSan Builder': 'tsan_disable_nacl_release_bot',
-      'Mac ASan 64 Builder': 'asan_full_symbols_disable_nacl_release_bot_dcheck_always_on',
+      'Mac ASan 64 Builder': 'asan_disable_nacl_release_bot_dcheck_always_on',
     },
 
     'chromium.perf': {
@@ -914,24 +915,24 @@
       'android_without_codecs', 'release_bot', 'minimal_symbols', 'strip_debug_info',
     ],
 
-    'asan_clang_edge_fuzzer_static_v8_heap_x86_full_symbols_release': [
-      'asan', 'clang_tot', 'edge', 'fuzzer', 'static', 'v8_heap', 'full_symbols', 'release', 'x64',
+    'asan_clang_edge_fuzzer_static_v8_heap_x86_release': [
+      'asan', 'clang_tot', 'edge', 'fuzzer', 'static', 'v8_heap', 'release', 'x64',
     ],
 
-    'asan_clang_shared_v8_heap_x86_full_symbols_release': [
-      'asan', 'clang_tot', 'shared', 'v8_heap', 'full_symbols', 'release', 'x64',
+    'asan_clang_shared_v8_heap_x86_release': [
+      'asan', 'clang_tot', 'shared', 'v8_heap', 'release', 'x64',
     ],
 
-    'asan_clang_fuzzer_static_v8_heap_x86_full_symbols_release': [
-      'asan', 'clang_tot', 'fuzzer', 'static', 'v8_heap', 'full_symbols', 'release', 'x64',
+    'asan_clang_fuzzer_static_v8_heap_x86_release': [
+      'asan', 'clang_tot', 'fuzzer', 'static', 'v8_heap', 'release', 'x64',
     ],
 
     'asan_dcheck_disable_nacl_release_bot': [
       'asan', 'dcheck_always_on', 'disable_nacl', 'release_bot',
     ],
 
-    'asan_disable_nacl_clang_tot_full_symbols_static_release': [
-      'asan', 'disable_nacl', 'clang_tot', 'full_symbols', 'static', 'release',
+    'asan_disable_nacl_clang_tot_static_release': [
+      'asan', 'disable_nacl', 'clang_tot', 'static', 'release',
     ],
 
     'asan_disable_nacl_edge_fuzzer_v8_heap_chrome_with_codecs_release_bot': [
@@ -958,8 +959,8 @@
       'asan', 'edge', 'v8_heap', 'debug_bot', 'hybrid',
     ],
 
-    'asan_full_symbols_disable_nacl_release_bot_dcheck_always_on': [
-      'asan', 'full_symbols', 'disable_nacl', 'release_bot', 'dcheck_always_on',
+    'asan_disable_nacl_release_bot_dcheck_always_on': [
+      'asan', 'disable_nacl', 'release_bot', 'dcheck_always_on',
     ],
 
     'asan_fuzzer_v8_heap_chrome_with_codecs_release_bot_x86': [
@@ -1261,11 +1262,11 @@
     ],
 
     'gpu_fyi_tests_release_trybot_asan': [
-      'gpu_fyi_tests', 'release_trybot', 'asan', 'full_symbols', 'disable_nacl',
+      'gpu_fyi_tests', 'release_trybot', 'asan', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot_tsan': [
-      'gpu_fyi_tests', 'release_trybot', 'tsan', 'full_symbols', 'disable_nacl',
+      'gpu_fyi_tests', 'release_trybot', 'tsan', 'disable_nacl',
     ],
 
     'gpu_fyi_tests_release_trybot_x86': [
@@ -1570,6 +1571,10 @@
       'release_bot', 'x86', 'minimal_symbols', 'no_clang', 'use_cxx11',
     ],
 
+    'tcmalloc_release_bot': [
+      'tcmalloc', 'release_bot',
+    ],
+
     'tsan_disable_nacl_debug_bot': [
       'tsan', 'disable_nacl', 'debug_bot',
     ],
@@ -2029,6 +2034,10 @@
       'gn_args': 'use_thin_lto=true',
     },
 
+    'tcmalloc': {
+      'gn_args': 'use_new_tcmalloc=true',
+    },
+
     'tsan': {
       'gn_args': 'is_tsan=true',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d943d1d..b447acc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13901,7 +13901,7 @@
   <int value="455" label="SafeSitesFilterBehavior"/>
   <int value="456" label="AllowedInputMethods"/>
   <int value="457" label="OverrideSecurityRestrictionsOnInsecureOrigin"/>
-  <int value="458" label="DeviceUpdateStagingPercentOfFleetPerWeek"/>
+  <int value="458" label="DeviceUpdateStagingSchedule"/>
   <int value="459" label="AutofillAddressEnabled"/>
   <int value="460" label="TabLifecyclesEnabled"/>
   <int value="461" label="UrlKeyedAnonymizedDataCollectionEnabled"/>
@@ -34691,6 +34691,25 @@
   <int value="2" label="Null"/>
 </enum>
 
+<enum name="NuxGoogleAppsInteractions">
+  <summary>
+    The button that a user pressed when interacting with the Google Apps NUX.
+  </summary>
+  <int value="0" label="Prompt Seen"/>
+  <int value="1" label="No Thanks"/>
+  <int value="2" label="Get Started"/>
+</enum>
+
+<enum name="NuxGoogleAppsSelections">
+  <summary>Google apps that can be added by the NUX.</summary>
+  <int value="0" label="Gmail"/>
+  <int value="1" label="YouTube"/>
+  <int value="2" label="Maps"/>
+  <int value="3" label="Translate"/>
+  <int value="4" label="News"/>
+  <int value="5" label="Chrome Web Store"/>
+</enum>
+
 <enum name="OAuth2LoginAccountRevokedMigrationState">
   <int value="0" label="Account ID migration not started"/>
   <int value="1" label="Account ID migration in progress"/>
@@ -41276,6 +41295,13 @@
   <int value="7" label="Error parsing certificate"/>
 </enum>
 
+<enum name="RulesetVerificationStatus">
+  <int value="0" label="Not verified"/>
+  <int value="1" label="Intact"/>
+  <int value="2" label="Corrupt"/>
+  <int value="3" label="Invalid File"/>
+</enum>
+
 <enum name="RunningMode">
   <obsolete>
     Removed 9/2016.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index dde109f..1b38927d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -31328,6 +31328,27 @@
   </summary>
 </histogram>
 
+<histogram name="FirstRun.NewUserExperience.GoogleAppsInteraction"
+    enum="NuxGoogleAppsInteractions">
+  <owner>hcarmona@chromium.org</owner>
+  <summary>
+    This histogram records when a user interacted with the Google Apps new user
+    experience. We record both accept and reject but are not recording when a
+    user ignored the prompt completely. Users can ignore this prompt by closing
+    the tab or navigating away, we record when the prompt is shown.
+  </summary>
+</histogram>
+
+<histogram name="FirstRun.NewUserExperience.GoogleAppsSelection"
+    enum="NuxGoogleAppsSelections">
+  <owner>hcarmona@chromium.org</owner>
+  <summary>
+    This histogram records what Google apps were selected as part of the Google
+    Apps New User Experience. Histogram is only recorded when a user accepts the
+    prompt to add Google Apps.
+  </summary>
+</histogram>
+
 <histogram name="FirstRun.Sentinel.Created" enum="FirstRunSentinelResult">
   <owner>jlebel@chromium.org</owner>
   <summary>Result of sentinel file has been written.</summary>
@@ -55608,6 +55629,13 @@
   </summary>
 </histogram>
 
+<histogram name="Net.SpdySession.ServerSupportsWebSocket" units="Boolean">
+  <owner>bnc@chromium.org</owner>
+  <summary>
+    Whether the server has advertised support for WebSockets over HTTP/2.
+  </summary>
+</histogram>
+
 <histogram name="Net.SpdySessionErrorDetails" enum="SpdyProtocolErrorDetails"
     units="count">
   <obsolete>
@@ -99223,6 +99251,18 @@
   </summary>
 </histogram>
 
+<histogram name="SubresourceFilter.RulesetVerificationStatus"
+    enum="RulesetVerificationStatus">
+  <owner>yaoxia@chromium.org</owner>
+  <summary>
+    Records the ruleset verification status at some point in a browsing session.
+    If AdTagging is disabled, this happens when the user first visits a site
+    that is failing the Better Ads Standard, and gets ads blocked on that site;
+    if AdTagging is enabled, this happens immediately on startup. Note that
+    subsequent failures will not be logged.
+  </summary>
+</histogram>
+
 <histogram name="SubresourceFilter.SafeBrowsing.CheckDispatchTime"
     units="microseconds">
   <obsolete>
@@ -127420,8 +127460,11 @@
       Deprecated as of 08/2015. Use Net.HttpJob.*Secure.Quic instead.
     </obsolete>
   </suffix>
-  <suffix name="Secure.NotQuic"
-      label="Recorded for Google servers (https) only when QUIC is not used."/>
+  <suffix name="Secure.NotQuic">
+    <obsolete>
+      Deprecated as of 08/2018. No longer used.
+    </obsolete>
+  </suffix>
   <suffix name="Secure.Quic"
       label="Recorded for Google servers (https) only when QUIC is used."/>
   <affected-histogram name="Net.HttpJob.TotalTime"/>
diff --git a/tools/translation/upload_screenshots.py b/tools/translation/upload_screenshots.py
index 4cef0a2..355d484 100755
--- a/tools/translation/upload_screenshots.py
+++ b/tools/translation/upload_screenshots.py
@@ -179,7 +179,8 @@
         num_threads=10,
         skip_hashing=False,
         gzip=None) != 0:
-      print 'Error uploading screenshots, exiting.'
+      print ('Error uploading screenshots. Try running '
+             '`download_from_google_storage --config`.')
       exit(1)
 
   print
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 0bea6fc..40df096 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -58,8 +58,7 @@
       client_(client),
       begin_frame_source_(this),
       enable_surface_synchronization_(enable_surface_synchronization),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       frame_evictor_(std::make_unique<viz::FrameEvictor>(this)) {
   DCHECK(view_);
   DCHECK(client_);
diff --git a/ui/aura/mus/drag_drop_controller_mus_unittest.cc b/ui/aura/mus/drag_drop_controller_mus_unittest.cc
index 34da908c..df9cbce 100644
--- a/ui/aura/mus/drag_drop_controller_mus_unittest.cc
+++ b/ui/aura/mus/drag_drop_controller_mus_unittest.cc
@@ -23,15 +23,13 @@
 namespace aura {
 namespace {
 
-class DragDropControllerMusTest : public test::AuraTestBase {
+class DragDropControllerMusTest : public test::AuraMusClientTestBase {
  public:
   DragDropControllerMusTest() = default;
 
-  // test::AuraMusClientTestBase
+  // test::AuraMusClientTestBase:
   void SetUp() override {
-    ConfigureEnvMode(Env::Mode::MUS);
-    SetCreateHostForPrimaryDisplay(true);
-    AuraTestBase::SetUp();
+    AuraMusClientTestBase::SetUp();
     controller_ = std::make_unique<DragDropControllerMus>(&controller_host_,
                                                           window_tree());
     window_ =
@@ -41,7 +39,7 @@
   void TearDown() override {
     window_.reset();
     controller_.reset();
-    AuraTestBase::TearDown();
+    AuraMusClientTestBase::TearDown();
   }
 
  protected:
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 5cf50e320..b69aed9 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -121,25 +121,6 @@
 
 }  // namespace
 
-// Trivial subclass of AuraTestBase that configures MUS *and* makes the test
-// create a WindowTreeHostMus.
-// TODO(sky): this is only necessary because AuraMusClientTestBase does not
-// create a default WindowTreeHost. Update AuraMusClientTestBase to create a
-// default WindowTreeHost and remove this. https://crbug.com/866634
-class WindowTreeClientWithHostTest : public test::AuraTestBase {
- public:
-  WindowTreeClientWithHostTest() = default;
-  ~WindowTreeClientWithHostTest() override = default;
-
-  void SetUp() override {
-    ConfigureEnvMode(Env::Mode::MUS);
-    AuraTestBase::SetUp();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WindowTreeClientWithHostTest);
-};
-
 class WindowTreeClientTest : public test::AuraMusClientTestBase {
  public:
   WindowTreeClientTest() = default;
@@ -186,7 +167,7 @@
 };
 
 class WindowTreeClientTestSurfaceSync
-    : public WindowTreeClientWithHostTest,
+    : public WindowTreeClientTest,
       public ::testing::WithParamInterface<bool> {
  public:
   WindowTreeClientTestSurfaceSync() {}
@@ -198,42 +179,13 @@
       base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
           switches::kForceDeviceScaleFactor, "2");
     }
-    WindowTreeClientWithHostTest::SetUp();
+    WindowTreeClientTest::SetUp();
   }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WindowTreeClientTestSurfaceSync);
 };
 
-// WindowTreeClientWithHostTest with --force-device-scale-factor=2.
-class WindowTreeClientWithHostTestHighDPI
-    : public WindowTreeClientWithHostTest {
- public:
-  WindowTreeClientWithHostTestHighDPI() {}
-  ~WindowTreeClientWithHostTestHighDPI() override {}
-
-  const ui::PointerEvent* last_event_observed() const {
-    return last_event_observed_.get();
-  }
-
-  // WindowTreeClientTest:
-  void SetUp() override {
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kForceDeviceScaleFactor, "2");
-    WindowTreeClientWithHostTest::SetUp();
-  }
-  void OnPointerEventObserved(const ui::PointerEvent& event,
-                              int64_t display_id,
-                              Window* target) override {
-    last_event_observed_.reset(new ui::PointerEvent(event));
-  }
-
- private:
-  std::unique_ptr<ui::PointerEvent> last_event_observed_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowTreeClientWithHostTestHighDPI);
-};
-
 // WindowTreeClientTest with --force-device-scale-factor=2.
 class WindowTreeClientTestHighDPI : public WindowTreeClientTest {
  public:
@@ -263,7 +215,7 @@
 };
 
 // Verifies bounds are reverted if the server replied that the change failed.
-TEST_F(WindowTreeClientWithHostTest, SetBoundsFailed) {
+TEST_F(WindowTreeClientTest, SetBoundsFailed) {
   Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
   const gfx::Rect original_bounds(window.bounds());
@@ -278,7 +230,7 @@
 
 // Verifies bounds and the viz::LocalSurfaceId associated with the bounds are
 // reverted if the server replied that the change failed.
-TEST_F(WindowTreeClientWithHostTest, SetBoundsFailedLocalSurfaceId) {
+TEST_F(WindowTreeClientTest, SetBoundsFailedLocalSurfaceId) {
   Window window(nullptr);
   // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds
   // when their sizes change.
@@ -461,7 +413,7 @@
 
 // Verifies a new window from the server doesn't result in attempting to add
 // the window back to the server.
-TEST_F(WindowTreeClientWithHostTest, AddFromServerDoesntAddAgain) {
+TEST_F(WindowTreeClientTest, AddFromServerDoesntAddAgain) {
   const ui::Id child_window_id = server_id(root_window()) + 11;
   ui::mojom::WindowDataPtr data = ui::mojom::WindowData::New();
   data->parent_id = server_id(root_window());
@@ -480,7 +432,7 @@
 }
 
 // Verifies a reparent from the server doesn't attempt signal the server.
-TEST_F(WindowTreeClientWithHostTest, ReparentFromServerDoesntAddAgain) {
+TEST_F(WindowTreeClientTest, ReparentFromServerDoesntAddAgain) {
   Window window1(nullptr);
   window1.Init(ui::LAYER_NOT_DRAWN);
   Window window2(nullptr);
@@ -501,7 +453,7 @@
 
 // Verifies properties passed in OnWindowHierarchyChanged() make there way to
 // the new window.
-TEST_F(WindowTreeClientWithHostTest, OnWindowHierarchyChangedWithProperties) {
+TEST_F(WindowTreeClientTest, OnWindowHierarchyChangedWithProperties) {
   RegisterTestProperties(GetPropertyConverter());
   window_tree()->AckAllChanges();
   const ui::Id child_window_id = server_id(root_window()) + 11;
@@ -537,7 +489,7 @@
 }
 
 // Verifies a move from the server doesn't attempt signal the server.
-TEST_F(WindowTreeClientWithHostTest, MoveFromServerDoesntAddAgain) {
+TEST_F(WindowTreeClientTest, MoveFromServerDoesntAddAgain) {
   Window window1(nullptr);
   window1.Init(ui::LAYER_NOT_DRAWN);
   Window window2(nullptr);
@@ -556,7 +508,7 @@
   EXPECT_EQ(&window1, root_window()->children()[1]);
 }
 
-TEST_F(WindowTreeClientWithHostTest, FocusFromServer) {
+TEST_F(WindowTreeClientTest, FocusFromServer) {
   Window window1(nullptr);
   window1.Init(ui::LAYER_NOT_DRAWN);
   Window window2(nullptr);
@@ -642,7 +594,7 @@
   EXPECT_EQ(original_bounds, root_window.bounds());
 }
 
-TEST_F(WindowTreeClientWithHostTest, TwoInFlightTransformsChangesBothCanceled) {
+TEST_F(WindowTreeClientTest, TwoInFlightTransformsChangesBothCanceled) {
   const gfx::Transform original_transform(root_window()->layer()->transform());
   gfx::Transform transform1;
   transform1.Scale(SkIntToMScalar(2), SkIntToMScalar(2));
@@ -668,7 +620,7 @@
 }
 
 // Verifies properties are set if the server replied that the change succeeded.
-TEST_F(WindowTreeClientWithHostTest, SetPropertySucceeded) {
+TEST_F(WindowTreeClientTest, SetPropertySucceeded) {
   ASSERT_FALSE(root_window()->GetProperty(client::kAlwaysOnTopKey));
   root_window()->SetProperty(client::kAlwaysOnTopKey, true);
   EXPECT_TRUE(root_window()->GetProperty(client::kAlwaysOnTopKey));
@@ -685,7 +637,7 @@
 
 // Verifies properties are reverted if the server replied that the change
 // failed.
-TEST_F(WindowTreeClientWithHostTest, SetPropertyFailed) {
+TEST_F(WindowTreeClientTest, SetPropertyFailed) {
   ASSERT_FALSE(root_window()->GetProperty(client::kAlwaysOnTopKey));
   root_window()->SetProperty(client::kAlwaysOnTopKey, true);
   EXPECT_TRUE(root_window()->GetProperty(client::kAlwaysOnTopKey));
@@ -702,7 +654,7 @@
 
 // Simulates a property change, and while the property change is in flight the
 // server replies with a new property and the original property change fails.
-TEST_F(WindowTreeClientWithHostTest, SetPropertyFailedWithPendingChange) {
+TEST_F(WindowTreeClientTest, SetPropertyFailedWithPendingChange) {
   RegisterTestProperties(GetPropertyConverter());
   const uint8_t value1 = 11;
   root_window()->SetProperty(kTestPropertyKey1, value1);
@@ -731,7 +683,7 @@
 }
 
 // Verifies property setting behavior with failures for primitive properties.
-TEST_F(WindowTreeClientWithHostTest, SetPrimitiveProperties) {
+TEST_F(WindowTreeClientTest, SetPrimitiveProperties) {
   PropertyConverter* property_converter = GetPropertyConverter();
   RegisterTestProperties(property_converter);
 
@@ -769,7 +721,7 @@
 }
 
 // Verifies property setting behavior for a gfx::Rect* property.
-TEST_F(WindowTreeClientWithHostTest, SetRectProperty) {
+TEST_F(WindowTreeClientTest, SetRectProperty) {
   gfx::Rect example(1, 2, 3, 4);
   ASSERT_EQ(nullptr, root_window()->GetProperty(client::kRestoreBoundsKey));
   root_window()->SetProperty(client::kRestoreBoundsKey, new gfx::Rect(example));
@@ -791,7 +743,7 @@
 }
 
 // Verifies property setting behavior for a std::string* property.
-TEST_F(WindowTreeClientWithHostTest, SetStringProperty) {
+TEST_F(WindowTreeClientTest, SetStringProperty) {
   std::string example = "123";
   ASSERT_NE(nullptr, root_window()->GetProperty(client::kNameKey));
   root_window()->SetProperty(client::kNameKey, new std::string(example));
@@ -812,7 +764,7 @@
 }
 
 // Verifies visible is reverted if the server replied that the change failed.
-TEST_F(WindowTreeClientWithHostTest, SetVisibleFailed) {
+TEST_F(WindowTreeClientTest, SetVisibleFailed) {
   const bool original_visible = root_window()->TargetVisibility();
   const bool new_visible = !original_visible;
   SetWindowVisibility(root_window(), new_visible);
@@ -823,7 +775,7 @@
 
 // Simulates a visible change, and while the visible change is in flight the
 // server replies with a new visible and the original visible change fails.
-TEST_F(WindowTreeClientWithHostTest, SetVisibleFailedWithPendingChange) {
+TEST_F(WindowTreeClientTest, SetVisibleFailedWithPendingChange) {
   const bool original_visible = root_window()->TargetVisibility();
   const bool new_visible = !original_visible;
   SetWindowVisibility(root_window(), new_visible);
@@ -1555,7 +1507,7 @@
 }
 
 // Verifies focus is reverted if the server replied that the change failed.
-TEST_F(WindowTreeClientWithHostTest, SetFocusFailed) {
+TEST_F(WindowTreeClientTest, SetFocusFailed) {
   Window child(nullptr);
   child.Init(ui::LAYER_NOT_DRAWN);
   root_window()->AddChild(&child);
@@ -1576,7 +1528,7 @@
 
 // Simulates a focus change, and while the focus change is in flight the server
 // replies with a new focus and the original focus change fails.
-TEST_F(WindowTreeClientWithHostTest, SetFocusFailedWithPendingChange) {
+TEST_F(WindowTreeClientTest, SetFocusFailedWithPendingChange) {
   Window child1(nullptr);
   child1.Init(ui::LAYER_NOT_DRAWN);
   root_window()->AddChild(&child1);
@@ -1608,8 +1560,7 @@
   EXPECT_TRUE(child1.HasFocus());
 }
 
-TEST_F(WindowTreeClientWithHostTest,
-       FocusOnRemovedWindowWithInFlightFocusChange) {
+TEST_F(WindowTreeClientTest, FocusOnRemovedWindowWithInFlightFocusChange) {
   std::unique_ptr<Window> child1(std::make_unique<Window>(nullptr));
   child1->Init(ui::LAYER_NOT_DRAWN);
   root_window()->AddChild(child1.get());
@@ -1663,7 +1614,7 @@
   DISALLOW_COPY_AND_ASSIGN(ToggleVisibilityFromDestroyedObserver);
 };
 
-TEST_F(WindowTreeClientWithHostTest, ToggleVisibilityFromWindowDestroyed) {
+TEST_F(WindowTreeClientTest, ToggleVisibilityFromWindowDestroyed) {
   std::unique_ptr<Window> child(std::make_unique<Window>(nullptr));
   child->Init(ui::LAYER_NOT_DRAWN);
   root_window()->AddChild(child.get());
@@ -2158,7 +2109,7 @@
 
 // Tests both SetCapture and ReleaseCapture, to ensure that Window is properly
 // updated on failures.
-TEST_F(WindowTreeClientWithHostTest, ExplicitCapture) {
+TEST_F(WindowTreeClientTest, ExplicitCapture) {
   root_window()->SetCapture();
   EXPECT_TRUE(root_window()->HasCapture());
   ASSERT_TRUE(window_tree()->AckSingleChangeOfType(
@@ -2185,7 +2136,7 @@
 
 // Tests that when capture is lost, while there is a release capture request
 // inflight, that the revert value of that request is updated correctly.
-TEST_F(WindowTreeClientWithHostTest, LostCaptureDifferentInFlightChange) {
+TEST_F(WindowTreeClientTest, LostCaptureDifferentInFlightChange) {
   root_window()->SetCapture();
   EXPECT_TRUE(root_window()->HasCapture());
   ASSERT_TRUE(window_tree()->AckSingleChangeOfType(
@@ -2205,7 +2156,7 @@
 
 // Tests that while two windows can inflight capture requests, that the
 // WindowTreeClient only identifies one as having the current capture.
-TEST_F(WindowTreeClientWithHostTest, TwoWindowsRequestCapture) {
+TEST_F(WindowTreeClientTest, TwoWindowsRequestCapture) {
   Window child(nullptr);
   child.Init(ui::LAYER_NOT_DRAWN);
   root_window()->AddChild(&child);
@@ -2232,8 +2183,7 @@
   EXPECT_FALSE(root_window()->HasCapture());
 }
 
-TEST_F(WindowTreeClientWithHostTest,
-       WindowDestroyedWhileTransientChildHasCapture) {
+TEST_F(WindowTreeClientTest, WindowDestroyedWhileTransientChildHasCapture) {
   std::unique_ptr<Window> transient_parent(std::make_unique<Window>(nullptr));
   transient_parent->Init(ui::LAYER_NOT_DRAWN);
   // Owned by |transient_parent|.
@@ -2305,7 +2255,7 @@
 
 }  // namespace
 
-TEST_F(WindowTreeClientWithHostTest, OnWindowTreeCaptureChanged) {
+TEST_F(WindowTreeClientTest, OnWindowTreeCaptureChanged) {
   CaptureRecorder capture_recorder(root_window());
 
   std::unique_ptr<Window> child1(std::make_unique<Window>(nullptr));
@@ -2462,8 +2412,7 @@
 
 // Verifies OnWindowHierarchyChanged() deals correctly with identifying existing
 // windows.
-TEST_F(WindowTreeClientWithHostTest,
-       OnWindowHierarchyChangedWithExistingWindow) {
+TEST_F(WindowTreeClientTest, OnWindowHierarchyChangedWithExistingWindow) {
   Window* window1 = new Window(nullptr);
   window1->Init(ui::LAYER_NOT_DRAWN);
   Window* window2 = new Window(nullptr);
@@ -2511,7 +2460,7 @@
   EXPECT_FALSE(window_tree()->has_change());
 }
 
-TEST_F(WindowTreeClientWithHostTestHighDPI, SetBounds) {
+TEST_F(WindowTreeClientTestHighDPI, SetBounds) {
   const gfx::Rect original_bounds(root_window()->bounds());
   const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100));
   ASSERT_NE(new_bounds, root_window()->bounds());
diff --git a/ui/aura/test/aura_mus_test_base.cc b/ui/aura/test/aura_mus_test_base.cc
index 87620d9a..7a83950 100644
--- a/ui/aura/test/aura_mus_test_base.cc
+++ b/ui/aura/test/aura_mus_test_base.cc
@@ -12,13 +12,7 @@
 AuraMusClientTestBase::~AuraMusClientTestBase() = default;
 
 void AuraMusClientTestBase::SetUp() {
-  // Run AuraTestBase::SetUp() first because it puts an InProcessContextFactory
-  // in env.
   ConfigureEnvMode(Env::Mode::MUS);
-  // Don't create a WindowTreeHost by default.
-  // TODO(sky): update tests so that this isn't necessary (make the test code
-  // always create a host for the primary display). https://crbug.com/866634
-  SetCreateHostForPrimaryDisplay(false);
   AuraTestBase::SetUp();
 }
 
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc
index d5158e8d..bb70f27 100644
--- a/ui/aura/test/aura_test_base.cc
+++ b/ui/aura/test/aura_test_base.cc
@@ -94,8 +94,6 @@
   helper_ = std::make_unique<AuraTestHelper>();
   if (env_mode_ == Env::Mode::MUS)
     helper_->EnableMusWithTestWindowTree(window_tree_client_delegate_);
-  helper_->set_create_host_for_primary_display(
-      create_host_for_primary_display_);
   helper_->SetUp(context_factory, context_factory_private);
 }
 
@@ -158,11 +156,6 @@
   return helper_->window_tree_client();
 }
 
-void AuraTestBase::SetCreateHostForPrimaryDisplay(bool value) {
-  DCHECK(!setup_called_);
-  create_host_for_primary_display_ = value;
-}
-
 void AuraTestBase::OnEmbed(
     std::unique_ptr<WindowTreeHostMus> window_tree_host) {}
 
diff --git a/ui/aura/test/aura_test_base.h b/ui/aura/test/aura_test_base.h
index 528716c..ef9bca5 100644
--- a/ui/aura/test/aura_test_base.h
+++ b/ui/aura/test/aura_test_base.h
@@ -93,10 +93,6 @@
     return observed_pointer_events_;
   }
 
-  // See AuraTestHelper for details on what this does. Must be called before
-  // SetUp().
-  void SetCreateHostForPrimaryDisplay(bool value);
-
   // WindowTreeClientDelegate:
   void OnEmbed(std::unique_ptr<WindowTreeHostMus> window_tree_host) override;
   void OnUnembed(Window* root) override;
@@ -120,7 +116,6 @@
   std::unique_ptr<AuraTestHelper> helper_;
   std::unique_ptr<AuraTestContextFactory> mus_context_factory_;
   std::vector<std::unique_ptr<ui::PointerEvent>> observed_pointer_events_;
-  bool create_host_for_primary_display_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(AuraTestBase);
 };
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index 18883c9e..ffc326c 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -20,6 +20,7 @@
 #include "ui/aura/test/mus/test_window_tree.h"
 #include "ui/aura/test/mus/test_window_tree_client_delegate.h"
 #include "ui/aura/test/mus/test_window_tree_client_setup.h"
+#include "ui/aura/test/mus/window_tree_client_private.h"
 #include "ui/aura/test/test_focus_client.h"
 #include "ui/aura/test/test_screen.h"
 #include "ui/aura/test/test_window_parenting_client.h"
@@ -146,30 +147,26 @@
 
   ui::InitializeInputMethodForTesting();
 
-  if (mode_ != Mode::MUS_DONT_CREATE_WINDOW_TREE_CLIENT) {
-    display::Screen* screen = display::Screen::GetScreen();
-    gfx::Size host_size(screen ? screen->GetPrimaryDisplay().GetSizeInPixel()
-                               : gfx::Size(800, 600));
-    // This must be reset before creating TestScreen, which sets up the display
-    // scale factor for this test iteration.
-    display::Display::ResetForceDeviceScaleFactorForTesting();
-    test_screen_.reset(TestScreen::Create(host_size, window_tree_client_));
-    if (!screen)
-      display::Screen::SetScreenInstance(test_screen_.get());
-    if (env_mode == Env::Mode::LOCAL || create_host_for_primary_display_) {
-      host_.reset(test_screen_->CreateHostForPrimaryDisplay());
-      host_->window()->SetEventTargeter(
-          std::unique_ptr<ui::EventTargeter>(new WindowTargeter()));
+  display::Screen* screen = display::Screen::GetScreen();
+  gfx::Size host_size(screen ? screen->GetPrimaryDisplay().GetSizeInPixel()
+                             : gfx::Size(800, 600));
+  // This must be reset before creating TestScreen, which sets up the display
+  // scale factor for this test iteration.
+  display::Display::ResetForceDeviceScaleFactorForTesting();
+  test_screen_.reset(TestScreen::Create(host_size, window_tree_client_));
+  if (!screen)
+    display::Screen::SetScreenInstance(test_screen_.get());
+  host_.reset(test_screen_->CreateHostForPrimaryDisplay());
+  host_->window()->SetEventTargeter(
+      std::unique_ptr<ui::EventTargeter>(new WindowTargeter()));
 
-      client::SetFocusClient(root_window(), focus_client_.get());
-      client::SetCaptureClient(root_window(), capture_client());
-      parenting_client_.reset(new TestWindowParentingClient(root_window()));
+  client::SetFocusClient(root_window(), focus_client_.get());
+  client::SetCaptureClient(root_window(), capture_client());
+  parenting_client_.reset(new TestWindowParentingClient(root_window()));
 
-      root_window()->Show();
-      // Ensure width != height so tests won't confuse them.
-      host()->SetBoundsInPixels(gfx::Rect(host_size));
-    }
-  }
+  root_window()->Show();
+  // Ensure width != height so tests won't confuse them.
+  host()->SetBoundsInPixels(gfx::Rect(host_size));
 
   if (mode_ == Mode::MUS_CREATE_WINDOW_TREE_CLIENT) {
     window_tree_client_->focus_synchronizer()->SetActiveFocusClient(
@@ -185,24 +182,18 @@
   teardown_called_ = true;
   parenting_client_.reset();
   env_window_tree_client_setter_.reset();
-  if (mode_ != Mode::MUS_DONT_CREATE_WINDOW_TREE_CLIENT && root_window()) {
-    client::SetFocusClient(root_window(), nullptr);
-    client::SetCaptureClient(root_window(), nullptr);
-    host_.reset();
+  client::SetFocusClient(root_window(), nullptr);
+  client::SetCaptureClient(root_window(), nullptr);
+  host_.reset();
 
-    if (display::Screen::GetScreen() == test_screen_.get())
-      display::Screen::SetScreenInstance(nullptr);
-    test_screen_.reset();
+  if (display::Screen::GetScreen() == test_screen_.get())
+    display::Screen::SetScreenInstance(nullptr);
+  test_screen_.reset();
 
-    window_tree_client_setup_.reset();
-    focus_client_.reset();
-    capture_client_.reset();
-  } else {
-    if (display::Screen::GetScreen() == test_screen_.get())
-      display::Screen::SetScreenInstance(nullptr);
-    test_screen_.reset();
-    window_tree_client_setup_.reset();
-  }
+  window_tree_client_setup_.reset();
+  focus_client_.reset();
+  capture_client_.reset();
+
   ui::GestureRecognizer::Reset();
   ui::ShutdownInputMethodForTesting();
 
diff --git a/ui/aura/test/aura_test_helper.h b/ui/aura/test/aura_test_helper.h
index 8b7fc06..bcb1c93 100644
--- a/ui/aura/test/aura_test_helper.h
+++ b/ui/aura/test/aura_test_helper.h
@@ -67,10 +67,6 @@
   // testing shutdown ordering.
   void DeleteWindowTreeClient();
 
-  void set_create_host_for_primary_display(bool value) {
-    create_host_for_primary_display_ = value;
-  }
-
   // Creates and initializes (shows and sizes) the RootWindow for use in tests.
   void SetUp(ui::ContextFactory* context_factory,
              ui::ContextFactoryPrivate* context_factory_private);
@@ -140,10 +136,6 @@
 
   WindowTreeClient* window_tree_client_ = nullptr;
 
-  // Whether SetUp() should create a WindowTreeHost for the primary display. The
-  // value of this is *not* used if Mode is MUS.
-  bool create_host_for_primary_display_ = true;
-
   DISALLOW_COPY_AND_ASSIGN(AuraTestHelper);
 };
 
diff --git a/ui/aura/test/mus/window_tree_client_private.cc b/ui/aura/test/mus/window_tree_client_private.cc
index c7b7a7ce..f996010 100644
--- a/ui/aura/test/mus/window_tree_client_private.cc
+++ b/ui/aura/test/mus/window_tree_client_private.cc
@@ -104,7 +104,13 @@
 ui::mojom::WindowDataPtr WindowTreeClientPrivate::CreateWindowDataForEmbed() {
   ui::mojom::WindowDataPtr root_data(ui::mojom::WindowData::New());
   root_data->parent_id = 0;
-  root_data->window_id = next_window_id_++;
+  // OnEmbed() is passed windows the client doesn't own. Use a |client_id| of 1
+  // to mirror what the server does for the client-id portion, and use the
+  // number of roots for the window id. The important part is the combination is
+  // unique to the client.
+  const ui::Id client_id = 1;
+  root_data->window_id =
+      (client_id << 32) | tree_client_impl_->GetRoots().size();
   root_data->visible = true;
   return root_data;
 }
diff --git a/ui/aura/test/mus/window_tree_client_private.h b/ui/aura/test/mus/window_tree_client_private.h
index 27cc057..43600c94 100644
--- a/ui/aura/test/mus/window_tree_client_private.h
+++ b/ui/aura/test/mus/window_tree_client_private.h
@@ -73,7 +73,6 @@
   ui::mojom::WindowDataPtr CreateWindowDataForEmbed();
 
   WindowTreeClient* tree_client_impl_;
-  uint16_t next_window_id_ = 1u;
 
   DISALLOW_COPY_AND_ASSIGN(WindowTreeClientPrivate);
 };
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 1550f531..6db737e 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -90,8 +90,8 @@
     "gpu/page_flip_request.h",
     "gpu/proxy_helpers.cc",
     "gpu/proxy_helpers.h",
-    "gpu/scanout_buffer.h",
-    "gpu/scanout_buffer_generator.h",
+    "gpu/drm_framebuffer.h",
+    "gpu/drm_framebuffer_generator.h",
     "gpu/screen_manager.cc",
     "gpu/screen_manager.h",
     "host/drm_cursor.cc",
@@ -186,10 +186,10 @@
     "gpu/mock_drm_device.h",
     "gpu/mock_dumb_buffer_generator.cc",
     "gpu/mock_dumb_buffer_generator.h",
-    "gpu/mock_scanout_buffer.cc",
-    "gpu/mock_scanout_buffer.h",
-    "gpu/mock_scanout_buffer_generator.cc",
-    "gpu/mock_scanout_buffer_generator.h",
+    "gpu/mock_drm_framebuffer.cc",
+    "gpu/mock_drm_framebuffer.h",
+    "gpu/mock_drm_framebuffer_generator.cc",
+    "gpu/mock_drm_framebuffer_generator.h",
     "gpu/proxy_helpers_unittest.cc",
     "gpu/screen_manager_unittest.cc",
   ]
diff --git a/ui/ozone/platform/drm/gpu/crtc_controller.cc b/ui/ozone/platform/drm/gpu/crtc_controller.cc
index 6013815..75a4d2d 100644
--- a/ui/ozone/platform/drm/gpu/crtc_controller.cc
+++ b/ui/ozone/platform/drm/gpu/crtc_controller.cc
@@ -7,10 +7,11 @@
 #include "base/logging.h"
 #include "base/time/time.h"
 #include "ui/gfx/presentation_feedback.h"
+#include "ui/ozone/platform/drm/gpu/drm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 
@@ -91,7 +92,7 @@
   return drm_->plane_manager()->GetFormatModifiers(crtc_, format);
 }
 
-bool CrtcController::SetCursor(const scoped_refptr<ScanoutBuffer>& buffer) {
+bool CrtcController::SetCursor(const scoped_refptr<DrmBuffer>& buffer) {
   DCHECK(!is_disabled_ || !buffer);
   cursor_buffer_ = buffer;
 
diff --git a/ui/ozone/platform/drm/gpu/crtc_controller.h b/ui/ozone/platform/drm/gpu/crtc_controller.h
index e7b34368..13879b5 100644
--- a/ui/ozone/platform/drm/gpu/crtc_controller.h
+++ b/ui/ozone/platform/drm/gpu/crtc_controller.h
@@ -20,6 +20,7 @@
 
 namespace ui {
 
+class DrmBuffer;
 class DrmDevice;
 
 // Wrapper around a CRTC.
@@ -60,8 +61,7 @@
   // gbm will pick a modifier as it allocates the bo.
   std::vector<uint64_t> GetFormatModifiers(uint32_t fourcc_format);
 
-
-  bool SetCursor(const scoped_refptr<ScanoutBuffer>& buffer);
+  bool SetCursor(const scoped_refptr<DrmBuffer>& buffer);
   bool MoveCursor(const gfx::Point& location);
 
  private:
@@ -71,7 +71,7 @@
 
   // Buffers need to be declared first so that they are destroyed last. Needed
   // since the controllers may reference the buffers.
-  scoped_refptr<ScanoutBuffer> cursor_buffer_;
+  scoped_refptr<DrmBuffer> cursor_buffer_;
 
   uint32_t crtc_;
 
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.cc b/ui/ozone/platform/drm/gpu/drm_buffer.cc
index 8cf70a8b..e827bb6 100644
--- a/ui/ozone/platform/drm/gpu/drm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/drm_buffer.cc
@@ -89,6 +89,10 @@
   return surface_->getCanvas();
 }
 
+uint32_t DrmBuffer::GetHandle() const {
+  return handle_;
+}
+
 uint32_t DrmBuffer::GetFramebufferId() const {
   return framebuffer_;
 }
@@ -109,20 +113,12 @@
   return DRM_FORMAT_MOD_NONE;
 }
 
-uint32_t DrmBuffer::GetHandle() const {
-  return handle_;
-}
-
 gfx::Size DrmBuffer::GetSize() const {
   return gfx::Size(surface_->width(), surface_->height());
 }
 
-const GbmDeviceLinux* DrmBuffer::GetGbmDeviceLinux() const {
-  return drm_->AsGbmDeviceLinux();
-}
-
-bool DrmBuffer::RequiresGlFinish() const {
-  return false;
+const DrmDevice* DrmBuffer::GetDrmDevice() const {
+  return drm_.get();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.h b/ui/ozone/platform/drm/gpu/drm_buffer.h
index fdf4397..1edb2e9 100644
--- a/ui/ozone/platform/drm/gpu/drm_buffer.h
+++ b/ui/ozone/platform/drm/gpu/drm_buffer.h
@@ -10,7 +10,7 @@
 
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 
 class SkCanvas;
 struct SkImageInfo;
@@ -19,12 +19,11 @@
 namespace ui {
 
 class DrmDevice;
-class GbmDeviceLinux;
 
 // Wrapper for a DRM allocated buffer. Keeps track of the native properties of
 // the buffer and wraps the pixel memory into a SkSurface which can be used to
 // draw into using Skia.
-class DrmBuffer : public ScanoutBuffer {
+class DrmBuffer : public DrmFramebuffer {
  public:
   DrmBuffer(const scoped_refptr<DrmDevice>& drm);
 
@@ -36,16 +35,16 @@
 
   SkCanvas* GetCanvas() const;
 
-  // ScanoutBuffer:
+  uint32_t GetHandle() const;
+
+  // DrmFramebuffer:
   uint32_t GetFramebufferId() const override;
   uint32_t GetFramebufferPixelFormat() const override;
   uint32_t GetOpaqueFramebufferId() const override;
   uint32_t GetOpaqueFramebufferPixelFormat() const override;
   uint64_t GetFormatModifier() const override;
-  uint32_t GetHandle() const override;
   gfx::Size GetSize() const override;
-  const GbmDeviceLinux* GetGbmDeviceLinux() const override;
-  bool RequiresGlFinish() const override;
+  const DrmDevice* GetDrmDevice() const override;
 
  protected:
   ~DrmBuffer() override;
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h
index 9053e17..7ba05828 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.h
+++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -80,8 +80,6 @@
 
   bool allow_addfb2_modifiers() const { return allow_addfb2_modifiers_; }
 
-  const GbmDeviceLinux* AsGbmDeviceLinux() const { return this; }
-
   // Open device.
   virtual bool Initialize();
 
diff --git a/ui/ozone/platform/drm/gpu/scanout_buffer.h b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
similarity index 79%
rename from ui/ozone/platform/drm/gpu/scanout_buffer.h
rename to ui/ozone/platform/drm/gpu/drm_framebuffer.h
index e396d54..b52d3e7 100644
--- a/ui/ozone/platform/drm/gpu/scanout_buffer.h
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
@@ -13,10 +13,10 @@
 
 namespace ui {
 
-class GbmDeviceLinux;
+class DrmDevice;
 
 // Abstraction for a DRM buffer that can be scanned-out of.
-class ScanoutBuffer : public base::RefCountedThreadSafe<ScanoutBuffer> {
+class DrmFramebuffer : public base::RefCountedThreadSafe<DrmFramebuffer> {
  public:
   // ID allocated by the KMS API when the buffer is registered (via the handle).
   virtual uint32_t GetFramebufferId() const = 0;
@@ -37,21 +37,16 @@
   // Returns format modifier for buffer.
   virtual uint64_t GetFormatModifier() const = 0;
 
-  // Handle for the buffer. This is received when allocating the buffer.
-  virtual uint32_t GetHandle() const = 0;
-
   // Size of the buffer.
   virtual gfx::Size GetSize() const = 0;
 
   // Device on which the buffer was created.
-  virtual const GbmDeviceLinux* GetGbmDeviceLinux() const = 0;
-
-  virtual bool RequiresGlFinish() const = 0;
+  virtual const DrmDevice* GetDrmDevice() const = 0;
 
  protected:
-  virtual ~ScanoutBuffer() {}
+  virtual ~DrmFramebuffer() {}
 
-  friend class base::RefCountedThreadSafe<ScanoutBuffer>;
+  friend class base::RefCountedThreadSafe<DrmFramebuffer>;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/scanout_buffer_generator.h b/ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h
similarity index 82%
rename from ui/ozone/platform/drm/gpu/scanout_buffer_generator.h
rename to ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h
index 266cc7e..04d8847 100644
--- a/ui/ozone/platform/drm/gpu/scanout_buffer_generator.h
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h
@@ -13,13 +13,13 @@
 namespace ui {
 
 class DrmDevice;
-class ScanoutBuffer;
+class DrmFramebuffer;
 
-class ScanoutBufferGenerator {
+class DrmFramebufferGenerator {
  public:
-  virtual ~ScanoutBufferGenerator() {}
+  virtual ~DrmFramebufferGenerator() {}
 
-  virtual scoped_refptr<ScanoutBuffer> Create(
+  virtual scoped_refptr<DrmFramebuffer> Create(
       const scoped_refptr<DrmDevice>& drm,
       uint32_t format,
       const std::vector<uint64_t>& modifiers,
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc b/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc
index 0933974b..cf5a5fcf 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc
@@ -7,7 +7,7 @@
 #include <stddef.h>
 
 #include "ui/gfx/gpu_fence.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 
 namespace ui {
 
@@ -23,7 +23,7 @@
 
 }  // namespace
 
-DrmOverlayPlane::DrmOverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer,
+DrmOverlayPlane::DrmOverlayPlane(const scoped_refptr<DrmFramebuffer>& buffer,
                                  std::unique_ptr<gfx::GpuFence> gpu_fence)
     : buffer(buffer),
       plane_transform(gfx::OVERLAY_TRANSFORM_NONE),
@@ -32,7 +32,7 @@
       enable_blend(false),
       gpu_fence(std::move(gpu_fence)) {}
 
-DrmOverlayPlane::DrmOverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer,
+DrmOverlayPlane::DrmOverlayPlane(const scoped_refptr<DrmFramebuffer>& buffer,
                                  int z_order,
                                  gfx::OverlayTransform plane_transform,
                                  const gfx::Rect& display_bounds,
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_plane.h b/ui/ozone/platform/drm/gpu/drm_overlay_plane.h
index 5af1f7b7..41824c9 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_plane.h
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_plane.h
@@ -19,17 +19,17 @@
 
 namespace ui {
 
-class ScanoutBuffer;
+class DrmFramebuffer;
 
 struct DrmOverlayPlane;
 typedef std::vector<DrmOverlayPlane> DrmOverlayPlaneList;
 
 struct DrmOverlayPlane {
   // Simpler constructor for the primary plane.
-  explicit DrmOverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer,
+  explicit DrmOverlayPlane(const scoped_refptr<DrmFramebuffer>& buffer,
                            std::unique_ptr<gfx::GpuFence> gpu_fence);
 
-  DrmOverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer,
+  DrmOverlayPlane(const scoped_refptr<DrmFramebuffer>& buffer,
                   int z_order,
                   gfx::OverlayTransform plane_transform,
                   const gfx::Rect& display_bounds,
@@ -52,7 +52,7 @@
   static std::vector<DrmOverlayPlane> Clone(
       const std::vector<DrmOverlayPlane>& planes);
 
-  scoped_refptr<ScanoutBuffer> buffer;
+  scoped_refptr<DrmFramebuffer> buffer;
   int z_order = 0;
   gfx::OverlayTransform plane_transform;
   gfx::Rect display_bounds;
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
index bb25d97..8d4b609 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
@@ -12,21 +12,21 @@
 #include "ui/ozone/common/linux/drm_util_linux.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
 
 namespace ui {
 
 namespace {
 
-scoped_refptr<ScanoutBuffer> GetBufferForPageFlipTest(
+scoped_refptr<DrmFramebuffer> GetBufferForPageFlipTest(
     const scoped_refptr<DrmDevice>& drm_device,
     const gfx::Size& size,
     uint32_t format,
-    ScanoutBufferGenerator* buffer_generator,
-    std::vector<scoped_refptr<ScanoutBuffer>>* reusable_buffers) {
+    DrmFramebufferGenerator* buffer_generator,
+    std::vector<scoped_refptr<DrmFramebuffer>>* reusable_buffers) {
   // Check if we can re-use existing buffers.
   for (const auto& buffer : *reusable_buffers) {
     if (buffer->GetFramebufferPixelFormat() == format &&
@@ -37,19 +37,19 @@
 
   const std::vector<uint64_t>
       modifiers;  // TODO(dcastagna): use the right modifiers.
-  scoped_refptr<ScanoutBuffer> scanout_buffer =
+  scoped_refptr<DrmFramebuffer> drm_framebuffer =
       buffer_generator->Create(drm_device, format, modifiers, size);
-  if (scanout_buffer)
-    reusable_buffers->push_back(scanout_buffer);
+  if (drm_framebuffer)
+    reusable_buffers->push_back(drm_framebuffer);
 
-  return scanout_buffer;
+  return drm_framebuffer;
 }
 
 }  // namespace
 
 DrmOverlayValidator::DrmOverlayValidator(
     DrmWindow* window,
-    ScanoutBufferGenerator* buffer_generator)
+    DrmFramebufferGenerator* buffer_generator)
     : window_(window), buffer_generator_(buffer_generator) {}
 
 DrmOverlayValidator::~DrmOverlayValidator() {}
@@ -68,7 +68,7 @@
   }
 
   DrmOverlayPlaneList test_list;
-  std::vector<scoped_refptr<ScanoutBuffer>> reusable_buffers;
+  std::vector<scoped_refptr<DrmFramebuffer>> reusable_buffers;
   scoped_refptr<DrmDevice> drm = controller->GetDrmDevice();
 
   for (const auto& plane : last_used_planes)
@@ -80,10 +80,10 @@
       continue;
     }
 
-    scoped_refptr<ScanoutBuffer> buffer =
-        GetBufferForPageFlipTest(drm, params[i].buffer_size,
-                                 GetFourCCFormatFromBufferFormat(params[i].format),
-                                 buffer_generator_, &reusable_buffers);
+    scoped_refptr<DrmFramebuffer> buffer = GetBufferForPageFlipTest(
+        drm, params[i].buffer_size,
+        GetFourCCFormatFromBufferFormat(params[i].format), buffer_generator_,
+        &reusable_buffers);
 
     DrmOverlayPlane plane(buffer, params[i].plane_z_order, params[i].transform,
                           params[i].display_rect, params[i].crop_rect,
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
index ecb83809..1304559 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.h
@@ -11,14 +11,14 @@
 namespace ui {
 
 class DrmWindow;
-class ScanoutBufferGenerator;
+class DrmFramebufferGenerator;
 struct OverlayCheck_Params;
 struct OverlayCheckReturn_Params;
 
 class DrmOverlayValidator {
  public:
   DrmOverlayValidator(DrmWindow* window,
-                      ScanoutBufferGenerator* buffer_generator);
+                      DrmFramebufferGenerator* buffer_generator);
   ~DrmOverlayValidator();
 
   // Tests if configurations |params| are compatible with |window_| and finds
@@ -30,7 +30,7 @@
 
  private:
   DrmWindow* window_;  // Not owned.
-  ScanoutBufferGenerator* buffer_generator_;  // Not owned.
+  DrmFramebufferGenerator* buffer_generator_;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(DrmOverlayValidator);
 };
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
index 03da115..d25ef4a 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -22,8 +22,8 @@
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 
 namespace {
@@ -55,8 +55,8 @@
     last_swap_buffers_result_ = result;
   }
 
-  scoped_refptr<ui::ScanoutBuffer> ReturnNullBuffer(const gfx::Size& size,
-                                                    uint32_t format) {
+  scoped_refptr<ui::DrmFramebuffer> ReturnNullBuffer(const gfx::Size& size,
+                                                     uint32_t format) {
     return nullptr;
   }
 
@@ -75,7 +75,7 @@
 
   std::unique_ptr<base::MessageLoop> message_loop_;
   scoped_refptr<ui::MockDrmDevice> drm_;
-  std::unique_ptr<ui::MockScanoutBufferGenerator> buffer_generator_;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::ScreenManager> screen_manager_;
   std::unique_ptr<ui::DrmDeviceManager> drm_device_manager_;
   ui::DrmWindow* window_;
@@ -104,7 +104,7 @@
                           }};
   InitializeDrmState({crtc_state});
 
-  buffer_generator_.reset(new ui::MockScanoutBufferGenerator());
+  buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
   screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
   screen_manager_->AddDisplayController(drm_, kCrtcIdBase, kConnectorIdBase);
   screen_manager_->ConfigureDisplayController(
@@ -206,11 +206,11 @@
 
 void DrmOverlayValidatorTest::AddPlane(const ui::OverlayCheck_Params& params) {
   scoped_refptr<ui::DrmDevice> drm = window_->GetController()->GetDrmDevice();
-  scoped_refptr<ui::ScanoutBuffer> scanout_buffer = buffer_generator_->Create(
+  scoped_refptr<ui::DrmFramebuffer> drm_framebuffer = buffer_generator_->Create(
       drm, ui::GetFourCCFormatFromBufferFormat(params.format), {},
       params.buffer_size);
   plane_list_.push_back(ui::DrmOverlayPlane(
-      std::move(scanout_buffer), params.plane_z_order, params.transform,
+      std::move(drm_framebuffer), params.plane_z_order, params.transform,
       params.display_rect, params.crop_rect, true, nullptr));
 }
 
@@ -361,8 +361,8 @@
       std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
           drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
   ui::DrmOverlayPlane plane1(
-      scoped_refptr<ui::ScanoutBuffer>(
-          new ui::MockScanoutBuffer(primary_rect_.size())),
+      scoped_refptr<ui::DrmFramebuffer>(
+          new ui::MockDrmFramebuffer(primary_rect_.size())),
       nullptr);
   EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
 
@@ -430,8 +430,8 @@
       std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
           drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
   ui::DrmOverlayPlane plane1(
-      scoped_refptr<ui::ScanoutBuffer>(
-          new ui::MockScanoutBuffer(primary_rect_.size())),
+      scoped_refptr<ui::DrmFramebuffer>(
+          new ui::MockDrmFramebuffer(primary_rect_.size())),
       nullptr);
   EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
 
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 0c40c3e..b32d4da 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -20,6 +20,7 @@
 #include "ui/ozone/platform/drm/gpu/drm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
@@ -27,7 +28,6 @@
 #include "ui/ozone/platform/drm/gpu/gbm_device.h"
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
 #include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 #include "ui/ozone/public/ozone_switches.h"
 
@@ -35,16 +35,16 @@
 
 namespace {
 
-class GbmBufferGenerator : public ScanoutBufferGenerator {
+class GbmBufferGenerator : public DrmFramebufferGenerator {
  public:
   GbmBufferGenerator() {}
   ~GbmBufferGenerator() override {}
 
-  // ScanoutBufferGenerator:
-  scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
-                                      uint32_t format,
-                                      const std::vector<uint64_t>& modifiers,
-                                      const gfx::Size& size) override {
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override {
     scoped_refptr<GbmDevice> gbm(static_cast<GbmDevice*>(drm.get()));
     if (modifiers.size() > 0) {
       return GbmBuffer::CreateBufferWithModifiers(
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index 9b53b49..e5fdc5069 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -43,7 +43,7 @@
 class DrmDeviceManager;
 class DrmGpuDisplayManager;
 class GbmBuffer;
-class ScanoutBufferGenerator;
+class DrmFramebufferGenerator;
 class ScreenManager;
 
 struct DrmOverlayPlane;
@@ -150,7 +150,7 @@
                                 std::vector<DrmOverlayPlane> planes);
 
   std::unique_ptr<DrmDeviceManager> device_manager_;
-  std::unique_ptr<ScanoutBufferGenerator> buffer_generator_;
+  std::unique_ptr<DrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ScreenManager> screen_manager_;
   std::unique_ptr<DrmGpuDisplayManager> display_manager_;
 
diff --git a/ui/ozone/platform/drm/gpu/drm_window.cc b/ui/ozone/platform/drm/gpu/drm_window.cc
index 3445ce59..f35e8ac 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window.cc
@@ -51,7 +51,7 @@
 DrmWindow::~DrmWindow() {
 }
 
-void DrmWindow::Initialize(ScanoutBufferGenerator* buffer_generator) {
+void DrmWindow::Initialize(DrmFramebufferGenerator* buffer_generator) {
   TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_);
 
   device_manager_->UpdateDrmDevice(widget_, nullptr);
@@ -91,10 +91,11 @@
   cursor_frame_delay_ms_ = frame_delay_ms;
   cursor_timer_.Stop();
 
-  if (cursor_frame_delay_ms_)
+  if (cursor_frame_delay_ms_) {
     cursor_timer_.Start(
         FROM_HERE, base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_),
         this, &DrmWindow::OnCursorAnimationTimeout);
+  }
 
   ResetCursor(false);
 }
@@ -122,8 +123,7 @@
   if (controller_) {
     const DrmDevice* drm = controller_->GetDrmDevice().get();
     for (const auto& plane : planes) {
-      if (plane.buffer &&
-          plane.buffer->GetGbmDeviceLinux() != drm->AsGbmDeviceLinux()) {
+      if (plane.buffer && plane.buffer->GetDrmDevice() != drm) {
         // Although |force_buffer_reallocation_| is set to true during window
         // bounds update, this may still be needed because of in-flight buffers.
         force_buffer_reallocation_ = true;
diff --git a/ui/ozone/platform/drm/gpu/drm_window.h b/ui/ozone/platform/drm/gpu/drm_window.h
index 9abb8e4..e7b7c4d 100644
--- a/ui/ozone/platform/drm/gpu/drm_window.h
+++ b/ui/ozone/platform/drm/gpu/drm_window.h
@@ -33,7 +33,7 @@
 class HardwareDisplayController;
 struct OverlayCheck_Params;
 struct OverlayCheckReturn_Params;
-class ScanoutBufferGenerator;
+class DrmFramebufferGenerator;
 class ScreenManager;
 
 // The GPU object representing a window.
@@ -55,7 +55,7 @@
 
   gfx::Rect bounds() const { return bounds_; }
 
-  void Initialize(ScanoutBufferGenerator* buffer_generator);
+  void Initialize(DrmFramebufferGenerator* buffer_generator);
 
   void Shutdown();
 
diff --git a/ui/ozone/platform/drm/gpu/drm_window_proxy.cc b/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
index e516ae1..a72300c 100644
--- a/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window_proxy.cc
@@ -6,10 +6,10 @@
 
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/presentation_feedback.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread.h"
 #include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 
diff --git a/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
index 15ae668f..22bc36b 100644
--- a/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
@@ -23,10 +23,10 @@
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
 #include "ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
index 36a9499..9c3af9d 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -109,10 +109,6 @@
   return gbm_bo_.format_modifier();
 }
 
-uint32_t GbmBuffer::GetHandle() const {
-  return gbm_bo_.GetBoHandle();
-}
-
 gfx::Size GbmBuffer::GetSize() const {
   return gbm_bo_.size();
 }
@@ -127,12 +123,8 @@
   return opaque_framebuffer_pixel_format_;
 }
 
-const GbmDeviceLinux* GbmBuffer::GetGbmDeviceLinux() const {
-  return drm_->AsGbmDeviceLinux();
-}
-
-bool GbmBuffer::RequiresGlFinish() const {
-  return !drm_->is_primary_device();
+const DrmDevice* GbmBuffer::GetDrmDevice() const {
+  return drm_.get();
 }
 
 scoped_refptr<GbmBuffer> GbmBuffer::CreateBufferForBO(
@@ -325,7 +317,7 @@
 }
 
 uint32_t GbmPixmap::GetUniqueId() const {
-  return buffer_->GetHandle();
+  return buffer_->gbm_bo()->GetBoHandle();
 }
 
 bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.h b/ui/ozone/platform/drm/gpu/gbm_buffer.h
index ea582ff..0fed4676 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.h
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.h
@@ -9,7 +9,7 @@
 
 #include "ui/gfx/native_pixmap.h"
 #include "ui/ozone/common/linux/gbm_bo_wrapper.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 
 struct gbm_bo;
 
@@ -18,7 +18,7 @@
 class GbmDevice;
 class GbmSurfaceFactory;
 
-class GbmBuffer : public ScanoutBuffer {
+class GbmBuffer : public DrmFramebuffer {
  public:
   static constexpr uint32_t kFlagNoModifiers = 1U << 0;
 
@@ -42,16 +42,14 @@
 
   const GbmBoWrapper* gbm_bo() const { return &gbm_bo_; }
 
-  // ScanoutBuffer:
+  // DrmFramebuffer:
   uint32_t GetFramebufferId() const override;
   uint32_t GetOpaqueFramebufferId() const override;
   uint32_t GetFramebufferPixelFormat() const override;
   uint32_t GetOpaqueFramebufferPixelFormat() const override;
   uint64_t GetFormatModifier() const override;
-  uint32_t GetHandle() const override;
   gfx::Size GetSize() const override;
-  const GbmDeviceLinux* GetGbmDeviceLinux() const override;
-  bool RequiresGlFinish() const override;
+  const DrmDevice* GetDrmDevice() const override;
 
  private:
   GbmBuffer(const scoped_refptr<GbmDevice>& gbm,
@@ -115,8 +113,8 @@
 
  private:
   ~GbmPixmap() override;
-  scoped_refptr<ScanoutBuffer> ProcessBuffer(const gfx::Size& size,
-                                             uint32_t format);
+  scoped_refptr<DrmFramebuffer> ProcessBuffer(const gfx::Size& size,
+                                              uint32_t format);
 
   GbmSurfaceFactory* surface_manager_;
   scoped_refptr<GbmBuffer> buffer_;
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 4c0ff77b..baca67c9e 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -13,10 +13,11 @@
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/ozone/common/egl_util.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h"
 #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h"
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 
@@ -45,7 +46,8 @@
 }
 
 void GbmSurfaceless::QueueOverlayPlane(DrmOverlayPlane plane) {
-  is_on_external_drm_device_ = plane.buffer->RequiresGlFinish();
+  is_on_external_drm_device_ =
+      !plane.buffer->GetDrmDevice()->is_primary_device();
   planes_.push_back(std::move(plane));
 }
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
index ea49fe87..e9bb5e6 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.cc
@@ -202,7 +202,7 @@
 }
 
 bool HardwareDisplayController::SetCursor(
-    const scoped_refptr<ScanoutBuffer>& buffer) {
+    const scoped_refptr<DrmBuffer>& buffer) {
   bool status = true;
 
   if (is_disabled_)
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
index 98127c87..5975d5e 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
@@ -29,7 +29,8 @@
 namespace ui {
 
 class CrtcController;
-class ScanoutBuffer;
+class DrmFramebuffer;
+class DrmBuffer;
 class DrmDevice;
 
 // The HDCOz will handle modesettings and scannout operations for hardware
@@ -137,7 +138,7 @@
       uint32_t fourcc_format);
 
   // Set the hardware cursor to show the contents of |surface|.
-  bool SetCursor(const scoped_refptr<ScanoutBuffer>& buffer);
+  bool SetCursor(const scoped_refptr<DrmBuffer>& buffer);
 
   bool UnsetCursor();
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
index 007cf64..b453f30 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc
@@ -17,7 +17,7 @@
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h"
 
 namespace {
 
@@ -173,8 +173,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, CheckModesettingResult) {
-  ui::DrmOverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(
-                                new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane(scoped_refptr<ui::DrmFramebuffer>(
+                                new ui::MockDrmFramebuffer(kDefaultModeSize)),
                             nullptr);
 
   EXPECT_TRUE(controller_->Modeset(plane, kDefaultMode));
@@ -182,14 +182,14 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
 
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
 
-  ui::DrmOverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   std::vector<ui::DrmOverlayPlane> planes;
   planes.push_back(plane2.Clone());
@@ -211,8 +211,8 @@
 TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) {
   drm_->set_set_crtc_expectation(false);
 
-  ui::DrmOverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(
-                                new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane(scoped_refptr<ui::DrmFramebuffer>(
+                                new ui::MockDrmFramebuffer(kDefaultModeSize)),
                             nullptr);
 
   EXPECT_FALSE(controller_->Modeset(plane, kDefaultMode));
@@ -221,14 +221,14 @@
 TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) {
   drm_->set_commit_expectation(false);
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
 
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
 
-  ui::DrmOverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   std::vector<ui::DrmOverlayPlane> planes;
   planes.push_back(plane2.Clone());
@@ -237,13 +237,14 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
-  ui::DrmOverlayPlane plane2(
-      scoped_refptr<ui::ScanoutBuffer>(new ui::MockScanoutBuffer(kOverlaySize)),
-      1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize),
-      gfx::RectF(kDefaultModeSizeF), true, nullptr);
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kOverlaySize)),
+                             1, gfx::OVERLAY_TRANSFORM_NONE,
+                             gfx::Rect(kOverlaySize),
+                             gfx::RectF(kDefaultModeSizeF), true, nullptr);
 
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
 
@@ -262,13 +263,14 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
-  ui::DrmOverlayPlane plane2(
-      scoped_refptr<ui::ScanoutBuffer>(new ui::MockScanoutBuffer(kOverlaySize)),
-      1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize),
-      gfx::RectF(kDefaultModeSizeF), true, nullptr);
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kOverlaySize)),
+                             1, gfx::OVERLAY_TRANSFORM_NONE,
+                             gfx::Rect(kOverlaySize),
+                             gfx::RectF(kDefaultModeSizeF), true, nullptr);
 
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
 
@@ -301,11 +303,11 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, AcceptUnderlays) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
-  ui::DrmOverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              -1, gfx::OVERLAY_TRANSFORM_NONE,
                              gfx::Rect(kDefaultModeSize),
                              gfx::RectF(kDefaultModeSizeF), true, nullptr);
@@ -326,14 +328,14 @@
   controller_->AddCrtc(std::unique_ptr<ui::CrtcController>(
       new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   EXPECT_EQ(2, drm_->get_set_crtc_call_count());
 
-  ui::DrmOverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane2(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   std::vector<ui::DrmOverlayPlane> planes;
   planes.push_back(plane2.Clone());
@@ -355,8 +357,8 @@
   controller_->AddCrtc(std::unique_ptr<ui::CrtcController>(
       new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -401,8 +403,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, PlaneStateAfterDestroyingCrtc) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -431,8 +433,8 @@
   controller_->AddCrtc(std::unique_ptr<ui::CrtcController>(
       new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -479,8 +481,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, ModesetWhilePageFlipping) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -496,8 +498,8 @@
 TEST_F(HardwareDisplayControllerTest, FailPageFlipping) {
   drm_->set_commit_expectation(false);
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -507,8 +509,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlane) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              1, gfx::OVERLAY_TRANSFORM_NONE,
                              gfx::Rect(kDefaultModeSize),
                              gfx::RectF(0, 0, 1, 1), true, nullptr);
@@ -523,8 +525,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -540,8 +542,8 @@
 }
 
 TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) {
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
   std::vector<ui::DrmOverlayPlane> planes;
@@ -559,13 +561,13 @@
   // Page flipping overlays is only supported on atomic configurations.
   InitializeDrmDevice(/* use_atomic= */ true);
 
-  ui::DrmOverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
-                                 new ui::MockScanoutBuffer(kDefaultModeSize)),
+  ui::DrmOverlayPlane plane1(scoped_refptr<ui::DrmFramebuffer>(
+                                 new ui::MockDrmFramebuffer(kDefaultModeSize)),
                              nullptr);
   EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
 
   ui::DrmOverlayPlane plane2(
-      new ui::MockScanoutBuffer(kOverlaySize), 1, gfx::OVERLAY_TRANSFORM_NONE,
+      new ui::MockDrmFramebuffer(kOverlaySize), 1, gfx::OVERLAY_TRANSFORM_NONE,
       gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr);
   std::vector<ui::DrmOverlayPlane> planes;
   planes.push_back(plane1.Clone());
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index 32ad0fb9..e8d2e27 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -13,9 +13,9 @@
 #include "base/logging.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 namespace {
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index ad365ad..e780209 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -17,10 +17,10 @@
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h"
 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
index 1eb3c57..06f5bb1 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -14,10 +14,10 @@
 #include "ui/gfx/presentation_feedback.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_dummy.h"
 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 
 namespace ui {
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 63a5c207..6326f9cb 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -22,7 +22,7 @@
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h"
 
 namespace {
 
@@ -63,7 +63,7 @@
 
  protected:
   ui::HardwareDisplayPlaneList state_;
-  scoped_refptr<ui::ScanoutBuffer> fake_buffer_;
+  scoped_refptr<ui::DrmFramebuffer> fake_buffer_;
   scoped_refptr<ui::MockDrmDevice> fake_drm_;
 
   std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties_;
@@ -78,7 +78,7 @@
 
 void HardwareDisplayPlaneManagerTest::SetUp() {
   use_atomic_ = GetParam();
-  fake_buffer_ = new ui::MockScanoutBuffer(kDefaultBufferSize);
+  fake_buffer_ = new ui::MockDrmFramebuffer(kDefaultBufferSize);
 
   fake_drm_ = new ui::MockDrmDevice;
   fake_drm_->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob(
@@ -154,8 +154,8 @@
     ui::HardwareDisplayPlaneList* state) {
   ui::DrmOverlayPlaneList assigns;
   ui::CrtcController crtc(fake_drm_, crtc_properties_[crtc_idx].id, 0);
-  scoped_refptr<ui::MockScanoutBuffer> xrgb_buffer =
-      new ui::MockScanoutBuffer(kDefaultBufferSize);
+  scoped_refptr<ui::MockDrmFramebuffer> xrgb_buffer =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize);
   assigns.push_back(ui::DrmOverlayPlane(xrgb_buffer, nullptr));
   fake_drm_->plane_manager()->BeginFrame(state);
   ASSERT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
@@ -282,8 +282,8 @@
 
 TEST_P(HardwareDisplayPlaneManagerLegacyTest, CheckFramebufferFormatMatch) {
   ui::DrmOverlayPlaneList assigns;
-  scoped_refptr<ui::MockScanoutBuffer> buffer =
-      new ui::MockScanoutBuffer(kDefaultBufferSize, kDummyFormat);
+  scoped_refptr<ui::MockDrmFramebuffer> buffer =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize, kDummyFormat);
   assigns.push_back(ui::DrmOverlayPlane(buffer, nullptr));
 
   InitializeDrmState(/*crtc_count=*/2, /*planes_per_crtc=*/1);
@@ -296,8 +296,8 @@
   EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes(
       &state_, assigns, crtc_properties_[0].id, nullptr));
   assigns.clear();
-  scoped_refptr<ui::MockScanoutBuffer> xrgb_buffer =
-      new ui::MockScanoutBuffer(kDefaultBufferSize);
+  scoped_refptr<ui::MockDrmFramebuffer> xrgb_buffer =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize);
   assigns.push_back(ui::DrmOverlayPlane(xrgb_buffer, nullptr));
   fake_drm_->plane_manager()->BeginFrame(&state_);
   EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
@@ -339,8 +339,8 @@
 
 TEST_P(HardwareDisplayPlaneManagerAtomicTest, SharedPlanes) {
   ui::DrmOverlayPlaneList assigns;
-  scoped_refptr<ui::MockScanoutBuffer> buffer =
-      new ui::MockScanoutBuffer(gfx::Size(1, 1));
+  scoped_refptr<ui::MockDrmFramebuffer> buffer =
+      new ui::MockDrmFramebuffer(gfx::Size(1, 1));
 
   assigns.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr));
   assigns.push_back(ui::DrmOverlayPlane(buffer, nullptr));
@@ -371,10 +371,10 @@
                              property_names_, use_atomic_);
 
   ui::DrmOverlayPlaneList assigns;
-  scoped_refptr<ui::MockScanoutBuffer> primary_buffer =
-      new ui::MockScanoutBuffer(kDefaultBufferSize);
-  scoped_refptr<ui::MockScanoutBuffer> overlay_buffer =
-      new ui::MockScanoutBuffer(gfx::Size(1, 1));
+  scoped_refptr<ui::MockDrmFramebuffer> primary_buffer =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize);
+  scoped_refptr<ui::MockDrmFramebuffer> overlay_buffer =
+      new ui::MockDrmFramebuffer(gfx::Size(1, 1));
   assigns.push_back(ui::DrmOverlayPlane(primary_buffer, nullptr));
   assigns.push_back(ui::DrmOverlayPlane(overlay_buffer, nullptr));
   ui::HardwareDisplayPlaneList hdpl;
@@ -663,8 +663,8 @@
 
 TEST_P(HardwareDisplayPlaneManagerAtomicTest,
        CommitReturnsNullOutFenceIfOutFencePtrNotSupported) {
-  scoped_refptr<ui::ScanoutBuffer> fake_buffer2 =
-      new ui::MockScanoutBuffer(kDefaultBufferSize);
+  scoped_refptr<ui::DrmFramebuffer> fake_buffer2 =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize);
 
   InitializeDrmState(/*crtc_count=*/2, /*planes_per_crtc=*/1);
   fake_drm_->InitializeState(crtc_properties_, plane_properties_,
@@ -775,17 +775,17 @@
 
   ui::DrmOverlayPlaneList CreatePlanesWithoutFences() {
     ui::DrmOverlayPlaneList planes;
-    planes.push_back(ui::DrmOverlayPlane(scanout_buffer, nullptr));
-    planes.push_back(ui::DrmOverlayPlane(scanout_buffer, nullptr));
+    planes.push_back(ui::DrmOverlayPlane(drm_framebuffer, nullptr));
+    planes.push_back(ui::DrmOverlayPlane(drm_framebuffer, nullptr));
     return planes;
   }
 
   ui::DrmOverlayPlaneList CreatePlanesWithFences() {
     ui::DrmOverlayPlaneList planes;
     planes.push_back(
-        ui::DrmOverlayPlane(scanout_buffer, fake_fence_fd1.GetGpuFence()));
+        ui::DrmOverlayPlane(drm_framebuffer, fake_fence_fd1.GetGpuFence()));
     planes.push_back(
-        ui::DrmOverlayPlane(scanout_buffer, fake_fence_fd2.GetGpuFence()));
+        ui::DrmOverlayPlane(drm_framebuffer, fake_fence_fd2.GetGpuFence()));
     return planes;
   }
 
@@ -794,8 +794,8 @@
   base::test::ScopedTaskEnvironment task_env_{
       base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
       base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED};
-  const scoped_refptr<ui::ScanoutBuffer> scanout_buffer{
-      new ui::MockScanoutBuffer(kDefaultBufferSize)};
+  const scoped_refptr<ui::DrmFramebuffer> drm_framebuffer{
+      new ui::MockDrmFramebuffer(kDefaultBufferSize)};
   const FakeFenceFD fake_fence_fd1;
   const FakeFenceFD fake_fence_fd2;
 
@@ -900,8 +900,8 @@
       std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>();
   ui::HardwareDisplayPlaneList plane_list;
   HardwareDisplayPlaneAtomicMock hw_plane;
-  scoped_refptr<ui::ScanoutBuffer> buffer =
-      new ui::MockScanoutBuffer(kDefaultBufferSize);
+  scoped_refptr<ui::DrmFramebuffer> buffer =
+      new ui::MockDrmFramebuffer(kDefaultBufferSize);
   ui::DrmOverlayPlane overlay(buffer, nullptr);
   overlay.enable_blend = true;
   plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect(),
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_framebuffer.cc b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer.cc
new file mode 100644
index 0000000..41927bb
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
+
+namespace ui {
+
+namespace {
+
+uint32_t g_current_framebuffer_id = 1;
+
+}  // namespace
+
+MockDrmFramebuffer::MockDrmFramebuffer(const gfx::Size& size,
+                                       uint32_t format,
+                                       uint64_t modifier,
+                                       const scoped_refptr<DrmDevice>& drm)
+    : size_(size),
+      format_(format),
+      modifier_(modifier),
+      id_(g_current_framebuffer_id++),
+      opaque_id_(g_current_framebuffer_id++),
+      drm_(drm) {}
+
+MockDrmFramebuffer::~MockDrmFramebuffer() {}
+
+uint32_t MockDrmFramebuffer::GetFramebufferId() const {
+  return id_;
+}
+
+uint32_t MockDrmFramebuffer::GetOpaqueFramebufferId() const {
+  return opaque_id_;
+}
+
+gfx::Size MockDrmFramebuffer::GetSize() const {
+  return size_;
+}
+
+uint32_t MockDrmFramebuffer::GetFramebufferPixelFormat() const {
+  return format_;
+}
+
+uint32_t MockDrmFramebuffer::GetOpaqueFramebufferPixelFormat() const {
+  return format_;
+}
+
+uint64_t MockDrmFramebuffer::GetFormatModifier() const {
+  return modifier_;
+}
+
+const DrmDevice* MockDrmFramebuffer::GetDrmDevice() const {
+  return drm_.get();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h
similarity index 61%
rename from ui/ozone/platform/drm/gpu/mock_scanout_buffer.h
rename to ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h
index 51f5e2da..b6132ea3 100644
--- a/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h
@@ -9,32 +9,30 @@
 #include <stdint.h>
 
 #include "base/macros.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 
 namespace ui {
 
 class DrmDevice;
 
-class MockScanoutBuffer : public ScanoutBuffer {
+class MockDrmFramebuffer : public DrmFramebuffer {
  public:
-  MockScanoutBuffer(const gfx::Size& size,
-                    uint32_t format = DRM_FORMAT_XRGB8888,
-                    uint64_t modifier = DRM_FORMAT_MOD_NONE,
-                    const scoped_refptr<DrmDevice>& drm = nullptr);
+  MockDrmFramebuffer(const gfx::Size& size,
+                     uint32_t format = DRM_FORMAT_XRGB8888,
+                     uint64_t modifier = DRM_FORMAT_MOD_NONE,
+                     const scoped_refptr<DrmDevice>& drm = nullptr);
 
-  // ScanoutBuffer:
+  // DrmFramebuffer:
   uint32_t GetFramebufferId() const override;
   uint32_t GetOpaqueFramebufferId() const override;
-  uint32_t GetHandle() const override;
   gfx::Size GetSize() const override;
   uint32_t GetFramebufferPixelFormat() const override;
   uint32_t GetOpaqueFramebufferPixelFormat() const override;
   uint64_t GetFormatModifier() const override;
-  const GbmDeviceLinux* GetGbmDeviceLinux() const override;
-  bool RequiresGlFinish() const override;
+  const DrmDevice* GetDrmDevice() const override;
 
  private:
-  ~MockScanoutBuffer() override;
+  ~MockDrmFramebuffer() override;
 
   gfx::Size size_;
   uint32_t format_;
@@ -43,7 +41,7 @@
   uint32_t opaque_id_;
   scoped_refptr<DrmDevice> drm_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockScanoutBuffer);
+  DISALLOW_COPY_AND_ASSIGN(MockDrmFramebuffer);
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc
new file mode 100644
index 0000000..b9b17f4
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
+
+#include "ui/ozone/platform/drm/common/drm_util.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer.h"
+
+namespace ui {
+
+MockDrmFramebufferGenerator::MockDrmFramebufferGenerator() {}
+
+MockDrmFramebufferGenerator::~MockDrmFramebufferGenerator() {}
+
+scoped_refptr<DrmFramebuffer> MockDrmFramebufferGenerator::Create(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t format,
+    const std::vector<uint64_t>& modifiers,
+    const gfx::Size& size) {
+  return CreateWithModifier(
+      drm, format, modifiers.empty() ? DRM_FORMAT_MOD_NONE : modifiers.front(),
+      size);
+}
+
+scoped_refptr<DrmFramebuffer> MockDrmFramebufferGenerator::CreateWithModifier(
+    const scoped_refptr<DrmDevice>& drm,
+    uint32_t format,
+    uint64_t modifier,
+    const gfx::Size& size) {
+  if (allocation_failure_)
+    return nullptr;
+
+  scoped_refptr<MockDrmFramebuffer> buffer(
+      new MockDrmFramebuffer(size, format, modifier, drm));
+
+  return buffer;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h
new file mode 100644
index 0000000..4b3e8cb
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
+
+#include "base/macros.h"
+
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
+
+namespace ui {
+
+class MockDrmFramebufferGenerator : public DrmFramebufferGenerator {
+ public:
+  MockDrmFramebufferGenerator();
+  ~MockDrmFramebufferGenerator() override;
+
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override;
+
+  scoped_refptr<DrmFramebuffer> CreateWithModifier(
+      const scoped_refptr<DrmDevice>& drm,
+      uint32_t format,
+      uint64_t modifier,
+      const gfx::Size& size);
+
+  void set_allocation_failure(bool allocation_failure) {
+    allocation_failure_ = allocation_failure;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDrmFramebufferGenerator);
+
+  bool allocation_failure_ = false;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc
index 0276dea..352aff2 100644
--- a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc
+++ b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.cc
@@ -13,7 +13,7 @@
 
 MockDumbBufferGenerator::~MockDumbBufferGenerator() {}
 
-scoped_refptr<ScanoutBuffer> MockDumbBufferGenerator::Create(
+scoped_refptr<DrmFramebuffer> MockDumbBufferGenerator::Create(
     const scoped_refptr<DrmDevice>& drm,
     uint32_t format,
     const std::vector<uint64_t>& modifiers,
diff --git a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h
index d9bb026..9fe8f4d 100644
--- a/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h
+++ b/ui/ozone/platform/drm/gpu/mock_dumb_buffer_generator.h
@@ -7,23 +7,23 @@
 
 #include "base/macros.h"
 
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 
 namespace ui {
 
-class ScanoutBuffer;
+class DrmFramebuffer;
 
-class MockDumbBufferGenerator : public ScanoutBufferGenerator {
+class MockDumbBufferGenerator : public DrmFramebufferGenerator {
  public:
   MockDumbBufferGenerator();
   ~MockDumbBufferGenerator() override;
 
-  // ScanoutBufferGenerator:
-  scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
-                                      uint32_t format,
-                                      const std::vector<uint64_t>& modifiers,
-                                      const gfx::Size& size) override;
+  // DrmFramebufferGenerator:
+  scoped_refptr<DrmFramebuffer> Create(const scoped_refptr<DrmDevice>& drm,
+                                       uint32_t format,
+                                       const std::vector<uint64_t>& modifiers,
+                                       const gfx::Size& size) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDumbBufferGenerator);
diff --git a/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc b/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc
deleted file mode 100644
index f4bfc08..0000000
--- a/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-
-namespace ui {
-
-namespace {
-
-uint32_t g_current_framebuffer_id = 1;
-
-}  // namespace
-
-MockScanoutBuffer::MockScanoutBuffer(const gfx::Size& size,
-                                     uint32_t format,
-                                     uint64_t modifier,
-                                     const scoped_refptr<DrmDevice>& drm)
-    : size_(size),
-      format_(format),
-      modifier_(modifier),
-      id_(g_current_framebuffer_id++),
-      opaque_id_(g_current_framebuffer_id++),
-      drm_(drm) {}
-
-MockScanoutBuffer::~MockScanoutBuffer() {}
-
-uint32_t MockScanoutBuffer::GetFramebufferId() const {
-  return id_;
-}
-
-uint32_t MockScanoutBuffer::GetOpaqueFramebufferId() const {
-  return opaque_id_;
-}
-
-uint32_t MockScanoutBuffer::GetHandle() const {
-  return 0;
-}
-
-gfx::Size MockScanoutBuffer::GetSize() const {
-  return size_;
-}
-
-uint32_t MockScanoutBuffer::GetFramebufferPixelFormat() const {
-  return format_;
-}
-
-uint32_t MockScanoutBuffer::GetOpaqueFramebufferPixelFormat() const {
-  return format_;
-}
-
-uint64_t MockScanoutBuffer::GetFormatModifier() const {
-  return modifier_;
-}
-
-const GbmDeviceLinux* MockScanoutBuffer::GetGbmDeviceLinux() const {
-  return drm_->AsGbmDeviceLinux();
-}
-
-bool MockScanoutBuffer::RequiresGlFinish() const {
-  return false;
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.cc b/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.cc
deleted file mode 100644
index c934a1db..0000000
--- a/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h"
-
-#include "ui/ozone/platform/drm/common/drm_util.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
-
-namespace ui {
-
-MockScanoutBufferGenerator::MockScanoutBufferGenerator() {}
-
-MockScanoutBufferGenerator::~MockScanoutBufferGenerator() {}
-
-scoped_refptr<ScanoutBuffer> MockScanoutBufferGenerator::Create(
-    const scoped_refptr<DrmDevice>& drm,
-    uint32_t format,
-    const std::vector<uint64_t>& modifiers,
-    const gfx::Size& size) {
-  return CreateWithModifier(
-      drm, format, modifiers.empty() ? DRM_FORMAT_MOD_NONE : modifiers.front(),
-      size);
-}
-
-scoped_refptr<ScanoutBuffer> MockScanoutBufferGenerator::CreateWithModifier(
-    const scoped_refptr<DrmDevice>& drm,
-    uint32_t format,
-    uint64_t modifier,
-    const gfx::Size& size) {
-  if (allocation_failure_)
-    return nullptr;
-
-  scoped_refptr<MockScanoutBuffer> buffer(
-      new MockScanoutBuffer(size, format, modifier, drm));
-
-  return buffer;
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h b/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h
deleted file mode 100644
index b6fcb93..0000000
--- a/ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
-#define UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
-
-#include "base/macros.h"
-
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
-
-namespace ui {
-
-class MockScanoutBufferGenerator : public ScanoutBufferGenerator {
- public:
-  MockScanoutBufferGenerator();
-  ~MockScanoutBufferGenerator() override;
-
-  // ScanoutBufferGenerator:
-  scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DrmDevice>& drm,
-                                      uint32_t format,
-                                      const std::vector<uint64_t>& modifiers,
-                                      const gfx::Size& size) override;
-
-  scoped_refptr<ScanoutBuffer> CreateWithModifier(
-      const scoped_refptr<DrmDevice>& drm,
-      uint32_t format,
-      uint64_t modifier,
-      const gfx::Size& size);
-
-  void set_allocation_failure(bool allocation_failure) {
-    allocation_failure_ = allocation_failure;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockScanoutBufferGenerator);
-
-  bool allocation_failure_ = false;
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PLATFORM_DRM_GPU_MOCK_SCANOUT_BUFFER_GENERATOR_H_
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.cc b/ui/ozone/platform/drm/gpu/screen_manager.cc
index a6eab3b..e1b0112 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager.cc
+++ b/ui/ozone/platform/drm/gpu/screen_manager.cc
@@ -19,10 +19,10 @@
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_console_buffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer_generator.h"
 
 namespace ui {
 
@@ -32,7 +32,7 @@
 // to the new modeset buffer |buffer|.
 void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm,
                        HardwareDisplayController* controller,
-                       ScanoutBuffer* buffer) {
+                       DrmFramebuffer* buffer) {
   DrmConsoleBuffer modeset_buffer(drm, buffer->GetOpaqueFramebufferId());
   if (!modeset_buffer.Initialize()) {
     VLOG(2) << "Failed to grab framebuffer " << buffer->GetOpaqueFramebufferId();
@@ -96,9 +96,8 @@
 
 }  // namespace
 
-ScreenManager::ScreenManager(ScanoutBufferGenerator* buffer_generator)
-    : buffer_generator_(buffer_generator) {
-}
+ScreenManager::ScreenManager(DrmFramebufferGenerator* buffer_generator)
+    : buffer_generator_(buffer_generator) {}
 
 ScreenManager::~ScreenManager() {
   DCHECK(window_map_.empty());
@@ -373,7 +372,7 @@
     const DrmOverlayPlane* primary = window->GetLastModesetBuffer();
     const DrmDevice* drm = controller->GetDrmDevice().get();
     if (primary && primary->buffer->GetSize() == bounds.size() &&
-        primary->buffer->GetGbmDeviceLinux() == drm->AsGbmDeviceLinux()) {
+        primary->buffer->GetDrmDevice() == drm) {
       // If the controller doesn't advertise modifiers, wont have a
       // modifier either and we can reuse the buffer. Otherwise, check
       // to see if the controller supports the buffers format
@@ -388,7 +387,7 @@
   }
 
   scoped_refptr<DrmDevice> drm = controller->GetDrmDevice();
-  scoped_refptr<ScanoutBuffer> buffer =
+  scoped_refptr<DrmFramebuffer> buffer =
       buffer_generator_->Create(drm, fourcc_format, modifiers, bounds.size());
   if (!buffer) {
     LOG(ERROR) << "Failed to create scanout buffer";
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.h b/ui/ozone/platform/drm/gpu/screen_manager.h
index ac237e8..c21dbde 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager.h
+++ b/ui/ozone/platform/drm/gpu/screen_manager.h
@@ -25,12 +25,12 @@
 
 class DrmDevice;
 class DrmWindow;
-class ScanoutBufferGenerator;
+class DrmFramebufferGenerator;
 
 // Responsible for keeping track of active displays and configuring them.
 class ScreenManager {
  public:
-  ScreenManager(ScanoutBufferGenerator* surface_generator);
+  ScreenManager(DrmFramebufferGenerator* surface_generator);
   virtual ~ScreenManager();
 
   // Register a display controller. This must be called before trying to
@@ -131,7 +131,7 @@
 
   DrmWindow* FindWindowAt(const gfx::Rect& bounds) const;
 
-  ScanoutBufferGenerator* buffer_generator_;  // Not owned.
+  DrmFramebufferGenerator* buffer_generator_;  // Not owned.
   // List of display controllers (active and disabled).
   HardwareDisplayControllers controllers_;
 
diff --git a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
index 7f31661..7a2751c 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
@@ -15,11 +15,11 @@
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
-#include "ui/ozone/platform/drm/gpu/mock_scanout_buffer_generator.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/mock_drm_framebuffer_generator.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 
 namespace ui {
@@ -58,7 +58,7 @@
   void SetUp() override {
     drm_ = new ui::MockDrmDevice;
     device_manager_.reset(new ui::DrmDeviceManager(nullptr));
-    buffer_generator_.reset(new ui::MockScanoutBufferGenerator());
+    buffer_generator_.reset(new ui::MockDrmFramebufferGenerator());
     screen_manager_.reset(new ui::ScreenManager(buffer_generator_.get()));
   }
   void TearDown() override {
@@ -69,7 +69,7 @@
  protected:
   scoped_refptr<ui::MockDrmDevice> drm_;
   std::unique_ptr<ui::DrmDeviceManager> device_manager_;
-  std::unique_ptr<ui::MockScanoutBufferGenerator> buffer_generator_;
+  std::unique_ptr<ui::MockDrmFramebufferGenerator> buffer_generator_;
   std::unique_ptr<ui::ScreenManager> screen_manager_;
 
  private:
@@ -505,7 +505,7 @@
   window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
 
-  scoped_refptr<ui::ScanoutBuffer> buffer = buffer_generator_->Create(
+  scoped_refptr<ui::DrmFramebuffer> buffer = buffer_generator_->Create(
       drm_, DRM_FORMAT_XRGB8888, {}, GetPrimaryBounds().size());
   ui::DrmOverlayPlaneList planes;
   planes.push_back(ui::DrmOverlayPlane(buffer, nullptr));
@@ -524,12 +524,13 @@
   window->Shutdown();
 }
 
-TEST_F(ScreenManagerTest, RejectBufferWithIncompatibleModifiers) {
+// See crbug.com/868010
+TEST_F(ScreenManagerTest, DISABLED_RejectBufferWithIncompatibleModifiers) {
   std::unique_ptr<ui::DrmWindow> window(
       new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get()));
   window->Initialize(buffer_generator_.get());
   window->SetBounds(GetPrimaryBounds());
-  scoped_refptr<ui::ScanoutBuffer> buffer =
+  scoped_refptr<ui::DrmFramebuffer> buffer =
       buffer_generator_->CreateWithModifier(drm_, DRM_FORMAT_XRGB8888,
                                             I915_FORMAT_MOD_X_TILED,
                                             GetPrimaryBounds().size());
@@ -550,6 +551,7 @@
   // I915_FORMAT_MOD_X_TILED modifier we created above and the two
   // framebuffer IDs should be different.
   EXPECT_NE(buffer->GetFramebufferId(), drm_->current_framebuffer());
+  EXPECT_NE(buffer->GetOpaqueFramebufferId(), drm_->current_framebuffer());
 
   window = screen_manager_->RemoveWindow(1);
   window->Shutdown();
@@ -559,7 +561,7 @@
   auto drm_device1 = base::MakeRefCounted<MockDrmDevice>();
   auto drm_device2 = base::MakeRefCounted<MockDrmDevice>();
   DrmDeviceManager drm_device_manager(nullptr);
-  MockScanoutBufferGenerator buffer_generator;
+  MockDrmFramebufferGenerator buffer_generator;
   ScreenManager screen_manager(&buffer_generator);
 
   constexpr uint32_t kCrtc19 = 19;
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
index 18a510f..f3e5c12 100644
--- a/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -26,12 +26,12 @@
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
 #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h"
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
 #include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
-#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
 #include "ui/ozone/platform/drm/host/drm_cursor.h"
 #include "ui/ozone/platform/drm/host/drm_device_connector.h"
diff --git a/ui/views/mus/ax_remote_host.cc b/ui/views/mus/ax_remote_host.cc
index d992c37..70b63d5 100644
--- a/ui/views/mus/ax_remote_host.cc
+++ b/ui/views/mus/ax_remote_host.cc
@@ -101,9 +101,17 @@
     Disable();
 }
 
-void AXRemoteHost::PerformAction(const ui::AXActionData& action) {
-  // TODO(jamescook): Support ax::mojom::Action::kHitTest.
-  tree_source_->HandleAccessibleAction(action);
+void AXRemoteHost::PerformAction(const ui::AXActionData& action_data) {
+  if (!enabled_)
+    return;
+
+  // A hit test requires determining the node to perform the action on first.
+  if (action_data.action == ax::mojom::Action::kHitTest) {
+    PerformHitTest(action_data);
+    return;
+  }
+
+  tree_source_->HandleAccessibleAction(action_data);
 }
 
 void AXRemoteHost::OnWidgetDestroying(Widget* widget) {
@@ -144,17 +152,17 @@
 
   std::set<aura::Window*> roots =
       MusClient::Get()->window_tree_client()->GetRoots();
-  if (roots.empty()) {
-    // Client hasn't opened any widgets yet.
-    return;
+  for (aura::Window* root : roots) {
+    Widget* widget = Widget::GetWidgetForNativeWindow(root);
+    // Typically it's only tests that create Windows (the WindowTreeHost
+    // backing the root Window) in such a way that there is no associated
+    // widget.
+    if (widget) {
+      // TODO(jamescook): Support multiple roots.
+      DCHECK(!widget_);
+      StartMonitoringWidget(widget);
+    }
   }
-
-  // TODO(jamescook): Support multiple roots.
-  aura::Window* root_window = *roots.begin();
-  DCHECK(root_window);
-  Widget* root_widget = Widget::GetWidgetForNativeWindow(root_window);
-  DCHECK(root_widget);
-  StartMonitoringWidget(root_widget);
 }
 
 void AXRemoteHost::Disable() {
@@ -194,4 +202,18 @@
   ax_host_ptr_->HandleAccessibilityEvent(kRemoteAXTreeID, updates, event);
 }
 
+void AXRemoteHost::PerformHitTest(const ui::AXActionData& action) {
+  DCHECK(enabled_);
+  DCHECK_EQ(action.action, ax::mojom::Action::kHitTest);
+
+  // If the widget isn't open yet there's nothing to hit.
+  if (!widget_)
+    return;
+
+  views::View* hit_view =
+      widget_->GetRootView()->GetEventHandlerForPoint(action.target_point);
+  if (hit_view)
+    hit_view->NotifyAccessibilityEvent(action.hit_test_event_to_fire, true);
+}
+
 }  // namespace views
diff --git a/ui/views/mus/ax_remote_host.h b/ui/views/mus/ax_remote_host.h
index f213060..2a89dea4 100644
--- a/ui/views/mus/ax_remote_host.h
+++ b/ui/views/mus/ax_remote_host.h
@@ -84,6 +84,8 @@
   // Sends an event to the host.
   void SendEvent(AXAuraObjWrapper* aura_obj, ax::mojom::Event event_type);
 
+  void PerformHitTest(const ui::AXActionData& action);
+
   // Accessibility host service in the browser.
   ax::mojom::AXHostPtr ax_host_ptr_;
 
diff --git a/ui/views/mus/ax_remote_host_unittest.cc b/ui/views/mus/ax_remote_host_unittest.cc
index cf93bb3..0a1baad 100644
--- a/ui/views/mus/ax_remote_host_unittest.cc
+++ b/ui/views/mus/ax_remote_host_unittest.cc
@@ -57,18 +57,31 @@
 // TestView senses accessibility actions.
 class TestView : public View {
  public:
-  TestView() = default;
+  TestView() { set_owned_by_client(); }
   ~TestView() override = default;
 
+  void ResetCounts() {
+    action_count_ = 0;
+    last_action_ = {};
+    event_count_ = 0;
+    last_event_type_ = ax::mojom::Event::kNone;
+  }
+
   // View:
   bool HandleAccessibleAction(const ui::AXActionData& action) override {
     ++action_count_;
     last_action_ = action;
     return true;
   }
+  void OnAccessibilityEvent(ax::mojom::Event event_type) override {
+    ++event_count_;
+    last_event_type_ = event_type;
+  }
 
   int action_count_ = 0;
   ui::AXActionData last_action_;
+  int event_count_ = 0;
+  ax::mojom::Event last_event_type_ = ax::mojom::Event::kNone;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestView);
@@ -165,5 +178,39 @@
   EXPECT_EQ(ax::mojom::Action::kScrollDown, view.last_action_.action);
 }
 
+TEST_F(AXRemoteHostTest, PerformHitTest) {
+  TestAXHostService service(true /*automation_enabled*/);
+  AXRemoteHost* remote = CreateRemote(&service);
+
+  // Create a view to sense the action.
+  TestView view;
+  view.SetBounds(0, 0, 100, 100);
+  std::unique_ptr<Widget> widget = CreateTestWidget();
+  widget->GetRootView()->AddChildView(&view);
+
+  // Clear event counts triggered by view creation and bounds set.
+  view.ResetCounts();
+
+  // Request a hit test.
+  ui::AXActionData action;
+  action.action = ax::mojom::Action::kHitTest;
+  action.hit_test_event_to_fire = ax::mojom::Event::kHitTestResult;
+  action.target_point = gfx::Point(5, 5);
+  remote->PerformAction(action);
+
+  // View sensed a hit.
+  EXPECT_EQ(1, view.event_count_);
+  EXPECT_EQ(ax::mojom::Event::kHitTestResult, view.last_event_type_);
+  view.ResetCounts();
+
+  // Try to hit a point outside the view.
+  action.target_point = gfx::Point(101, 101);
+  remote->PerformAction(action);
+
+  // View wasn't hit.
+  EXPECT_EQ(0, view.event_count_);
+  EXPECT_EQ(ax::mojom::Event::kNone, view.last_event_type_);
+}
+
 }  // namespace
 }  // namespace views