diff --git a/DEPS b/DEPS
index a62c24d..9a34191 100644
--- a/DEPS
+++ b/DEPS
@@ -116,11 +116,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': 'b45b53f01cf3d883eeb0e01205ec078cbe9ace9a',
+  'skia_revision': 'd7157b2ccf53febde957a2c229e5f59fbd4f5cf2',
   # 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': '666bded3cec43d84f484c5f310ccba96291f096d',
+  'v8_revision': 'ebedb7ebb3f46fb54efc4601bbd5580989cb575f',
   # 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.
@@ -176,7 +176,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b0c06d4b490c5cc1b42af3c8a78fa1b40c473827',
+  'catapult_revision': '6b8b30c8a1b138f083cb474871e500662397b3f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -224,7 +224,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '2b1f6b373ca9ee776e969948b883d93c8b4f304c',
+  'spv_tools_revision': '5beeee15c175a15e904ad7ce3df37d1d0d55bd06',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -655,7 +655,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c59d48ec1594e5ea2973e156d54f2b7572307ead',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd5fa47311da1d0457f3ba3c12a0480bcb77449af',
       'condition': 'checkout_linux',
   },
 
@@ -1014,7 +1014,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '96026fbcbeee8593b92f0aa6908465a248a9f55e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '74096ec8f277c79fd2e85f8880a848ad49b63791',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1166,7 +1166,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6dbf0e43a5ff4768fea849de829c84e5470d544b',
+    Var('webrtc_git') + '/src.git' + '@' + 'fdc635d2a8ffceeec44b57a83c9ca102fba1a88f',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1197,7 +1197,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2dcaf264b69f8e7b7f12a5356c668a833994195f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8eae8558004a4404a5f6ea5437fdf5b4cc92ac55',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/common/devtools_instrumentation.h b/android_webview/common/devtools_instrumentation.h
index 31f11707..abb95dc 100644
--- a/android_webview/common/devtools_instrumentation.h
+++ b/android_webview/common/devtools_instrumentation.h
@@ -12,22 +12,23 @@
 namespace devtools_instrumentation {
 
 namespace internal {
-const char kCategory[] = "Java,devtools,disabled-by-default-devtools.timeline";
+constexpr const char* Category() {
+  // Declared as a constexpr function to have an external linkage and to be
+  // known at compile-time.
+  return "Java,devtools,disabled-by-default-devtools.timeline";
+}
 const char kEmbedderCallback[] = "EmbedderCallback";
 const char kCallbackNameArgument[] = "callbackName";
 }  // namespace internal
 
 class ScopedEmbedderCallbackTask {
  public:
-  ScopedEmbedderCallbackTask(const char* callback_name) {
-    TRACE_EVENT_BEGIN1(internal::kCategory,
-                       internal::kEmbedderCallback,
-                       internal::kCallbackNameArgument,
-                       callback_name);
+  explicit ScopedEmbedderCallbackTask(const char* callback_name) {
+    TRACE_EVENT_BEGIN1(internal::Category(), internal::kEmbedderCallback,
+                       internal::kCallbackNameArgument, callback_name);
   }
   ~ScopedEmbedderCallbackTask() {
-    TRACE_EVENT_END0(internal::kCategory,
-                     internal::kEmbedderCallback);
+    TRACE_EVENT_END0(internal::Category(), internal::kEmbedderCallback);
   }
 
  private:
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b1716ec..dcf1ee7 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -48,6 +48,11 @@
     "frame/wide_frame_view.h",
     "login/ui/lock_window.h",
     "magnifier/magnification_controller.h",
+
+    # TODO: move MultiUserWindowManager (and delegate) to sources:
+    # https://crbug.com/756085
+    "multi_user/multi_user_window_manager.h",
+    "multi_user/multi_user_window_manager_delegate.h",
     "new_window_controller.h",
     "root_window_controller.h",
     "screenshot_delegate.h",
@@ -518,6 +523,9 @@
     "multi_device_setup/multi_device_notification_presenter.h",
     "multi_profile_uma.cc",
     "multi_profile_uma.h",
+    "multi_user/multi_user_window_manager.cc",
+    "multi_user/user_switch_animator.cc",
+    "multi_user/user_switch_animator.h",
     "network_connect_delegate_mus.cc",
     "network_connect_delegate_mus.h",
     "new_window_controller.cc",
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index e183387..913bd18 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -555,7 +555,7 @@
                           flags);
   }
 
-  int preview_circle_radius = GetPreviewCircleRadius();
+  const int preview_circle_radius = GetPreviewCircleRadius();
   if (!preview_circle_radius)
     return;
 
@@ -841,6 +841,7 @@
   SetUIState(UI_STATE_DRAGGING);
 }
 
+// static
 gfx::Rect AppListItemView::GetIconBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& icon_size) {
@@ -850,6 +851,7 @@
   return rect;
 }
 
+// static
 gfx::Rect AppListItemView::GetTitleBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& title_size) {
@@ -861,6 +863,7 @@
   return rect;
 }
 
+// static
 gfx::Rect AppListItemView::GetProgressBarBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds,
     const gfx::Size& progress_bar_size) {
diff --git a/ash/multi_user/multi_user_window_manager.cc b/ash/multi_user/multi_user_window_manager.cc
index 723fd247..09e16917 100644
--- a/ash/multi_user/multi_user_window_manager.cc
+++ b/ash/multi_user/multi_user_window_manager.cc
@@ -7,21 +7,20 @@
 #include <set>
 #include <vector>
 
+#include "ash/media_controller.h"
+#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_delegate.h"
+#include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/interfaces/ash_window_manager.mojom.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/auto_reset.h"
 #include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_util.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/aura/mus/window_mus.h"
-#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/events/event.h"
 #include "ui/views/mus/mus_client.h"
@@ -29,67 +28,22 @@
 #include "ui/wm/core/window_animations.h"
 #include "ui/wm/core/window_util.h"
 
+namespace ash {
 namespace {
 
-// The animation time in milliseconds for a single window which is fading
-// in / out.
-const int kAnimationTimeMS = 100;
+// The animation time for a single window that is fading in / out.
+constexpr base::TimeDelta kAnimationTime =
+    base::TimeDelta::FromMilliseconds(100);
 
-// The animation time in milliseconds for the fade in and / or out when
-// switching users.
-const int kUserFadeTimeMS = 110;
+// The animation time for the fade in and / or out when switching users.
+constexpr base::TimeDelta kUserFadeTime =
+    base::TimeDelta::FromMilliseconds(110);
 
 // The animation time in ms for a window which get teleported to another screen.
-const int kTeleportAnimationTimeMS = 300;
+constexpr base::TimeDelta kTeleportAnimationTime =
+    base::TimeDelta::FromMilliseconds(300);
 
-// Used for UMA metrics. Do not reorder.
-enum TeleportWindowType {
-  TELEPORT_WINDOW_BROWSER = 0,
-  TELEPORT_WINDOW_INCOGNITO_BROWSER,
-  TELEPORT_WINDOW_V1_APP,
-  TELEPORT_WINDOW_V2_APP,
-  DEPRECATED_TELEPORT_WINDOW_PANEL,
-  TELEPORT_WINDOW_POPUP,
-  TELEPORT_WINDOW_UNKNOWN,
-  NUM_TELEPORT_WINDOW_TYPES
-};
-
-// Records the type of window which was transferred to another desktop.
-void RecordUMAForTransferredWindowType(aura::Window* window) {
-  // We need to figure out what kind of window this is to record the transfer.
-  Browser* browser = chrome::FindBrowserWithWindow(window);
-  TeleportWindowType window_type = TELEPORT_WINDOW_UNKNOWN;
-  if (browser) {
-    if (browser->profile()->IsOffTheRecord()) {
-      window_type = TELEPORT_WINDOW_INCOGNITO_BROWSER;
-    } else if (browser->is_app()) {
-      window_type = TELEPORT_WINDOW_V1_APP;
-    } else if (browser->is_type_popup()) {
-      window_type = TELEPORT_WINDOW_POPUP;
-    } else {
-      window_type = TELEPORT_WINDOW_BROWSER;
-    }
-  } else {
-    // Unit tests might come here without a profile manager.
-    if (!g_browser_process->profile_manager())
-      return;
-    // If it is not a browser, it is probably be a V2 application. In that case
-    // one of the AppWindowRegistry instances should know about it.
-    extensions::AppWindow* app_window = NULL;
-    std::vector<Profile*> profiles =
-        g_browser_process->profile_manager()->GetLoadedProfiles();
-    for (std::vector<Profile*>::iterator it = profiles.begin();
-         it != profiles.end() && app_window == NULL; it++) {
-      app_window =
-          extensions::AppWindowRegistry::Get(*it)->GetAppWindowForNativeWindow(
-              window);
-    }
-    if (app_window)
-      window_type = TELEPORT_WINDOW_V2_APP;
-  }
-  UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindowType", window_type,
-                            NUM_TELEPORT_WINDOW_TYPES);
-}
+MultiUserWindowManager* g_instance = nullptr;
 
 bool HasSystemModalTransientChildWindow(aura::Window* window) {
   if (window == nullptr)
@@ -100,34 +54,54 @@
   if (window->parent() == system_modal_container)
     return true;
 
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it) {
-    if (HasSystemModalTransientChildWindow(*it))
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window)) {
+    if (HasSystemModalTransientChildWindow(transient_child))
       return true;
   }
   return false;
 }
 
+mojom::WallpaperUserInfoPtr WallpaperUserInfoForAccount(
+    const AccountId& account_id) {
+  DCHECK(account_id.is_valid());
+  mojom::WallpaperUserInfoPtr wallpaper_user_info =
+      mojom::WallpaperUserInfo::New();
+  SessionController* session_controller = Shell::Get()->session_controller();
+  for (const mojom::UserSessionPtr& user_session :
+       session_controller->GetUserSessions()) {
+    if (user_session->user_info->account_id == account_id) {
+      wallpaper_user_info->account_id = account_id;
+      wallpaper_user_info->type = user_session->user_info->type;
+      wallpaper_user_info->is_ephemeral = user_session->user_info->is_ephemeral;
+      wallpaper_user_info->has_gaia_account =
+          user_session->user_info->has_gaia_account;
+      return wallpaper_user_info;
+    }
+  }
+  NOTREACHED();
+  return wallpaper_user_info;
+}
+
 }  // namespace
 
 // A class to temporarily change the animation properties for a window.
 class AnimationSetter {
  public:
-  AnimationSetter(aura::Window* window, int animation_time_in_ms)
+  AnimationSetter(aura::Window* window, base::TimeDelta animation_time)
       : window_(window),
-        previous_animation_type_(wm::GetWindowVisibilityAnimationType(window_)),
+        previous_animation_type_(
+            ::wm::GetWindowVisibilityAnimationType(window_)),
         previous_animation_time_(
-            wm::GetWindowVisibilityAnimationDuration(*window_)) {
-    wm::SetWindowVisibilityAnimationType(
-        window_, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-    wm::SetWindowVisibilityAnimationDuration(
-        window_, base::TimeDelta::FromMilliseconds(animation_time_in_ms));
+            ::wm::GetWindowVisibilityAnimationDuration(*window_)) {
+    ::wm::SetWindowVisibilityAnimationType(
+        window_, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+    ::wm::SetWindowVisibilityAnimationDuration(window_, animation_time);
   }
 
   ~AnimationSetter() {
-    wm::SetWindowVisibilityAnimationType(window_, previous_animation_type_);
-    wm::SetWindowVisibilityAnimationDuration(window_, previous_animation_time_);
+    ::wm::SetWindowVisibilityAnimationType(window_, previous_animation_type_);
+    ::wm::SetWindowVisibilityAnimationDuration(window_,
+                                               previous_animation_time_);
   }
 
  private:
@@ -143,46 +117,16 @@
   DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
 };
 
-// This class keeps track of all applications which were started for a user.
-// When an app gets created, the window will be tagged for that user. Note
-// that the destruction does not need to be tracked here since the universal
-// window observer will take care of that.
-class AppObserver : public extensions::AppWindowRegistry::Observer {
- public:
-  explicit AppObserver(const std::string& user_id) : user_id_(user_id) {}
-  ~AppObserver() override {}
-
-  // AppWindowRegistry::Observer overrides:
-  void OnAppWindowAdded(extensions::AppWindow* app_window) override {
-    aura::Window* window = app_window->GetNativeWindow();
-    DCHECK(window);
-    MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(
-        window, AccountId::FromUserEmail(user_id_));
-  }
-
- private:
-  std::string user_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppObserver);
-};
-
-MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
-    const AccountId& current_account_id)
-    : current_account_id_(current_account_id),
-      suppress_visibility_changes_(false),
-      animation_speed_(ANIMATION_SPEED_NORMAL) {
-  if (features::IsUsingWindowService()) {
-    ash_window_manager_ =
-        views::MusClient::Get()
-            ->window_tree_client()
-            ->BindWindowManagerInterface<ash::mojom::AshWindowManager>();
-  }
-
-  if (TabletModeClient::Get())
-    tablet_mode_observer_.Add(TabletModeClient::Get());
+MultiUserWindowManager::MultiUserWindowManager(
+    MultiUserWindowManagerDelegate* delegate,
+    const AccountId& account_id)
+    : delegate_(delegate), current_account_id_(account_id) {
+  g_instance = this;
+  Shell::Get()->tablet_mode_controller()->AddObserver(this);
+  Shell::Get()->session_controller()->AddObserver(this);
 }
 
-MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
+MultiUserWindowManager::~MultiUserWindowManager() {
   // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
   // As such we should not try to finalize any outstanding user animations.
   // Note that the destruction of the object can be done later.
@@ -199,57 +143,22 @@
     window = window_to_entry_.begin();
   }
 
-  if (user_manager::UserManager::IsInitialized())
-    user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
-
-  // Remove all app observers.
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  // might be nullptr in unit tests.
-  if (!profile_manager)
-    return;
-
-  std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
-  for (auto it = profiles.begin(); it != profiles.end(); ++it) {
-    const AccountId account_id = multi_user_util::GetAccountIdFromProfile(*it);
-    AccountIdToAppWindowObserver::iterator app_observer_iterator =
-        account_id_to_app_observer_.find(account_id);
-    if (app_observer_iterator != account_id_to_app_observer_.end()) {
-      extensions::AppWindowRegistry::Get(*it)->RemoveObserver(
-          app_observer_iterator->second);
-      delete app_observer_iterator->second;
-      account_id_to_app_observer_.erase(app_observer_iterator);
-    }
-  }
+  Shell::Get()->session_controller()->RemoveObserver(this);
+  Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
+  g_instance = nullptr;
 }
 
-void MultiUserWindowManagerChromeOS::Init() {
-  // Since we are setting the SessionStateObserver and adding the user, this
-  // function should get called only once.
-  DCHECK(account_id_to_app_observer_.find(current_account_id_) ==
-         account_id_to_app_observer_.end());
-
-  // Add a session state observer to be able to monitor session changes.
-  if (user_manager::UserManager::IsInitialized())
-    user_manager::UserManager::Get()->AddSessionStateObserver(this);
-
-  // The BrowserListObserver would have been better to use then the old
-  // notification system, but that observer fires before the window got created.
-  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
-                 content::NotificationService::AllSources());
-
-  // Add an app window observer & all already running apps.
-  Profile* profile =
-      multi_user_util::GetProfileFromAccountId(current_account_id_);
-  if (profile)
-    AddUser(profile);
+// static
+MultiUserWindowManager* MultiUserWindowManager::Get() {
+  return g_instance;
 }
 
-void MultiUserWindowManagerChromeOS::SetWindowOwner(
-    aura::Window* window,
-    const AccountId& account_id) {
+void MultiUserWindowManager::SetWindowOwner(aura::Window* window,
+                                            const AccountId& account_id) {
   // Make sure the window is valid and there was no owner yet.
   DCHECK(window);
   DCHECK(account_id.is_valid());
+
   if (GetWindowOwner(window) == account_id)
     return;
   DCHECK(GetWindowOwner(window).empty());
@@ -260,7 +169,7 @@
 
   // Add observers to track state changes.
   window->AddObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
 
   // Check if this window was created due to a user interaction. If it was,
   // transfer it to the current user.
@@ -271,23 +180,18 @@
   // will add the children but not the owner to the transient children map.
   AddTransientOwnerRecursive(window, window);
 
-  // Notify entry adding.
-  for (Observer& observer : observers_)
-    observer.OnOwnerEntryAdded(window);
-
   if (!IsWindowOnDesktopOfUser(window, current_account_id_))
-    SetWindowVisibility(window, false, 0);
+    SetWindowVisibility(window, false);
 }
 
-const AccountId& MultiUserWindowManagerChromeOS::GetWindowOwner(
+const AccountId& MultiUserWindowManager::GetWindowOwner(
     aura::Window* window) const {
   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
   return it != window_to_entry_.end() ? it->second->owner() : EmptyAccountId();
 }
 
-void MultiUserWindowManagerChromeOS::ShowWindowForUser(
-    aura::Window* window,
-    const AccountId& account_id) {
+void MultiUserWindowManager::ShowWindowForUser(aura::Window* window,
+                                               const AccountId& account_id) {
   const AccountId previous_owner(GetUserPresentingWindow(window));
   if (!ShowWindowForUserIntern(window, account_id))
     return;
@@ -298,10 +202,10 @@
       previous_owner != current_account_id_)
     return;
 
-  SessionControllerClient::DoSwitchActiveUser(account_id);
+  Shell::Get()->session_controller()->SwitchActiveUser(account_id);
 }
 
-bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const {
+bool MultiUserWindowManager::AreWindowsSharedAmongUsers() const {
   WindowToEntryMap::const_iterator it = window_to_entry_.begin();
   for (; it != window_to_entry_.end(); ++it) {
     if (it->second->owner() != it->second->show_for_user())
@@ -310,23 +214,14 @@
   return false;
 }
 
-void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows(
-    std::set<AccountId>* account_ids) const {
-  for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
-       it != window_to_entry_.end(); ++it) {
-    if (it->first->IsVisible())
-      account_ids->insert(it->second->owner());
-  }
-}
-
-bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser(
+bool MultiUserWindowManager::IsWindowOnDesktopOfUser(
     aura::Window* window,
     const AccountId& account_id) const {
   const AccountId& presenting_user = GetUserPresentingWindow(window);
   return (!presenting_user.is_valid()) || presenting_user == account_id;
 }
 
-const AccountId& MultiUserWindowManagerChromeOS::GetUserPresentingWindow(
+const AccountId& MultiUserWindowManager::GetUserPresentingWindow(
     aura::Window* window) const {
   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
   // If the window is not owned by anyone it is shown on all desktops and we
@@ -337,82 +232,41 @@
   return it->second->show_for_user();
 }
 
-void MultiUserWindowManagerChromeOS::AddUser(content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  const AccountId& account_id(
-      multi_user_util::GetAccountIdFromProfile(profile));
-  if (account_id_to_app_observer_.find(account_id) !=
-      account_id_to_app_observer_.end())
-    return;
-
-  account_id_to_app_observer_[account_id] =
-      new AppObserver(account_id.GetUserEmail());
-  extensions::AppWindowRegistry::Get(profile)->AddObserver(
-      account_id_to_app_observer_[account_id]);
-
-  // Account all existing application windows of this user accordingly.
-  const extensions::AppWindowRegistry::AppWindowList& app_windows =
-      extensions::AppWindowRegistry::Get(profile)->app_windows();
-  extensions::AppWindowRegistry::AppWindowList::const_iterator it =
-      app_windows.begin();
-  for (; it != app_windows.end(); ++it)
-    account_id_to_app_observer_[account_id]->OnAppWindowAdded(*it);
-
-  // Account all existing browser windows of this user accordingly.
-  BrowserList* browser_list = BrowserList::GetInstance();
-  BrowserList::const_iterator browser_it = browser_list->begin();
-  for (; browser_it != browser_list->end(); ++browser_it) {
-    if ((*browser_it)->profile()->GetOriginalProfile() == profile)
-      AddBrowserWindow(*browser_it);
-  }
-}
-
-void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void MultiUserWindowManagerChromeOS::ActiveUserChanged(
-    const user_manager::User* active_user) {
+void MultiUserWindowManager::OnActiveUserSessionChanged(
+    const AccountId& account_id) {
   // This needs to be set before the animation starts.
-  current_account_id_ = active_user->GetAccountId();
+  current_account_id_ = account_id;
 
   // Here to avoid a very nasty race condition, we must destruct any previously
   // created animation before creating a new one. Otherwise, the newly
   // constructed will hide all windows of the old user in the first step of the
   // animation only to be reshown again by the destructor of the old animation.
   animation_.reset();
-  animation_.reset(new UserSwitchAnimatorChromeOS(
-      this, current_account_id_,
-      GetAdjustedAnimationTimeInMS(kUserFadeTimeMS)));
+  animation_ = std::make_unique<UserSwitchAnimator>(
+      this, WallpaperUserInfoForAccount(current_account_id_),
+      GetAdjustedAnimationTime(kUserFadeTime));
 
   // Call RequestCaptureState here instead of having MediaClient observe
   // ActiveUserChanged because it must happen after
-  // MultiUserWindowManagerChromeOS is notified. The MediaClient may be null in
-  // tests.
-  if (MediaClient::Get())
-    MediaClient::Get()->RequestCaptureState();
+  // MultiUserWindowManager is notified.
+  Shell::Get()->media_controller()->RequestCaptureState();
 }
 
-void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) {
+void MultiUserWindowManager::OnWindowDestroyed(aura::Window* window) {
   if (GetWindowOwner(window).empty()) {
     // This must be a window in the transient chain - remove it and its
     // children from the owner.
     RemoveTransientOwnerRecursive(window);
     return;
   }
-  wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
   // Remove the window from the owners list.
   delete window_to_entry_[window];
   window_to_entry_.erase(window);
 }
 
-void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging(
-    aura::Window* window,
-    bool visible) {
+void MultiUserWindowManager::OnWindowVisibilityChanging(aura::Window* window,
+                                                        bool visible) {
   // This command gets called first and immediately when show or hide gets
   // called. We remember here the desired state for restoration IF we were
   // not ourselves issuing the call.
@@ -435,24 +289,23 @@
   }
 }
 
-void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged(
-    aura::Window* window,
-    bool visible) {
+void MultiUserWindowManager::OnWindowVisibilityChanged(aura::Window* window,
+                                                       bool visible) {
   if (suppress_visibility_changes_)
     return;
 
   // Don't allow to make the window visible if it shouldn't be.
   if (visible && !IsWindowOnDesktopOfUser(window, current_account_id_)) {
-    SetWindowVisibility(window, false, 0);
+    SetWindowVisibility(window, false);
     return;
   }
   aura::Window* owned_parent = GetOwningWindowInTransientChain(window);
   if (owned_parent && owned_parent != window && visible &&
       !IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
-    SetWindowVisibility(window, false, 0);
+    SetWindowVisibility(window, false);
 }
 
-void MultiUserWindowManagerChromeOS::OnTransientChildAdded(
+void MultiUserWindowManager::OnTransientChildAdded(
     aura::Window* window,
     aura::Window* transient_window) {
   if (!GetWindowOwner(window).empty()) {
@@ -467,7 +320,7 @@
   AddTransientOwnerRecursive(transient_window, owned_parent);
 }
 
-void MultiUserWindowManagerChromeOS::OnTransientChildRemoved(
+void MultiUserWindowManager::OnTransientChildRemoved(
     aura::Window* window,
     aura::Window* transient_window) {
   // Remove the transient child if the window itself is owned, or one of the
@@ -477,43 +330,25 @@
     RemoveTransientOwnerRecursive(transient_window);
 }
 
-void MultiUserWindowManagerChromeOS::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_OPENED, type);
-  AddBrowserWindow(content::Source<Browser>(source).ptr());
+void MultiUserWindowManager::OnTabletModeStarted() {
+  for (auto entry : window_to_entry_)
+    Shell::Get()->tablet_mode_controller()->AddWindow(entry.first);
 }
 
-void MultiUserWindowManagerChromeOS::OnTabletModeToggled(bool enabled) {
-  if (!enabled)
-    return;
-
-  for (auto entry : window_to_entry_) {
-    aura::Window* window = entry.first;
-    if (ash_window_manager_) {
-      ash_window_manager_->AddWindowToTabletMode(
-          aura::WindowMus::Get(window)->server_id());
-    } else {
-      ash::Shell::Get()->tablet_mode_controller()->AddWindow(window);
-    }
-  }
-}
-
-void MultiUserWindowManagerChromeOS::SetAnimationSpeedForTest(
-    MultiUserWindowManagerChromeOS::AnimationSpeed speed) {
+void MultiUserWindowManager::SetAnimationSpeedForTest(
+    MultiUserWindowManager::AnimationSpeed speed) {
   animation_speed_ = speed;
 }
 
-bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() {
+bool MultiUserWindowManager::IsAnimationRunningForTest() {
   return animation_ && !animation_->IsAnimationFinished();
 }
 
-const AccountId& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() const {
+const AccountId& MultiUserWindowManager::GetCurrentUserForTest() const {
   return current_account_id_;
 }
 
-bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern(
+bool MultiUserWindowManager::ShowWindowForUserIntern(
     aura::Window* window,
     const AccountId& account_id) {
   // If there is either no owner, or the owner is the current user, no action
@@ -523,55 +358,35 @@
       (owner == account_id && IsWindowOnDesktopOfUser(window, account_id)))
     return false;
 
-  bool minimized = wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED);
+  bool minimized = ::wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED);
   // Check that we are not trying to transfer ownership of a minimized window.
   if (account_id != owner && minimized)
     return false;
 
-  if (!minimized) {
-    // If the window was transferred without getting minimized, we should record
-    // the window type. Otherwise it falls back to the original desktop.
-    RecordUMAForTransferredWindowType(window);
-  }
-
   WindowToEntryMap::iterator it = window_to_entry_.find(window);
   it->second->set_show_for_user(account_id);
 
-  // Tests could either not have a UserManager or the UserManager does not
-  // know the window owner.
-  const user_manager::User* const window_owner =
-      user_manager::UserManager::IsInitialized()
-          ? user_manager::UserManager::Get()->FindUser(owner)
-          : nullptr;
-
   const bool teleported = !IsWindowOnDesktopOfUser(window, owner);
-  if (window_owner && teleported) {
-    window->SetProperty(
-        aura::client::kAvatarIconKey,
-        new gfx::ImageSkia(GetAvatarImageForUser(window_owner)));
-  } else {
-    window->ClearProperty(aura::client::kAvatarIconKey);
-  }
 
   // Show the window if the added user is the current one.
   if (account_id == current_account_id_) {
     // Only show the window if it should be shown according to its state.
     if (it->second->show())
-      SetWindowVisibility(window, true, kTeleportAnimationTimeMS);
+      SetWindowVisibility(window, true, kTeleportAnimationTime);
   } else {
-    SetWindowVisibility(window, false, kTeleportAnimationTimeMS);
+    SetWindowVisibility(window, false, kTeleportAnimationTime);
   }
 
   // Notify entry change.
-  for (Observer& observer : observers_)
-    observer.OnOwnerEntryChanged(window);
+  if (delegate_)
+    delegate_->OnOwnerEntryChanged(window, account_id, minimized, teleported);
   return true;
 }
 
-void MultiUserWindowManagerChromeOS::SetWindowVisibility(
+void MultiUserWindowManager::SetWindowVisibility(
     aura::Window* window,
     bool visible,
-    int animation_time_in_ms) {
+    base::TimeDelta animation_time) {
   if (window->IsVisible() == visible)
     return;
 
@@ -590,7 +405,7 @@
         account_id = GetUserPresentingWindow(owning_window);
         DCHECK(account_id.is_valid());
       }
-      SessionControllerClient::DoSwitchActiveUser(account_id);
+      Shell::Get()->session_controller()->SwitchActiveUser(account_id);
       return;
     }
   }
@@ -600,61 +415,43 @@
   base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
 
   if (visible)
-    ShowWithTransientChildrenRecursive(window, animation_time_in_ms);
+    ShowWithTransientChildrenRecursive(window, animation_time);
   else
-    SetWindowVisible(window, false, animation_time_in_ms);
+    SetWindowVisible(window, false, animation_time);
 }
 
-void MultiUserWindowManagerChromeOS::NotifyAfterUserSwitchAnimationFinished() {
-  for (Observer& observer : observers_)
-    observer.OnUserSwitchAnimationFinished();
-}
-
-void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) {
-  // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
-  // come here with no valid window.
-  if (!browser->window() || !browser->window()->GetNativeWindow())
-    return;
-  SetWindowOwner(browser->window()->GetNativeWindow(),
-                 multi_user_util::GetAccountIdFromProfile(browser->profile()));
-}
-
-void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
+void MultiUserWindowManager::ShowWithTransientChildrenRecursive(
     aura::Window* window,
-    int animation_time_in_ms) {
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    ShowWithTransientChildrenRecursive(*it, animation_time_in_ms);
+    base::TimeDelta animation_time) {
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    ShowWithTransientChildrenRecursive(transient_child, animation_time);
 
   // We show all children which were not explicitly hidden.
-  TransientWindowToVisibility::iterator it2 =
+  TransientWindowToVisibility::iterator it =
       transient_window_to_visibility_.find(window);
-  if (it2 == transient_window_to_visibility_.end() || it2->second)
-    SetWindowVisible(window, true, animation_time_in_ms);
+  if (it == transient_window_to_visibility_.end() || it->second)
+    SetWindowVisible(window, true, animation_time);
 }
 
-aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
+aura::Window* MultiUserWindowManager::GetOwningWindowInTransientChain(
     aura::Window* window) const {
   if (!GetWindowOwner(window).empty())
-    return NULL;
-  aura::Window* parent = wm::GetTransientParent(window);
+    return nullptr;
+  aura::Window* parent = ::wm::GetTransientParent(window);
   while (parent) {
     if (!GetWindowOwner(parent).empty())
       return parent;
-    parent = wm::GetTransientParent(parent);
+    parent = ::wm::GetTransientParent(parent);
   }
-  return NULL;
+  return nullptr;
 }
 
-void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
+void MultiUserWindowManager::AddTransientOwnerRecursive(
     aura::Window* window,
     aura::Window* owned_parent) {
   // First add all child windows.
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    AddTransientOwnerRecursive(*it, owned_parent);
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    AddTransientOwnerRecursive(transient_child, owned_parent);
 
   // If this window is the owned window, we do not have to handle it again.
   if (window == owned_parent)
@@ -667,22 +464,20 @@
 
   // Add observers to track state changes.
   window->AddObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
 
   // Hide the window if it should not be shown. Note that this hide operation
   // will hide recursively this and all children - but we have already collected
   // their initial view state.
   if (!IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
-    SetWindowVisibility(window, false, kAnimationTimeMS);
+    SetWindowVisibility(window, false, kAnimationTime);
 }
 
-void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
+void MultiUserWindowManager::RemoveTransientOwnerRecursive(
     aura::Window* window) {
   // First remove all child windows.
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    RemoveTransientOwnerRecursive(*it);
+  for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
+    RemoveTransientOwnerRecursive(transient_child);
 
   // Find from transient window storage the visibility for the given window,
   // set the visibility accordingly and delete the window from the map.
@@ -691,7 +486,7 @@
   DCHECK(visibility_item != transient_window_to_visibility_.end());
 
   window->RemoveObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
+  ::wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
 
   bool unowned_view_state = visibility_item->second;
   transient_window_to_visibility_.erase(visibility_item);
@@ -705,40 +500,31 @@
   }
 }
 
-void MultiUserWindowManagerChromeOS::SetWindowVisible(
-    aura::Window* window,
-    bool visible,
-    int animation_time_in_ms) {
+void MultiUserWindowManager::SetWindowVisible(aura::Window* window,
+                                              bool visible,
+                                              base::TimeDelta animation_time) {
   // The TabletModeWindowManager will not handle invisible windows since they
   // are not user activatable. Since invisible windows are not being tracked,
   // we tell it to maximize / track this window now before it gets shown, to
   // reduce animation jank from multiple resizes.
-  if (visible) {
-    if (ash_window_manager_) {
-      ash_window_manager_->AddWindowToTabletMode(
-          aura::WindowMus::Get(window)->server_id());
-    } else {
-      ash::Shell::Get()->tablet_mode_controller()->AddWindow(window);
-    }
-  }
+  if (visible)
+    Shell::Get()->tablet_mode_controller()->AddWindow(window);
 
-  // Under mash we apply visibility changes to the root so both the window frame
-  // and contents hide together.
-  if (features::IsUsingWindowService())
-    window = window->GetRootWindow();
-
-  AnimationSetter animation_setter(
-      window, GetAdjustedAnimationTimeInMS(animation_time_in_ms));
-
+  AnimationSetter animation_setter(window,
+                                   GetAdjustedAnimationTime(animation_time));
   if (visible)
     window->Show();
   else
     window->Hide();
 }
 
-int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS(
-    int default_time_in_ms) const {
+base::TimeDelta MultiUserWindowManager::GetAdjustedAnimationTime(
+    base::TimeDelta default_time) const {
   return animation_speed_ == ANIMATION_SPEED_NORMAL
-             ? default_time_in_ms
-             : (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0);
+             ? default_time
+             : (animation_speed_ == ANIMATION_SPEED_FAST
+                    ? base::TimeDelta::FromMilliseconds(10)
+                    : base::TimeDelta());
 }
+
+}  // namespace ash
diff --git a/ash/multi_user/multi_user_window_manager.h b/ash/multi_user/multi_user_window_manager.h
index d1861a32..e336b5d 100644
--- a/ash/multi_user/multi_user_window_manager.h
+++ b/ash/multi_user/multi_user_window_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -7,39 +7,35 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 
-#include "ash/public/interfaces/ash_window_manager.mojom.h"
-#include "base/compiler_specific.h"
+#include "ash/ash_export.h"
+#include "ash/session/session_observer.h"
+#include "ash/wm/tablet_mode/tablet_mode_observer.h"
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
-#include "base/observer_list.h"
-#include "base/scoped_observer.h"
+#include "base/time/time.h"
 #include "components/account_id/account_id.h"
 #include "ui/aura/window_observer.h"
 #include "ui/wm/core/transient_window_observer.h"
 
-class Browser;
-
-namespace content {
-class BrowserContext;
-}
-
-namespace aura {
-class Window;
-}  // namespace aura
-
 namespace ash {
-class MultiUserWindowManagerChromeOSTest;
-}  // namespace ash
 
-class AppObserver;
-class UserSwitchAnimatorChromeOS;
+class MultiUserWindowManagerDelegate;
+class UserSwitchAnimator;
 
-// This ChromeOS implementation of the MultiUserWindowManager interface is
-// detecting app and browser creations, tagging their windows automatically and
-// using (currently) show and hide to make the owned windows visible - or not.
-// If it becomes necessary, the function |SetWindowVisibility| can be
-// overwritten to match new ways of doing this.
+// MultiUserWindowManager associates windows with users and ensures the
+// appropriate set of windows are visible at the right time.
+// MultiUserWindowManager must be explicitly told about the windows to manage.
+// This is done by way of SetWindowOwner().
+//
+// Each window may be associated with two accounts. The owning account (the
+// account supplied to SetWindowOwner()), and an account the window is shown
+// with when the account is active. Typically the 'shown' account and 'owning'
+// account are the same, but the user may choose to show a window from an other
+// account, in which case the 'shown' account changes.
+//
 // Note:
 // - aura::Window::Hide() is currently hiding the window and all owned transient
 //   children. However aura::Window::Show() is only showing the window itself.
@@ -48,13 +44,10 @@
 //   visibility changes from the owning user. This way the visibility can be
 //   changed back to its requested state upon showing by us - or when the window
 //   gets detached from its current owning parent.
-class MultiUserWindowManagerChromeOS
-    : public MultiUserWindowManager,
-      public user_manager::UserManager::UserSessionStateObserver,
-      public aura::WindowObserver,
-      public content::NotificationObserver,
-      public wm::TransientWindowObserver,
-      public TabletModeClientObserver {
+class ASH_EXPORT MultiUserWindowManager : public SessionObserver,
+                                          public aura::WindowObserver,
+                                          public ::wm::TransientWindowObserver,
+                                          public TabletModeObserver {
  public:
   // The speed which should be used to perform animations.
   enum AnimationSpeed {
@@ -63,31 +56,25 @@
     ANIMATION_SPEED_DISABLED  // Unit tests which do not require animations.
   };
 
-  // Create the manager and use |active_account_id| as the active user.
-  explicit MultiUserWindowManagerChromeOS(const AccountId& active_account_id);
-  ~MultiUserWindowManagerChromeOS() override;
+  MultiUserWindowManager(MultiUserWindowManagerDelegate* delegate,
+                         const AccountId& account_id);
+  ~MultiUserWindowManager() override;
 
-  // Initializes the manager after its creation. Should only be called once.
-  void Init();
+  static MultiUserWindowManager* Get();
 
-  // MultiUserWindowManager overrides:
-  void SetWindowOwner(aura::Window* window,
-                      const AccountId& account_id) override;
-  const AccountId& GetWindowOwner(aura::Window* window) const override;
-  void ShowWindowForUser(aura::Window* window,
-                         const AccountId& account_id) override;
-  bool AreWindowsSharedAmongUsers() const override;
-  void GetOwnersOfVisibleWindows(
-      std::set<AccountId>* account_ids) const override;
-  bool IsWindowOnDesktopOfUser(aura::Window* window,
-                               const AccountId& account_id) const override;
-  const AccountId& GetUserPresentingWindow(aura::Window* window) const override;
-  void AddUser(content::BrowserContext* context) override;
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
+  // Associates a window with a particular account. This may result in hiding
+  // |window|. This should *not* be called more than once with a different
+  // account.
+  void SetWindowOwner(aura::Window* window, const AccountId& account_id);
 
-  // user_manager::UserManager::UserSessionStateObserver overrides:
-  void ActiveUserChanged(const user_manager::User* active_user) override;
+  // Sets the 'shown' account for a window. See class description for details on
+  // what the 'shown' account is. This function may trigger changing the active
+  // user. When the window is minimized, the 'shown' account is reset to the
+  // 'owning' account.
+  void ShowWindowForUser(aura::Window* window, const AccountId& account_id);
+
+  // SessionObserver:
+  void OnActiveUserSessionChanged(const AccountId& account_id) override;
 
   // WindowObserver overrides:
   void OnWindowDestroyed(aura::Window* window) override;
@@ -100,13 +87,8 @@
   void OnTransientChildRemoved(aura::Window* window,
                                aura::Window* transient) override;
 
-  // content::NotificationObserver overrides:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
-  // TabletModeClientObserver:
-  void OnTabletModeToggled(bool enabled) override;
+  // TabletModeObserver:
+  void OnTabletModeStarted() override;
 
   // Disable any animations for unit tests.
   void SetAnimationSpeedForTest(AnimationSpeed speed);
@@ -118,13 +100,11 @@
   const AccountId& GetCurrentUserForTest() const;
 
  protected:
-  friend class UserSwitchAnimatorChromeOS;
-
   class WindowEntry {
    public:
     explicit WindowEntry(const AccountId& account_id)
-        : owner_(account_id), show_for_user_(account_id), show_(true) {}
-    virtual ~WindowEntry() {}
+        : owner_(account_id), show_for_user_(account_id) {}
+    ~WindowEntry() {}
 
     // Returns the owner of this window. This cannot be changed.
     const AccountId& owner() const { return owner_; }
@@ -152,12 +132,27 @@
     AccountId show_for_user_;
 
     // True if the window should be visible for the user which shows the window.
-    bool show_;
+    bool show_ = true;
 
     DISALLOW_COPY_AND_ASSIGN(WindowEntry);
   };
 
-  typedef std::map<aura::Window*, WindowEntry*> WindowToEntryMap;
+  // TODO: make map to std::unique_ptr<WindowEntry>.
+  using WindowToEntryMap = std::map<aura::Window*, WindowEntry*>;
+
+  const AccountId& GetWindowOwner(aura::Window* window) const;
+
+  // Returns true if at least one window's 'owner' account differs from its
+  // 'shown' account. In other words, a window from one account is shown with
+  // windows from another account.
+  bool AreWindowsSharedAmongUsers() const;
+
+  // Returns true if the 'shown' owner of |window| is |account_id|.
+  bool IsWindowOnDesktopOfUser(aura::Window* window,
+                               const AccountId& account_id) const;
+
+  // Returns the 'shown' owner.
+  const AccountId& GetUserPresentingWindow(aura::Window* window) const;
 
   // Show a window for a user without switching the user.
   // Returns true when the window moved to a new desktop.
@@ -169,31 +164,25 @@
   // distinguish state changes performed by this class vs. state changes
   // performed by the others. Note furthermore that system modal dialogs will
   // not get hidden. We will switch instead to the owners desktop.
-  // The |animation_time_in_ms| is the time the animation should take. Set to 0
-  // if it should get set instantly.
+  // The |animation_time| is the time the animation should take, an empty value
+  // switches instantly.
   void SetWindowVisibility(aura::Window* window,
                            bool visible,
-                           int animation_time_in_ms);
-
-  // Notify the observers after the user switching animation is finished.
-  void NotifyAfterUserSwitchAnimationFinished();
+                           base::TimeDelta animation_time = base::TimeDelta());
 
   const WindowToEntryMap& window_to_entry() { return window_to_entry_; }
 
  private:
-  friend class ash::MultiUserWindowManagerChromeOSTest;
+  friend class MultiUserWindowManagerChromeOSTest;
+  friend class UserSwitchAnimator;
 
-  typedef std::map<AccountId, AppObserver*> AccountIdToAppWindowObserver;
-  typedef std::map<aura::Window*, bool> TransientWindowToVisibility;
-
-  // Add a browser window to the system so that the owner can be remembered.
-  void AddBrowserWindow(Browser* browser);
+  using TransientWindowToVisibility = base::flat_map<aura::Window*, bool>;
 
   // Show the window and its transient children. However - if a transient child
   // was turned invisible by some other operation, it will stay invisible.
-  // Use the given |animation_time_in_ms| for transitioning.
+  // |animation_time| is the amount of time to animate.
   void ShowWithTransientChildrenRecursive(aura::Window* window,
-                                          int animation_time_in_ms);
+                                          base::TimeDelta animation_time);
 
   // Find the first owned window in the chain.
   // Returns NULL when the window itself is owned.
@@ -209,25 +198,20 @@
   // unregistered.
   void RemoveTransientOwnerRecursive(aura::Window* window);
 
-  // Animate a |window| to be |visible| in |animation_time_in_ms|.
+  // Animate a |window| to be |visible| over a time of |animation_time|.
   void SetWindowVisible(aura::Window* window,
                         bool visible,
-                        int aimation_time_in_ms);
+                        base::TimeDelta aimation_time);
 
-  // Get the animation time in milliseconds dependent on the |AnimationSpeed|
-  // from the passed |default_time_in_ms|.
-  int GetAdjustedAnimationTimeInMS(int default_time_in_ms) const;
+  // Returns the time for an animation.
+  base::TimeDelta GetAdjustedAnimationTime(base::TimeDelta default_time) const;
+
+  MultiUserWindowManagerDelegate* delegate_;
 
   // A lookup to see to which user the given window belongs to, where and if it
   // should get shown.
   WindowToEntryMap window_to_entry_;
 
-  // A list of all known users and their app window observers.
-  AccountIdToAppWindowObserver account_id_to_app_observer_;
-
-  // An observer list to be notified upon window owner changes.
-  base::ObserverList<Observer>::Unchecked observers_;
-
   // A map which remembers for owned transient windows their own visibility.
   TransientWindowToVisibility transient_window_to_visibility_;
 
@@ -236,25 +220,18 @@
   // being read from the user manager to be in sync while a switch occurs.
   AccountId current_account_id_;
 
-  // The notification registrar to track the creation of browser windows.
-  content::NotificationRegistrar registrar_;
-
   // Suppress changes to the visibility flag while we are changing it ourselves.
-  bool suppress_visibility_changes_;
+  bool suppress_visibility_changes_ = false;
 
   // The speed which is used to perform any animations.
-  AnimationSpeed animation_speed_;
+  AnimationSpeed animation_speed_ = ANIMATION_SPEED_NORMAL;
 
   // The animation between users.
-  std::unique_ptr<UserSwitchAnimatorChromeOS> animation_;
+  std::unique_ptr<UserSwitchAnimator> animation_;
 
-  // Only used in mash.
-  ash::mojom::AshWindowManagerAssociatedPtr ash_window_manager_;
-
-  ScopedObserver<TabletModeClient, TabletModeClientObserver>
-      tablet_mode_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerChromeOS);
+  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManager);
 };
 
+}  // namespace ash
+
 #endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
diff --git a/ash/multi_user/multi_user_window_manager_delegate.h b/ash/multi_user/multi_user_window_manager_delegate.h
new file mode 100644
index 0000000..4df91f87
--- /dev/null
+++ b/ash/multi_user/multi_user_window_manager_delegate.h
@@ -0,0 +1,45 @@
+// 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 ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
+#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "ash/ash_export.h"
+
+class AccountId;
+
+namespace aura {
+class Window;
+}  // namespace aura
+
+namespace ash {
+
+class ASH_EXPORT MultiUserWindowManagerDelegate {
+ public:
+  // Called when the owner of the window tracked by the manager is changed.
+  // |was_minimized| is true if the window was minimized. |teleported| is true
+  // if the window was not on the desktop of the current user.
+  virtual void OnOwnerEntryChanged(aura::Window* window,
+                                   const AccountId& account_id,
+                                   bool was_minimized,
+                                   bool teleported) {}
+
+  // Called when the active account changes. This is followed by
+  // OnTransitionUserShelfToNewAccount() and OnDidSwitchActiveAccount().
+  virtual void OnWillSwitchActiveAccount(const AccountId& account_id) {}
+
+  // Called at the time when the user's shelf should transition to the account
+  // supplied to OnWillSwitchActiveAccount().
+  virtual void OnTransitionUserShelfToNewAccount() {}
+
+  // Called when the active account change is complete.
+  virtual void OnDidSwitchActiveAccount() {}
+
+ protected:
+  virtual ~MultiUserWindowManagerDelegate() {}
+};
+
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc b/ash/multi_user/user_switch_animator.cc
similarity index 76%
rename from chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc
rename to ash/multi_user/user_switch_animator.cc
index 9d554296..54a2a3d 100644
--- a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc
+++ b/ash/multi_user/user_switch_animator.cc
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
+#include "ash/multi_user/user_switch_animator.h"
 
+#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_delegate.h"
 #include "ash/shell.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_positioner.h"
-#include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
-#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_tree_owner.h"
@@ -19,23 +18,25 @@
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
+namespace ash {
 namespace {
 
 // The minimal possible animation time for animations which should happen
 // "instantly".
-const int kMinimalAnimationTimeMS = 1;
+constexpr base::TimeDelta kMinimalAnimationTime =
+    base::TimeDelta::FromMilliseconds(1);
 
 // logic while the user gets switched.
 class UserChangeActionDisabler {
  public:
   UserChangeActionDisabler() {
-    ash::WindowPositioner::DisableAutoPositioning(true);
-    ash::Shell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
+    WindowPositioner::DisableAutoPositioning(true);
+    Shell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
   }
 
   ~UserChangeActionDisabler() {
-    ash::WindowPositioner::DisableAutoPositioning(false);
-    ash::Shell::Get()->mru_window_tracker()->SetIgnoreActivations(false);
+    WindowPositioner::DisableAutoPositioning(false);
+    Shell::Get()->mru_window_tracker()->SetIgnoreActivations(false);
   }
 
  private:
@@ -78,36 +79,37 @@
 
 }  // namespace
 
-UserSwitchAnimatorChromeOS::UserSwitchAnimatorChromeOS(
-    MultiUserWindowManagerChromeOS* owner,
-    const AccountId& new_account_id,
-    int animation_speed_ms)
+UserSwitchAnimator::UserSwitchAnimator(
+    MultiUserWindowManager* owner,
+    mojom::WallpaperUserInfoPtr wallpaper_user_info,
+    base::TimeDelta animation_speed)
     : owner_(owner),
-      new_account_id_(new_account_id),
-      animation_speed_ms_(animation_speed_ms),
+      wallpaper_user_info_(std::move(wallpaper_user_info)),
+      new_account_id_(wallpaper_user_info_->account_id),
+      animation_speed_(animation_speed),
       animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
       screen_cover_(GetScreenCover(NULL)),
       windows_by_account_id_() {
   BuildUserToWindowsListMap();
   AdvanceUserTransitionAnimation();
 
-  if (!animation_speed_ms_) {
+  if (animation_speed_.is_zero()) {
     FinalizeAnimation();
   } else {
     user_changed_animation_timer_.reset(new base::RepeatingTimer());
     user_changed_animation_timer_->Start(
-        FROM_HERE, base::TimeDelta::FromMilliseconds(animation_speed_ms_),
-        base::Bind(&UserSwitchAnimatorChromeOS::AdvanceUserTransitionAnimation,
-                   base::Unretained(this)));
+        FROM_HERE, animation_speed_,
+        base::BindRepeating(&UserSwitchAnimator::AdvanceUserTransitionAnimation,
+                            base::Unretained(this)));
   }
 }
 
-UserSwitchAnimatorChromeOS::~UserSwitchAnimatorChromeOS() {
+UserSwitchAnimator::~UserSwitchAnimator() {
   FinalizeAnimation();
 }
 
 // static
-bool UserSwitchAnimatorChromeOS::CoversScreen(aura::Window* window) {
+bool UserSwitchAnimator::CoversScreen(aura::Window* window) {
   // Full screen covers the screen naturally. Since a normal window can have the
   // same size as the work area, we only compare the bounds against the work
   // area.
@@ -120,7 +122,7 @@
   return work_area == bounds;
 }
 
-void UserSwitchAnimatorChromeOS::AdvanceUserTransitionAnimation() {
+void UserSwitchAnimator::AdvanceUserTransitionAnimation() {
   DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
 
   TransitionWallpaper(animation_step_);
@@ -138,7 +140,8 @@
     case ANIMATION_STEP_FINALIZE:
       user_changed_animation_timer_.reset();
       animation_step_ = ANIMATION_STEP_ENDED;
-      owner_->NotifyAfterUserSwitchAnimationFinished();
+      if (owner_->delegate_)
+        owner_->delegate_->OnDidSwitchActiveAccount();
       break;
     case ANIMATION_STEP_ENDED:
       NOTREACHED();
@@ -146,29 +149,31 @@
   }
 }
 
-void UserSwitchAnimatorChromeOS::CancelAnimation() {
+void UserSwitchAnimator::CancelAnimation() {
   animation_step_ = ANIMATION_STEP_ENDED;
 }
 
-void UserSwitchAnimatorChromeOS::FinalizeAnimation() {
+void UserSwitchAnimator::FinalizeAnimation() {
   user_changed_animation_timer_.reset();
   while (ANIMATION_STEP_ENDED != animation_step_)
     AdvanceUserTransitionAnimation();
 }
 
-void UserSwitchAnimatorChromeOS::TransitionWallpaper(
-    AnimationStep animation_step) {
+void UserSwitchAnimator::TransitionWallpaper(AnimationStep animation_step) {
+  WallpaperController* wallpaper_controller =
+      Shell::Get()->wallpaper_controller();
+
   // Handle the wallpaper switch.
   if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
     // Set the wallpaper cross dissolve animation duration to our complete
     // animation cycle for a fade in and fade out.
-    int duration =
-        NO_USER_COVERS_SCREEN == screen_cover_ ? (2 * animation_speed_ms_) : 0;
-    WallpaperControllerClient::Get()->SetAnimationDuration(
-        base::TimeDelta::FromMilliseconds(
-            std::max(duration, kMinimalAnimationTimeMS)));
+    base::TimeDelta duration =
+        animation_speed_ * (NO_USER_COVERS_SCREEN == screen_cover_ ? 2 : 0);
+    wallpaper_controller->SetAnimationDuration(
+        duration > kMinimalAnimationTime ? duration : kMinimalAnimationTime);
     if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
-      WallpaperControllerClient::Get()->ShowUserWallpaper(new_account_id_);
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
       wallpaper_user_id_for_test_ =
           (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
           new_account_id_.Serialize();
@@ -176,37 +181,34 @@
   } else if (animation_step == ANIMATION_STEP_FINALIZE) {
     // Revert the wallpaper cross dissolve animation duration back to the
     // default.
-    if (screen_cover_ == NEW_USER_COVERS_SCREEN)
-      WallpaperControllerClient::Get()->ShowUserWallpaper(new_account_id_);
+    if (screen_cover_ == NEW_USER_COVERS_SCREEN) {
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
+    }
 
     // Coming here the wallpaper user id is the final result. No matter how we
     // got here.
     wallpaper_user_id_for_test_ = new_account_id_.Serialize();
-    WallpaperControllerClient::Get()->SetAnimationDuration(base::TimeDelta());
+    wallpaper_controller->SetAnimationDuration(base::TimeDelta());
   }
 }
 
-void UserSwitchAnimatorChromeOS::TransitionUserShelf(
-    AnimationStep animation_step) {
+void UserSwitchAnimator::TransitionUserShelf(AnimationStep animation_step) {
   if (animation_step != ANIMATION_STEP_SHOW_NEW_USER)
     return;
 
-  ChromeLauncherController* chrome_launcher_controller =
-      ChromeLauncherController::instance();
-  // Some unit tests have no ChromeLauncherController.
-  if (chrome_launcher_controller) {
-    chrome_launcher_controller->ActiveUserChanged(
-        new_account_id_.GetUserEmail());
-  }
+  if (owner_->delegate_)
+    owner_->delegate_->OnTransitionUserShelfToNewAccount();
 }
 
-void UserSwitchAnimatorChromeOS::TransitionWindows(
-    AnimationStep animation_step) {
+void UserSwitchAnimator::TransitionWindows(AnimationStep animation_step) {
   // Disable the window position manager and the MRU window tracker temporarily.
   UserChangeActionDisabler disabler;
 
   // Animation duration.
-  int duration = std::max(kMinimalAnimationTimeMS, 2 * animation_speed_ms_);
+  base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+      std::max(kMinimalAnimationTime.InMilliseconds(),
+               2 * animation_speed_.InMilliseconds()));
 
   switch (animation_step) {
     case ANIMATION_STEP_HIDE_OLD_USER: {
@@ -229,7 +231,7 @@
           // different than that of the for_show_account_id) should retrun to
           // their
           // original owners' desktops.
-          MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator itr =
+          MultiUserWindowManager::WindowToEntryMap::const_iterator itr =
               owner_->window_to_entry().find(window);
           DCHECK(itr != owner_->window_to_entry().end());
           if (show_for_account_id != itr->second->owner() &&
@@ -291,7 +293,7 @@
     case ANIMATION_STEP_FINALIZE: {
       // Reactivate the MRU window of the new user.
       aura::Window::Windows mru_list =
-          ash::Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+          Shell::Get()->mru_window_tracker()->BuildMruWindowList();
       if (!mru_list.empty()) {
         aura::Window* window = mru_list[0];
         if (owner_->IsWindowOnDesktopOfUser(window, new_account_id_) &&
@@ -312,13 +314,11 @@
   }
 }
 
-UserSwitchAnimatorChromeOS::TransitioningScreenCover
-UserSwitchAnimatorChromeOS::GetScreenCover(aura::Window* root_window) {
+UserSwitchAnimator::TransitioningScreenCover UserSwitchAnimator::GetScreenCover(
+    aura::Window* root_window) {
   TransitioningScreenCover cover = NO_USER_COVERS_SCREEN;
-  for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it_map =
-           owner_->window_to_entry().begin();
-       it_map != owner_->window_to_entry().end(); ++it_map) {
-    aura::Window* window = it_map->first;
+  for (auto& pair : owner_->window_to_entry()) {
+    aura::Window* window = pair.first;
     if (root_window && window->GetRootWindow() != root_window)
       continue;
     if (window->IsVisible() && CoversScreen(window)) {
@@ -337,7 +337,7 @@
   return cover;
 }
 
-void UserSwitchAnimatorChromeOS::BuildUserToWindowsListMap() {
+void UserSwitchAnimator::BuildUserToWindowsListMap() {
   // This is to be called only at the time this animation is constructed.
   DCHECK(windows_by_account_id_.empty());
 
@@ -362,3 +362,5 @@
     }
   }
 }
+
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h b/ash/multi_user/user_switch_animator.h
similarity index 81%
rename from chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h
rename to ash/multi_user/user_switch_animator.h
index fbfef100..2f7ba50b5 100644
--- a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h
+++ b/ash/multi_user/user_switch_animator.h
@@ -2,26 +2,31 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_
-#define CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_
+#ifndef ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
+#define ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
 
 #include <map>
 #include <memory>
 #include <string>
 
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/account_id/account_id.h"
 #include "ui/aura/window.h"
 
-class MultiUserWindowManagerChromeOS;
+namespace ash {
+
+class MultiUserWindowManager;
 
 // A class which performs transitions animations between users. Upon creation,
 // the animation gets started and upon destruction the animation gets finished
 // if not done yet.
 // Specifying |animation_disabled| upon creation will perform the transition
 // without visible animations.
-class UserSwitchAnimatorChromeOS {
+class ASH_EXPORT UserSwitchAnimator {
  public:
   // The animation step for the user change animation.
   enum AnimationStep {
@@ -31,10 +36,12 @@
     ANIMATION_STEP_ENDED           // The animation has ended.
   };
 
-  UserSwitchAnimatorChromeOS(MultiUserWindowManagerChromeOS* owner,
-                             const AccountId& new_account_id,
-                             int animation_speed_ms);
-  ~UserSwitchAnimatorChromeOS();
+  // Creates a UserSwitchAnimator to animate between the current user and
+  // |user_info|.
+  UserSwitchAnimator(MultiUserWindowManager* owner,
+                     mojom::WallpaperUserInfoPtr user_info,
+                     base::TimeDelta animation_speed);
+  ~UserSwitchAnimator();
 
   // Check if a window is covering the entire work area of the screen it is on.
   static bool CoversScreen(aura::Window* window);
@@ -92,13 +99,17 @@
   void BuildUserToWindowsListMap();
 
   // The owning window manager.
-  MultiUserWindowManagerChromeOS* owner_;
+  MultiUserWindowManager* owner_;
+
+  // Contains the wallpaper configuration for the user switching to. This is
+  // passed to the WallpaperController at the right time.
+  mojom::WallpaperUserInfoPtr wallpaper_user_info_;
 
   // The new user to set.
   AccountId new_account_id_;
 
   // The animation speed in ms. If 0, animations are disabled.
-  int animation_speed_ms_;
+  base::TimeDelta animation_speed_;
 
   // The next animation step for AdvanceUserTransitionAnimation().
   AnimationStep animation_step_;
@@ -117,7 +128,9 @@
   // For unit tests: Check which wallpaper was set.
   std::string wallpaper_user_id_for_test_;
 
-  DISALLOW_COPY_AND_ASSIGN(UserSwitchAnimatorChromeOS);
+  DISALLOW_COPY_AND_ASSIGN(UserSwitchAnimator);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_
+}  // namespace ash
+
+#endif  // ASH_MULTI_USER_USER_SWITCH_ANIMATOR_H_
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 452b59a..5e08383 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -69,7 +69,8 @@
     grid_icon_dimension_ = 64;
     grid_icon_bottom_padding_ = 24;
     grid_title_top_padding_ = 82;
-    grid_title_width_ = 96;
+    grid_title_horizontal_padding_ = 0;
+    grid_title_width_ = grid_tile_width_;
     grid_focus_dimension_ = 80;
     grid_focus_corner_radius_ = 12;
     suggestion_chip_icon_dimension_ = 16;
diff --git a/ash/system/network/network_tray_view.cc b/ash/system/network/network_tray_view.cc
index 891409e..72353b03 100644
--- a/ash/system/network/network_tray_view.cc
+++ b/ash/system/network/network_tray_view.cc
@@ -29,7 +29,10 @@
   return handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
 }
 
-NetworkTrayView::NetworkTrayView(Shelf* shelf) : TrayItemView(shelf) {
+NetworkTrayView::NetworkTrayView(Shelf* shelf)
+    : TrayItemView(shelf),
+      network_state_observer_(
+          std::make_unique<TrayNetworkStateObserver>(this)) {
   CreateImageView();
   UpdateNetworkStateHandlerIcon();
   UpdateConnectionStatus(GetConnectedNetwork(), true /* notify_a11y */);
@@ -43,6 +46,11 @@
   return "NetworkTrayView";
 }
 
+void NetworkTrayView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  node_data->SetName(connection_status_string_);
+  node_data->role = ax::mojom::Role::kButton;
+}
+
 views::View* NetworkTrayView::GetTooltipHandlerForPoint(
     const gfx::Point& point) {
   return GetLocalBounds().Contains(point) ? this : nullptr;
@@ -56,6 +64,28 @@
   return true;
 }
 
+void NetworkTrayView::NetworkIconChanged() {
+  UpdateNetworkStateHandlerIcon();
+  UpdateConnectionStatus(GetConnectedNetwork(), false /* notify_a11y */);
+}
+
+void NetworkTrayView::OnSessionStateChanged(
+    session_manager::SessionState state) {
+  UpdateNetworkStateHandlerIcon();
+}
+
+void NetworkTrayView::NetworkStateChanged(bool notify_a11y) {
+  UpdateNetworkStateHandlerIcon();
+  UpdateConnectionStatus(GetConnectedNetwork(), notify_a11y);
+}
+
+void NetworkTrayView::UpdateIcon(bool tray_icon_visible,
+                                 const gfx::ImageSkia& image) {
+  image_view()->SetImage(image);
+  SetVisible(tray_icon_visible);
+  SchedulePaint();
+}
+
 void NetworkTrayView::UpdateNetworkStateHandlerIcon() {
   gfx::ImageSkia image;
   base::string16 name;
@@ -76,21 +106,6 @@
     network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
 }
 
-void NetworkTrayView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->SetName(connection_status_string_);
-  node_data->role = ax::mojom::Role::kButton;
-}
-
-void NetworkTrayView::NetworkIconChanged() {
-  UpdateNetworkStateHandlerIcon();
-  UpdateConnectionStatus(GetConnectedNetwork(), false /* notify_a11y */);
-}
-
-void NetworkTrayView::OnSessionStateChanged(
-    session_manager::SessionState state) {
-  UpdateNetworkStateHandlerIcon();
-}
-
 void NetworkTrayView::UpdateConnectionStatus(
     const NetworkState* connected_network,
     bool notify_a11y) {
@@ -146,12 +161,5 @@
   }
 }
 
-void NetworkTrayView::UpdateIcon(bool tray_icon_visible,
-                                 const gfx::ImageSkia& image) {
-  image_view()->SetImage(image);
-  SetVisible(tray_icon_visible);
-  SchedulePaint();
-}
-
 }  // namespace tray
 }  // namespace ash
diff --git a/ash/system/network/network_tray_view.h b/ash/system/network/network_tray_view.h
index 05cb9c7..8ed4738 100644
--- a/ash/system/network/network_tray_view.h
+++ b/ash/system/network/network_tray_view.h
@@ -5,8 +5,11 @@
 #ifndef ASH_SYSTEM_NETWORK_NETWORK_TRAY_VIEW_H_
 #define ASH_SYSTEM_NETWORK_NETWORK_TRAY_VIEW_H_
 
+#include <memory>
+
 #include "ash/session/session_observer.h"
 #include "ash/system/network/network_icon_animation_observer.h"
+#include "ash/system/network/tray_network_state_observer.h"
 #include "ash/system/tray/tray_item_view.h"
 #include "base/macros.h"
 
@@ -22,7 +25,8 @@
 
 class NetworkTrayView : public TrayItemView,
                         public network_icon::AnimationObserver,
-                        public SessionObserver {
+                        public SessionObserver,
+                        public TrayNetworkStateObserver::Delegate {
  public:
   explicit NetworkTrayView(Shelf* shelf);
 
@@ -30,8 +34,6 @@
 
   const char* GetClassName() const override;
 
-  void UpdateNetworkStateHandlerIcon();
-
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
@@ -44,16 +46,23 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
-  // Updates connection status and notifies accessibility event when necessary.
-  void UpdateConnectionStatus(const chromeos::NetworkState* connected_network,
-                              bool notify_a11y);
+  // TrayNetworkStateObserver::Delegate:
+  void NetworkStateChanged(bool notify_a11y) override;
 
  private:
   void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image);
 
+  void UpdateNetworkStateHandlerIcon();
+
+  // Updates connection status and notifies accessibility event when necessary.
+  void UpdateConnectionStatus(const chromeos::NetworkState* connected_network,
+                              bool notify_a11y);
+
   base::string16 connection_status_string_;
   base::string16 connection_status_tooltip_;
 
+  std::unique_ptr<TrayNetworkStateObserver> network_state_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkTrayView);
 };
 
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 325c21aa..aca7029 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -15,7 +15,6 @@
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/network_tray_view.h"
-#include "ash/system/network/tray_network_state_observer.h"
 #include "ash/system/power/tray_power.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_constants.h"
@@ -39,7 +38,7 @@
 
 class UnifiedSystemTray::UiDelegate : public MessageCenterUiDelegate {
  public:
-  UiDelegate(UnifiedSystemTray* owner);
+  explicit UiDelegate(UnifiedSystemTray* owner);
   ~UiDelegate() override;
 
   // MessageCenterUiDelegate:
@@ -113,36 +112,6 @@
   owner_->HideBubbleInternal();
 }
 
-class UnifiedSystemTray::NetworkStateDelegate
-    : public TrayNetworkStateObserver::Delegate {
- public:
-  explicit NetworkStateDelegate(tray::NetworkTrayView* tray_view);
-  ~NetworkStateDelegate() override;
-
-  // TrayNetworkStateObserver::Delegate
-  void NetworkStateChanged(bool notify_a11y) override;
-
- private:
-  tray::NetworkTrayView* const tray_view_;
-  const std::unique_ptr<TrayNetworkStateObserver> network_state_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkStateDelegate);
-};
-
-UnifiedSystemTray::NetworkStateDelegate::NetworkStateDelegate(
-    tray::NetworkTrayView* tray_view)
-    : tray_view_(tray_view),
-      network_state_observer_(
-          std::make_unique<TrayNetworkStateObserver>(this)) {}
-
-UnifiedSystemTray::NetworkStateDelegate::~NetworkStateDelegate() = default;
-
-void UnifiedSystemTray::NetworkStateDelegate::NetworkStateChanged(
-    bool notify_a11y) {
-  tray_view_->UpdateNetworkStateHandlerIcon();
-  tray_view_->UpdateConnectionStatus(tray::GetConnectedNetwork(), notify_a11y);
-}
-
 UnifiedSystemTray::UnifiedSystemTray(Shelf* shelf)
     : TrayBackgroundView(shelf),
       ui_delegate_(std::make_unique<UiDelegate>(this)),
@@ -163,8 +132,6 @@
   // It is possible in unit tests that it's missing.
   if (chromeos::NetworkHandler::IsInitialized()) {
     tray::NetworkTrayView* network_item = new tray::NetworkTrayView(shelf);
-    network_state_delegate_ =
-        std::make_unique<NetworkStateDelegate>(network_item);
     tray_container()->AddChildView(network_item);
   }
 
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index 0747124..d712984 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -99,7 +99,7 @@
   UnifiedSystemTrayModel* model() { return model_.get(); }
 
  private:
-  const static base::TimeDelta kNotificationCountUpdateDelay;
+  static const base::TimeDelta kNotificationCountUpdateDelay;
 
   friend class UnifiedSystemTrayTest;
   friend class UnifiedSystemTrayTestApi;
@@ -107,9 +107,6 @@
   // Private class implements MessageCenterUiDelegate.
   class UiDelegate;
 
-  // Private class implements TrayNetworkStateObserver::Delegate.
-  class NetworkStateDelegate;
-
   // Forwarded from UiDelegate.
   void ShowBubbleInternal(bool show_by_click);
   void HideBubbleInternal();
@@ -118,8 +115,6 @@
 
   const std::unique_ptr<UiDelegate> ui_delegate_;
 
-  std::unique_ptr<NetworkStateDelegate> network_state_delegate_;
-
   std::unique_ptr<UnifiedSystemTrayBubble> bubble_;
 
   // Model class that stores UnifiedSystemTray's UI specific variables.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9a560e6..f5d50e9 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -910,10 +910,11 @@
     "timer/timer.h",
     "token.cc",
     "token.h",
-    "trace_event/auto_open_close_event.cc",
     "trace_event/auto_open_close_event.h",
     "trace_event/blame_context.cc",
     "trace_event/blame_context.h",
+    "trace_event/builtin_categories.cc",
+    "trace_event/builtin_categories.h",
     "trace_event/category_registry.cc",
     "trace_event/category_registry.h",
     "trace_event/common/trace_event_common.h",
diff --git a/base/android/early_trace_event_binding.cc b/base/android/early_trace_event_binding.cc
index d5f5e79c..93369d93 100644
--- a/base/android/early_trace_event_binding.cc
+++ b/base/android/early_trace_event_binding.cc
@@ -14,7 +14,7 @@
 namespace base {
 namespace android {
 
-const char kEarlyJavaCategory[] = "EarlyJava";
+constexpr const char kEarlyJavaCategory[] = "EarlyJava";
 
 static void JNI_EarlyTraceEvent_RecordEarlyEvent(
     JNIEnv* env,
diff --git a/base/android/proguard/chromium_code.flags b/base/android/proguard/chromium_code.flags
index 8a3ec58..19347af 100644
--- a/base/android/proguard/chromium_code.flags
+++ b/base/android/proguard/chromium_code.flags
@@ -38,9 +38,6 @@
 -keepclasseswithmembers class * {
   @org.chromium.base.annotations.UsedByReflection <fields>;
 }
--keepclasseswithmembers,includedescriptorclasses class * {
-  native <methods>;
-}
 
 # Remove methods annotated with this if their return value is unused.
 -assumenosideeffects class ** {
diff --git a/base/android/proguard/explicit_jni_registration.flags b/base/android/proguard/explicit_jni_registration.flags
new file mode 100644
index 0000000..f95ea5d
--- /dev/null
+++ b/base/android/proguard/explicit_jni_registration.flags
@@ -0,0 +1,10 @@
+# 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.
+
+# -keepclasseswithmembers rather than -keepclasseswithmembernames to avoid
+# shrinking of unused native methods. Explicit JNI registration requires even
+# unused classes to be present during RegisterNatives().
+-keepclasseswithmembers,includedescriptorclasses class * {
+  native <methods>;
+}
diff --git a/base/android/proguard/implicit_jni_registration.flags b/base/android/proguard/implicit_jni_registration.flags
new file mode 100644
index 0000000..2495f7b
--- /dev/null
+++ b/base/android/proguard/implicit_jni_registration.flags
@@ -0,0 +1,9 @@
+# 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.
+
+# -keepclasseswithmembernames rather than -keepclasseswithmembers to allow
+# shrinking of unused native methods.
+-keepclasseswithmembernames,includedescriptorclasses class * {
+  native <methods>;
+}
diff --git a/base/android/trace_event_binding.cc b/base/android/trace_event_binding.cc
index 98bb8232..2123f94e 100644
--- a/base/android/trace_event_binding.cc
+++ b/base/android/trace_event_binding.cc
@@ -17,9 +17,9 @@
 
 namespace {
 
-const char kJavaCategory[] = "Java";
-const char kToplevelCategory[] = "toplevel";
-const char kLooperDispatchMessage[] = "Looper.dispatchMessage";
+constexpr const char kJavaCategory[] = "Java";
+constexpr const char kToplevelCategory[] = "toplevel";
+constexpr const char kLooperDispatchMessage[] = "Looper.dispatchMessage";
 
 // Boilerplate for safely converting Java data to TRACE_EVENT data.
 class TraceEventDataConverter {
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc
index 5c4b564..a00c5885 100644
--- a/base/debug/stack_trace_win.cc
+++ b/base/debug/stack_trace_win.cc
@@ -17,6 +17,7 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/synchronization/lock.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace debug {
@@ -315,16 +316,23 @@
   // Initialize stack walking.
   STACKFRAME64 stack_frame;
   memset(&stack_frame, 0, sizeof(stack_frame));
-#if defined(_WIN64)
+#if defined(ARCH_CPU_X86_64)
   int machine_type = IMAGE_FILE_MACHINE_AMD64;
   stack_frame.AddrPC.Offset = context_record->Rip;
   stack_frame.AddrFrame.Offset = context_record->Rbp;
   stack_frame.AddrStack.Offset = context_record->Rsp;
-#else
+#elif defined(ARCH_CPU_ARM64)
+  int machine_type = IMAGE_FILE_MACHINE_ARM64;
+  stack_frame.AddrPC.Offset = context_record->Pc;
+  stack_frame.AddrFrame.Offset = context_record->Fp;
+  stack_frame.AddrStack.Offset = context_record->Sp;
+#elif defined(ARCH_CPU_X86)
   int machine_type = IMAGE_FILE_MACHINE_I386;
   stack_frame.AddrPC.Offset = context_record->Eip;
   stack_frame.AddrFrame.Offset = context_record->Ebp;
   stack_frame.AddrStack.Offset = context_record->Esp;
+#else
+#error Unsupported Windows Arch
 #endif
   stack_frame.AddrPC.Mode = AddrModeFlat;
   stack_frame.AddrFrame.Mode = AddrModeFlat;
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 4aed48065..7598329 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -45,6 +45,16 @@
 
 }  // namespace
 
+// Unfortunately since we're not on C++17 we're required to provide an out of
+// line definition.
+constexpr MessageLoop::Type MessageLoop::TYPE_DEFAULT;
+constexpr MessageLoop::Type MessageLoop::TYPE_UI;
+constexpr MessageLoop::Type MessageLoop::TYPE_CUSTOM;
+constexpr MessageLoop::Type MessageLoop::TYPE_IO;
+#if defined(OS_ANDROID)
+constexpr MessageLoop::Type MessageLoop::TYPE_JAVA;
+#endif
+
 MessageLoop::MessageLoop(Type type)
     : MessageLoop(type, MessagePumpFactoryCallback()) {
   BindToCurrentThread();
@@ -72,30 +82,6 @@
   DCHECK((!pump_ && !IsBoundToCurrentThread()) ||
          !RunLoop::IsRunningOnCurrentThread());
 #endif  // !defined(OS_IOS)
-
-  // Clean up any unprocessed tasks, but take care: deleting a task could
-  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
-  // limit on the number of times we will allow a deleted task to generate more
-  // tasks.  Normally, we should only pass through this loop once or twice.  If
-  // we end up hitting the loop limit, then it is probably due to one task that
-  // is being stubborn.  Inspect the queues to see who is left.
-  bool tasks_remain;
-  for (int i = 0; i < 100; ++i) {
-    DeletePendingTasks();
-    // If we end up with empty queues, then break out of the loop.
-    tasks_remain = HasTasks();
-    if (!tasks_remain)
-      break;
-  }
-  DCHECK(!tasks_remain);
-
-  // Let interested parties have one last shot at accessing this.
-  for (auto& observer : destruction_observers_)
-    observer.WillDestroyCurrentMessageLoop();
-
-  // OK, now make it so that no one can find us.
-  if (IsBoundToCurrentThread())
-    MessageLoopCurrent::UnbindFromCurrentThreadInternal(this);
 }
 
 // static
@@ -159,18 +145,17 @@
 }
 
 bool MessageLoop::IsBoundToCurrentThread() const {
-  return MessageLoopCurrent::Get()->ToMessageLoopDeprecated() == this;
-}
-
-void MessageLoop::SetAddQueueTimeToTasks(bool enable) {
-  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
-  message_loop_impl_->SetAddQueueTimeToTasks(enable);
+  return message_loop_impl_->IsBoundToCurrentThread();
 }
 
 bool MessageLoop::IsIdleForTesting() {
   return message_loop_impl_->IsIdleForTesting();
 }
 
+MessageLoopBase* MessageLoop::GetMessageLoopBase() {
+  return message_loop_impl_.get();
+}
+
 //------------------------------------------------------------------------------
 
 // static
@@ -182,7 +167,7 @@
 
 MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory)
     : pump_(nullptr),
-      message_loop_impl_(std::make_unique<MessageLoopImpl>()),
+      message_loop_impl_(std::make_unique<MessageLoopImpl>(type)),
       type_(type),
       pump_factory_(std::move(pump_factory)) {
   // If type is TYPE_CUSTOM non-null pump_factory must be given.
@@ -203,7 +188,6 @@
 
   DCHECK(!MessageLoopCurrent::IsSet())
       << "should only have one message loop per thread";
-  MessageLoopCurrent::BindToCurrentThreadInternal(this);
 
   message_loop_impl_->BindToCurrentThread(std::move(pump));
 
@@ -222,11 +206,12 @@
   }
 }
 
+void MessageLoop::SetTimerSlack(TimerSlack timer_slack) {
+  message_loop_impl_->SetTimerSlack(timer_slack);
+}
+
 std::string MessageLoop::GetThreadName() const {
-  DCHECK_NE(kInvalidThreadId, thread_id_)
-      << "GetThreadName() must only be called after BindToCurrentThread()'s "
-      << "side-effects have been synchronized with this thread.";
-  return ThreadIdNameManager::GetInstance()->GetName(thread_id_);
+  return message_loop_impl_->GetThreadName();
 }
 
 const scoped_refptr<SingleThreadTaskRunner>& MessageLoop::task_runner() const {
@@ -247,18 +232,6 @@
   return message_loop_impl_->HasTasks();
 }
 
-void MessageLoop::SetTaskExecutionAllowed(bool allowed) {
-  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
-  if (allowed)
-    pump_->ScheduleWork();
-  message_loop_impl_->SetTaskExecutionAllowed(allowed);
-}
-
-bool MessageLoop::IsTaskExecutionAllowed() const {
-  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
-  return message_loop_impl_->IsTaskExecutionAllowed();
-}
-
 #if !defined(OS_NACL)
 
 //------------------------------------------------------------------------------
@@ -284,8 +257,7 @@
 
 #if defined(OS_IOS)
 void MessageLoopForUI::Attach() {
-  static_cast<MessagePumpUIApplication*>(pump_)->Attach(
-      message_loop_impl_.get());
+  message_loop_impl_->AttachToMessagePump();
 }
 #endif  // defined(OS_IOS)
 
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 1553996..4ffdff3 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -18,7 +18,6 @@
 #include "base/message_loop/message_pump.h"
 #include "base/message_loop/pending_task_queue.h"
 #include "base/message_loop/timer_slack.h"
-#include "base/observer_list.h"
 #include "base/pending_task.h"
 #include "base/run_loop.h"
 #include "base/synchronization/lock.h"
@@ -34,6 +33,7 @@
 class LazyThreadControllerForTest;
 namespace internal {
 class SequenceManagerImpl;
+class ThreadControllerImpl;
 }
 }  // namespace sequence_manager
 
@@ -79,11 +79,11 @@
 //
 // Please be SURE your task is reentrant (nestable) and all global variables
 // are stable and accessible before calling SetNestableTasksAllowed(true).
-class BASE_EXPORT MessageLoop {
+
+class BASE_EXPORT MessageLoopBase {
  public:
-  // TODO(gab): Migrate usage of this class to MessageLoopCurrent and remove
-  // this forwarded declaration.
-  using DestructionObserver = MessageLoopCurrent::DestructionObserver;
+  MessageLoopBase() = default;
+  virtual ~MessageLoopBase() = default;
 
   // A MessageLoop has a particular type, which indicates the set of
   // asynchronous events it may process in addition to tasks and timers.
@@ -118,6 +118,99 @@
 #endif  // defined(OS_ANDROID)
   };
 
+  // Returns true if this loop is |type|. This allows subclasses (especially
+  // those in tests) to specialize how they are identified.
+  virtual bool IsType(Type type) const = 0;
+
+  // Returns the name of the thread this message loop is bound to. This function
+  // is only valid when this message loop is running, BindToCurrentThread has
+  // already been called and has an "happens-before" relationship with this call
+  // (this relationship is obtained implicitly by the MessageLoop's task posting
+  // system unless calling this very early).
+  virtual std::string GetThreadName() const = 0;
+
+  using DestructionObserver = MessageLoopCurrent::DestructionObserver;
+
+  // Add a DestructionObserver, which will start receiving notifications
+  // immediately.
+  virtual void AddDestructionObserver(
+      DestructionObserver* destruction_observer) = 0;
+
+  // Remove a DestructionObserver.  It is safe to call this method while a
+  // DestructionObserver is receiving a notification callback.
+  virtual void RemoveDestructionObserver(
+      DestructionObserver* destruction_observer) = 0;
+
+  // TODO(altimin,yutak): Replace with base::TaskObserver.
+  using TaskObserver = MessageLoopCurrent::TaskObserver;
+
+  // These functions can only be called on the same thread that |this| is
+  // running on.
+  // These functions must not be called from a TaskObserver callback.
+  virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
+  virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
+
+  // When this functionality is enabled, the queue time will be recorded for
+  // posted tasks.
+  virtual void SetAddQueueTimeToTasks(bool enable) = 0;
+
+  // Returns true if this is the active MessageLoop for the current thread.
+  virtual bool IsBoundToCurrentThread() const = 0;
+
+  // Returns true if the message loop is idle (ignoring delayed tasks). This is
+  // the same condition which triggers DoWork() to return false: i.e.
+  // out of tasks which can be processed at the current run-level -- there might
+  // be deferred non-nestable tasks remaining if currently in a nested run
+  // level.
+  virtual bool IsIdleForTesting() = 0;
+
+  // Returns the MessagePump owned by this MessageLoop if any.
+  virtual MessagePump* GetMessagePump() const = 0;
+
+  // Sets a new TaskRunner for this message loop. If the message loop was
+  // already bound, this must be called on the thread to which it is bound.
+  // TODO(alexclarke): Remove this as part of https://crbug.com/825327.
+  virtual void SetTaskRunner(
+      scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;
+
+  // Gets the TaskRunner associated with this message loop.
+  // TODO(alexclarke): Remove this as part of https://crbug.com/825327.
+  virtual scoped_refptr<SingleThreadTaskRunner> GetTaskRunner() = 0;
+
+ protected:
+  friend class MessageLoopCurrent;
+  friend class MessageLoopCurrentForIO;
+  friend class MessageLoopCurrentForUI;
+  friend class sequence_manager::internal::ThreadControllerImpl;
+
+  // Explicitly allow or disallow task execution. Task execution is disallowed
+  // implicitly when we enter a nested runloop.
+  virtual void SetTaskExecutionAllowed(bool allowed) = 0;
+
+  // Whether task execution is allowed at the moment.
+  virtual bool IsTaskExecutionAllowed() const = 0;
+
+#if defined(OS_IOS)
+  virtual void AttachToMessagePump() = 0;
+#endif
+
+  // Set the timer slack for this message loop.
+  // TODO(alexclarke): Remove this as part of https://crbug.com/891670.
+  virtual void SetTimerSlack(TimerSlack timer_slack) = 0;
+};
+
+class BASE_EXPORT MessageLoop {
+ public:
+  // For migration convenience we define the Type enum.
+  using Type = MessageLoopBase::Type;
+  static constexpr Type TYPE_DEFAULT = Type::TYPE_DEFAULT;
+  static constexpr Type TYPE_UI = Type::TYPE_UI;
+  static constexpr Type TYPE_CUSTOM = Type::TYPE_CUSTOM;
+  static constexpr Type TYPE_IO = Type::TYPE_IO;
+#if defined(OS_ANDROID)
+  static constexpr Type TYPE_JAVA = Type::TYPE_JAVA;
+#endif  // defined(OS_ANDROID)
+
   // Normally, it is not necessary to instantiate a MessageLoop.  Instead, it
   // is typical to make use of the current thread's MessageLoop instance.
   explicit MessageLoop(Type type = TYPE_DEFAULT);
@@ -138,9 +231,7 @@
   static std::unique_ptr<MessagePump> CreateMessagePumpForType(Type type);
 
   // Set the timer slack for this message loop.
-  void SetTimerSlack(TimerSlack timer_slack) {
-    pump_->SetTimerSlack(timer_slack);
-  }
+  void SetTimerSlack(TimerSlack timer_slack);
 
   // Returns true if this loop is |type|. This allows subclasses (especially
   // those in tests) to specialize how they are identified.
@@ -156,17 +247,13 @@
   // system unless calling this very early).
   std::string GetThreadName() const;
 
-  // Gets the TaskRunner associated with this message loop.
-  const scoped_refptr<SingleThreadTaskRunner>& task_runner() const;
-
   // Sets a new TaskRunner for this message loop. If the message loop was
   // already bound, this must be called on the thread to which it is bound.
   void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
 
-  // TODO(https://crbug.com/825327): Remove users of TaskObservers through
-  // MessageLoop::current() and migrate the type back here.
-  //
-  // This alias is deprecated. Use base::TaskObserver instead.
+  // Gets the TaskRunner associated with this message loop.
+  const scoped_refptr<SingleThreadTaskRunner>& task_runner() const;
+
   // TODO(yutak): Replace all the use sites with base::TaskObserver.
   using TaskObserver = MessageLoopCurrent::TaskObserver;
 
@@ -176,10 +263,6 @@
   void AddTaskObserver(TaskObserver* task_observer);
   void RemoveTaskObserver(TaskObserver* task_observer);
 
-  // When this functionality is enabled, the queue time will be recorded for
-  // posted tasks.
-  void SetAddQueueTimeToTasks(bool enable);
-
   // Returns true if this is the active MessageLoop for the current thread.
   bool IsBoundToCurrentThread() const;
 
@@ -188,10 +271,13 @@
   // out of tasks which can be processed at the current run-level -- there might
   // be deferred non-nestable tasks remaining if currently in a nested run
   // level.
+  // TODO(alexclarke): Make this const when MessageLoopImpl goes away.
   bool IsIdleForTesting();
 
   MessageLoopImpl* impl_for_testing() { return message_loop_impl_.get(); }
 
+  MessageLoopBase* GetMessageLoopBase();
+
   //----------------------------------------------------------------------------
  protected:
   using MessagePumpFactoryCallback =
@@ -215,9 +301,6 @@
   std::unique_ptr<MessageLoopImpl> message_loop_impl_;
 
  private:
-  friend class MessageLoopCurrent;
-  friend class MessageLoopCurrentForIO;
-  friend class MessageLoopCurrentForUI;
   friend class MessageLoopTaskRunnerTest;
   friend class ScheduleWorkTest;
   friend class sequence_manager::LazyThreadControllerForTest;
@@ -243,12 +326,6 @@
       Type type,
       MessagePumpFactoryCallback pump_factory);
 
-  // Explicitly allow or disallow task execution. Task execution is disallowed
-  // implicitly when we enter a nested runloop.
-  void SetTaskExecutionAllowed(bool allowed);
-  // Whether task execution is allowed at the moment.
-  bool IsTaskExecutionAllowed() const;
-
   void DeletePendingTasks();
   bool HasTasks() const;
 
@@ -264,8 +341,6 @@
 
   const Type type_;
 
-  ObserverList<DestructionObserver>::Unchecked destruction_observers_;
-
   // pump_factory_.Run() is called to create a message pump for this loop
   // if |type_| is TYPE_CUSTOM and |pump_| is null.
   MessagePumpFactoryCallback pump_factory_;
diff --git a/base/message_loop/message_loop_current.cc b/base/message_loop/message_loop_current.cc
index 2bcd97a..e669669 100644
--- a/base/message_loop/message_loop_current.cc
+++ b/base/message_loop/message_loop_current.cc
@@ -10,13 +10,14 @@
 #include "base/message_loop/message_pump_for_ui.h"
 #include "base/no_destructor.h"
 #include "base/threading/thread_local.h"
+#include "base/threading/thread_task_runner_handle.h"
 
 namespace base {
 
 namespace {
 
-base::ThreadLocalPointer<MessageLoop>* GetTLSMessageLoop() {
-  static NoDestructor<ThreadLocalPointer<MessageLoop>> lazy_tls_ptr;
+base::ThreadLocalPointer<MessageLoopBase>* GetTLSMessageLoop() {
+  static NoDestructor<ThreadLocalPointer<MessageLoopBase>> lazy_tls_ptr;
   return lazy_tls_ptr.get();
 }
 
@@ -42,29 +43,28 @@
 
 void MessageLoopCurrent::AddDestructionObserver(
     DestructionObserver* destruction_observer) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  current_->destruction_observers_.AddObserver(destruction_observer);
+  DCHECK(current_->IsBoundToCurrentThread());
+  current_->AddDestructionObserver(destruction_observer);
 }
 
 void MessageLoopCurrent::RemoveDestructionObserver(
     DestructionObserver* destruction_observer) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  current_->destruction_observers_.RemoveObserver(destruction_observer);
+  DCHECK(current_->IsBoundToCurrentThread());
+  current_->RemoveDestructionObserver(destruction_observer);
 }
 
 std::string MessageLoopCurrent::GetThreadName() const {
   return current_->GetThreadName();
 }
 
-const scoped_refptr<SingleThreadTaskRunner>& MessageLoopCurrent::task_runner()
-    const {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return current_->task_runner();
+scoped_refptr<SingleThreadTaskRunner> MessageLoopCurrent::task_runner() const {
+  DCHECK(current_->IsBoundToCurrentThread());
+  return current_->GetTaskRunner();
 }
 
 void MessageLoopCurrent::SetTaskRunner(
     scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   current_->SetTaskRunner(std::move(task_runner));
 }
 
@@ -73,27 +73,27 @@
 }
 
 bool MessageLoopCurrent::IsIdleForTesting() {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   return current_->IsIdleForTesting();
 }
 
 void MessageLoopCurrent::AddTaskObserver(TaskObserver* task_observer) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   current_->AddTaskObserver(task_observer);
 }
 
 void MessageLoopCurrent::RemoveTaskObserver(TaskObserver* task_observer) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   current_->RemoveTaskObserver(task_observer);
 }
 
 void MessageLoopCurrent::SetAddQueueTimeToTasks(bool enable) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   current_->SetAddQueueTimeToTasks(enable);
 }
 
 void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
+  DCHECK(current_->IsBoundToCurrentThread());
   current_->SetTaskExecutionAllowed(allowed);
 }
 
@@ -112,14 +112,15 @@
 }
 
 // static
-void MessageLoopCurrent::BindToCurrentThreadInternal(MessageLoop* current) {
+void MessageLoopCurrent::BindToCurrentThreadInternal(MessageLoopBase* current) {
   DCHECK(!GetTLSMessageLoop()->Get())
       << "Can't register a second MessageLoop on the same thread.";
   GetTLSMessageLoop()->Set(current);
 }
 
 // static
-void MessageLoopCurrent::UnbindFromCurrentThreadInternal(MessageLoop* current) {
+void MessageLoopCurrent::UnbindFromCurrentThreadInternal(
+    MessageLoopBase* current) {
   DCHECK_EQ(current, GetTLSMessageLoop()->Get());
   GetTLSMessageLoop()->Set(nullptr);
 }
@@ -135,7 +136,7 @@
 
 // static
 MessageLoopCurrentForUI MessageLoopCurrentForUI::Get() {
-  MessageLoop* loop = GetTLSMessageLoop()->Get();
+  MessageLoopBase* loop = GetTLSMessageLoop()->Get();
   DCHECK(loop);
 #if defined(OS_ANDROID)
   DCHECK(loop->IsType(MessageLoop::TYPE_UI) ||
@@ -143,14 +144,12 @@
 #else   // defined(OS_ANDROID)
   DCHECK(loop->IsType(MessageLoop::TYPE_UI));
 #endif  // defined(OS_ANDROID)
-  auto* loop_for_ui = static_cast<MessageLoopForUI*>(loop);
-  return MessageLoopCurrentForUI(
-      loop_for_ui, static_cast<MessagePumpForUI*>(loop_for_ui->pump_));
+  return MessageLoopCurrentForUI(loop);
 }
 
 // static
 bool MessageLoopCurrentForUI::IsSet() {
-  MessageLoop* loop = GetTLSMessageLoop()->Get();
+  MessageLoopBase* loop = GetTLSMessageLoop()->Get();
   return loop &&
 #if defined(OS_ANDROID)
          (loop->IsType(MessageLoop::TYPE_UI) ||
@@ -160,6 +159,10 @@
 #endif  // defined(OS_ANDROID)
 }
 
+MessagePumpForUI* MessageLoopCurrentForUI::GetMessagePumpForUI() const {
+  return static_cast<MessagePumpForUI*>(current_->GetMessagePump());
+}
+
 #if defined(USE_OZONE) && !defined(OS_FUCHSIA) && !defined(OS_WIN)
 bool MessageLoopCurrentForUI::WatchFileDescriptor(
     int fd,
@@ -167,32 +170,33 @@
     MessagePumpForUI::Mode mode,
     MessagePumpForUI::FdWatchController* controller,
     MessagePumpForUI::FdWatcher* delegate) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->WatchFileDescriptor(fd, persistent, mode, controller, delegate);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForUI()->WatchFileDescriptor(fd, persistent, mode,
+                                                    controller, delegate);
 }
 #endif
 
 #if defined(OS_IOS)
 void MessageLoopCurrentForUI::Attach() {
-  static_cast<MessageLoopForUI*>(current_)->Attach();
+  current_->AttachToMessagePump();
 }
 #endif  // defined(OS_IOS)
 
 #if defined(OS_ANDROID)
 void MessageLoopCurrentForUI::Abort() {
-  static_cast<MessageLoopForUI*>(current_)->Abort();
+  GetMessagePumpForUI()->Abort();
 }
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_WIN)
 void MessageLoopCurrentForUI::AddMessagePumpObserver(
     MessagePumpForUI::Observer* observer) {
-  pump_->AddObserver(observer);
+  GetMessagePumpForUI()->AddObserver(observer);
 }
 
 void MessageLoopCurrentForUI::RemoveMessagePumpObserver(
     MessagePumpForUI::Observer* observer) {
-  pump_->RemoveObserver(observer);
+  GetMessagePumpForUI()->RemoveObserver(observer);
 }
 #endif  // defined(OS_WIN)
 
@@ -203,42 +207,44 @@
 
 // static
 MessageLoopCurrentForIO MessageLoopCurrentForIO::Get() {
-  MessageLoop* loop = GetTLSMessageLoop()->Get();
+  MessageLoopBase* loop = GetTLSMessageLoop()->Get();
   DCHECK(loop);
-  DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
-  auto* loop_for_io = static_cast<MessageLoopForIO*>(loop);
-  return MessageLoopCurrentForIO(
-      loop_for_io, static_cast<MessagePumpForIO*>(loop_for_io->pump_));
+  DCHECK(loop->IsType(MessageLoop::TYPE_IO));
+  return MessageLoopCurrentForIO(loop);
 }
 
 // static
 bool MessageLoopCurrentForIO::IsSet() {
-  MessageLoop* loop = GetTLSMessageLoop()->Get();
+  MessageLoopBase* loop = GetTLSMessageLoop()->Get();
   return loop && loop->IsType(MessageLoop::TYPE_IO);
 }
 
+MessagePumpForIO* MessageLoopCurrentForIO::GetMessagePumpForIO() const {
+  return static_cast<MessagePumpForIO*>(current_->GetMessagePump());
+}
+
 #if !defined(OS_NACL_SFI)
 
 #if defined(OS_WIN)
 HRESULT MessageLoopCurrentForIO::RegisterIOHandler(
     HANDLE file,
     MessagePumpForIO::IOHandler* handler) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->RegisterIOHandler(file, handler);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForIO()->RegisterIOHandler(file, handler);
 }
 
 bool MessageLoopCurrentForIO::RegisterJobObject(
     HANDLE job,
     MessagePumpForIO::IOHandler* handler) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->RegisterJobObject(job, handler);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForIO()->RegisterJobObject(job, handler);
 }
 
 bool MessageLoopCurrentForIO::WaitForIOCompletion(
     DWORD timeout,
     MessagePumpForIO::IOHandler* filter) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->WaitForIOCompletion(timeout, filter);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForIO()->WaitForIOCompletion(timeout, filter);
 }
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 bool MessageLoopCurrentForIO::WatchFileDescriptor(
@@ -247,8 +253,9 @@
     MessagePumpForIO::Mode mode,
     MessagePumpForIO::FdWatchController* controller,
     MessagePumpForIO::FdWatcher* delegate) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->WatchFileDescriptor(fd, persistent, mode, controller, delegate);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForIO()->WatchFileDescriptor(fd, persistent, mode,
+                                                    controller, delegate);
 }
 #endif  // defined(OS_WIN)
 
@@ -262,9 +269,9 @@
     zx_signals_t signals,
     MessagePumpForIO::ZxHandleWatchController* controller,
     MessagePumpForIO::ZxHandleWatcher* delegate) {
-  DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
-  return pump_->WatchZxHandle(handle, persistent, signals, controller,
-                              delegate);
+  DCHECK(current_->IsBoundToCurrentThread());
+  return GetMessagePumpForIO()->WatchZxHandle(handle, persistent, signals,
+                                              controller, delegate);
 }
 #endif
 
diff --git a/base/message_loop/message_loop_current.h b/base/message_loop/message_loop_current.h
index e6f6dc7..d469a72 100644
--- a/base/message_loop/message_loop_current.h
+++ b/base/message_loop/message_loop_current.h
@@ -23,7 +23,8 @@
 
 namespace base {
 
-class MessageLoop;
+class MessageLoopBase;
+class MessageLoopImpl;
 
 namespace sequence_manager {
 class LazyThreadControllerForTest;
@@ -112,7 +113,7 @@
   // Forwards to MessageLoop::task_runner().
   // DEPRECATED(https://crbug.com/616447): Use ThreadTaskRunnerHandle::Get()
   // instead of MessageLoopCurrent::Get()->task_runner().
-  const scoped_refptr<SingleThreadTaskRunner>& task_runner() const;
+  scoped_refptr<SingleThreadTaskRunner> task_runner() const;
 
   // Forwards to MessageLoop::SetTaskRunner().
   // DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop
@@ -172,7 +173,7 @@
     ~ScopedNestableTaskAllower();
 
    private:
-    MessageLoop* const loop_;
+    MessageLoopBase* const loop_;
     const bool old_state_;
   };
 
@@ -186,20 +187,20 @@
   // level.
   bool IsIdleForTesting();
 
+ protected:
   // Binds |current| to the current thread. It will from then on be the
   // MessageLoop driven by MessageLoopCurrent on this thread. This is only meant
   // to be invoked by the MessageLoop itself.
-  static void BindToCurrentThreadInternal(MessageLoop* current);
+  static void BindToCurrentThreadInternal(MessageLoopBase* current);
 
   // Unbinds |current| from the current thread. Must be invoked on the same
   // thread that invoked |BindToCurrentThreadInternal(current)|. This is only
   // meant to be invoked by the MessageLoop itself.
-  static void UnbindFromCurrentThreadInternal(MessageLoop* current);
+  static void UnbindFromCurrentThreadInternal(MessageLoopBase* current);
 
- protected:
-  explicit MessageLoopCurrent(MessageLoop* current) : current_(current) {}
+  explicit MessageLoopCurrent(MessageLoopBase* current) : current_(current) {}
 
-  friend class MessageLoop;
+  friend class MessageLoopImpl;
   friend class MessagePumpLibeventTest;
   friend class ScheduleWorkTest;
   friend class Thread;
@@ -208,14 +209,12 @@
   friend class MessageLoopTaskRunnerTest;
   friend class web::TestWebThreadBundle;
 
-  static MessagePump* GetMessagePumpForMessageLoop(MessageLoop* loop);
-
   // Return the pointer to MessageLoop for internal needs.
   // All other callers should call MessageLoopCurrent::Get().
   // TODO(altimin): Remove this.
-  MessageLoop* ToMessageLoopDeprecated() const { return current_; }
+  MessageLoopBase* ToMessageLoopBaseDeprecated() const { return current_; }
 
-  MessageLoop* current_;
+  MessageLoopBase* current_;
 };
 
 #if !defined(OS_NACL)
@@ -266,12 +265,10 @@
 #endif
 
  private:
-  MessageLoopCurrentForUI(MessageLoop* current, MessagePumpForUI* pump)
-      : MessageLoopCurrent(current), pump_(pump) {
-    DCHECK(pump_);
-  }
+  explicit MessageLoopCurrentForUI(MessageLoopBase* current)
+      : MessageLoopCurrent(current) {}
 
-  MessagePumpForUI* const pump_;
+  MessagePumpForUI* GetMessagePumpForUI() const;
 };
 
 #endif  // !defined(OS_NACL)
@@ -317,12 +314,10 @@
 #endif  // !defined(OS_NACL_SFI)
 
  private:
-  MessageLoopCurrentForIO(MessageLoop* current, MessagePumpForIO* pump)
-      : MessageLoopCurrent(current), pump_(pump) {
-    DCHECK(pump_);
-  }
+  explicit MessageLoopCurrentForIO(MessageLoopBase* current)
+      : MessageLoopCurrent(current) {}
 
-  MessagePumpForIO* const pump_;
+  MessagePumpForIO* GetMessagePumpForIO() const;
 };
 
 }  // namespace base
diff --git a/base/message_loop/message_loop_impl.cc b/base/message_loop/message_loop_impl.cc
index 2d669a6..49f9740 100644
--- a/base/message_loop/message_loop_impl.cc
+++ b/base/message_loop/message_loop_impl.cc
@@ -231,6 +231,22 @@
 //------------------------------------------------------------------------------
 
 MessageLoopImpl::~MessageLoopImpl() {
+  // Clean up any unprocessed tasks, but take care: deleting a task could
+  // result in the addition of more tasks (e.g., via DeleteSoon).  We set a
+  // limit on the number of times we will allow a deleted task to generate more
+  // tasks.  Normally, we should only pass through this loop once or twice.  If
+  // we end up hitting the loop limit, then it is probably due to one task that
+  // is being stubborn.  Inspect the queues to see who is left.
+  bool tasks_remain;
+  for (int i = 0; i < 100; ++i) {
+    DeletePendingTasks();
+    // If we end up with empty queues, then break out of the loop.
+    tasks_remain = HasTasks();
+    if (!tasks_remain)
+      break;
+  }
+  DCHECK(!tasks_remain);
+
 #if defined(OS_WIN)
   if (in_high_res_mode_)
     Time::ActivateHighResolutionTimer(false);
@@ -253,6 +269,14 @@
   // serialize ScheduleWork() call and as such that optimization isn't worth it.
   message_loop_controller_->DisconnectFromParent();
   underlying_task_runner_->Shutdown();
+
+  // Let interested parties have one last shot at accessing this.
+  for (auto& observer : destruction_observers_)
+    observer.WillDestroyCurrentMessageLoop();
+
+  // OK, now make it so that no one can find us.
+  if (IsBoundToCurrentThread())
+    MessageLoopCurrent::UnbindFromCurrentThreadInternal(this);
 }
 
 // TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns
@@ -292,8 +316,9 @@
 
 //------------------------------------------------------------------------------
 
-MessageLoopImpl::MessageLoopImpl()
-    : message_loop_controller_(
+MessageLoopImpl::MessageLoopImpl(MessageLoopBase::Type type)
+    : type_(type),
+      message_loop_controller_(
           new Controller(this)),  // Ownership transferred on the next line.
       underlying_task_runner_(MakeRefCounted<internal::MessageLoopTaskRunner>(
           WrapUnique(message_loop_controller_))),
@@ -319,6 +344,11 @@
       &sequence_local_storage_map_);
 
   RunLoop::RegisterDelegateForCurrentThread(this);
+  MessageLoopCurrent::BindToCurrentThreadInternal(this);
+}
+
+bool MessageLoopImpl::IsType(MessageLoopBase::Type type) const {
+  return type_ == type;
 }
 
 std::string MessageLoopImpl::GetThreadName() const {
@@ -344,6 +374,41 @@
   }
 }
 
+scoped_refptr<SingleThreadTaskRunner> MessageLoopImpl::GetTaskRunner() {
+  return task_runner_;
+}
+
+void MessageLoopImpl::AddDestructionObserver(
+    DestructionObserver* destruction_observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
+  destruction_observers_.AddObserver(destruction_observer);
+}
+
+void MessageLoopImpl::RemoveDestructionObserver(
+    DestructionObserver* destruction_observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
+  destruction_observers_.RemoveObserver(destruction_observer);
+}
+
+bool MessageLoopImpl::IsBoundToCurrentThread() const {
+  return MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated() == this;
+}
+
+MessagePump* MessageLoopImpl::GetMessagePump() const {
+  return pump_.get();
+}
+
+#if defined(OS_IOS)
+void MessageLoopImpl::AttachToMessagePump() {
+  DCHECK_EQ(type_, MessageLoopBase::TYPE_UI);
+  static_cast<MessagePumpUIApplication*>(pump_.get())->Attach(this);
+}
+#endif  // defined(OS_IOS)
+
+void MessageLoopImpl::SetTimerSlack(TimerSlack timer_slack) {
+  pump_->SetTimerSlack(timer_slack);
+}
+
 void MessageLoopImpl::Run(bool application_tasks_allowed) {
   DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
   if (application_tasks_allowed && !task_execution_allowed_) {
@@ -462,10 +527,14 @@
 }
 
 void MessageLoopImpl::SetTaskExecutionAllowed(bool allowed) {
+  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
+  if (allowed)
+    pump_->ScheduleWork();
   task_execution_allowed_ = allowed;
 }
 
 bool MessageLoopImpl::IsTaskExecutionAllowed() const {
+  DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
   return task_execution_allowed_;
 }
 
diff --git a/base/message_loop/message_loop_impl.h b/base/message_loop/message_loop_impl.h
index 429bb56..42736ec4 100644
--- a/base/message_loop/message_loop_impl.h
+++ b/base/message_loop/message_loop_impl.h
@@ -14,7 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_pump.h"
 #include "base/message_loop/pending_task_queue.h"
 #include "base/message_loop/timer_slack.h"
@@ -40,27 +40,39 @@
 // basic scheduling functionality. MessageLoopImpl is the legacy implementation,
 // which is being deprecated and replaced with SequenceManager-based
 // implementation (crbug.com/891670).
-class BASE_EXPORT MessageLoopImpl : public MessagePump::Delegate,
+class BASE_EXPORT MessageLoopImpl : public MessageLoopBase,
+                                    public MessagePump::Delegate,
                                     public RunLoop::Delegate {
  public:
   // Create an unbound MessageLoopImpl implementation.
   // Pump will be created by owning MessageLoop and will be passed via
   // BindToCurrentThread.
-  explicit MessageLoopImpl();
+  explicit MessageLoopImpl(MessageLoopBase::Type type);
 
   ~MessageLoopImpl() override;
 
-  // Set the timer slack for this message loop.
-  void SetTimerSlack(TimerSlack timer_slack) {
-    pump_->SetTimerSlack(timer_slack);
-  }
-
-  // Returns the name of the thread this message loop is bound to. This function
-  // is only valid when this message loop is running, BindToCurrentThread has
-  // already been called and has an "happens-before" relationship with this call
-  // (this relationship is obtained implicitly by the MessageLoop's task posting
-  // system unless calling this very early).
-  std::string GetThreadName() const;
+  // MessageLoopBase implementation:
+  bool IsType(MessageLoopBase::Type type) const override;
+  std::string GetThreadName() const override;
+  void SetTaskRunner(
+      scoped_refptr<SingleThreadTaskRunner> task_runner) override;
+  scoped_refptr<SingleThreadTaskRunner> GetTaskRunner() override;
+  void AddDestructionObserver(
+      DestructionObserver* destruction_observer) override;
+  void RemoveDestructionObserver(
+      DestructionObserver* destruction_observer) override;
+  void AddTaskObserver(TaskObserver* task_observer) override;
+  void RemoveTaskObserver(TaskObserver* task_observer) override;
+  void SetAddQueueTimeToTasks(bool enable) override;
+  bool IsBoundToCurrentThread() const override;
+  MessagePump* GetMessagePump() const override;
+  bool IsIdleForTesting() override;
+  void SetTaskExecutionAllowed(bool allowed) override;
+  bool IsTaskExecutionAllowed() const override;
+#if defined(OS_IOS)
+  void AttachToMessagePump() override;
+#endif
+  void SetTimerSlack(TimerSlack timer_slack) override;
 
   // Gets the TaskRunner associated with this message loop.
   const scoped_refptr<SingleThreadTaskRunner>& task_runner() const {
@@ -69,30 +81,6 @@
 
   bool IsCurrent() const;
 
-  // Sets a new TaskRunner for this message loop. If the message loop was
-  // already bound, this must be called on the thread to which it is bound.
-  void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
-
-  // TODO(altimin,yutak): Replace with base::TaskObserver.
-  using TaskObserver = MessageLoopCurrent::TaskObserver;
-
-  // These functions can only be called on the same thread that |this| is
-  // running on.
-  // These functions must not be called from a TaskObserver callback.
-  void AddTaskObserver(TaskObserver* task_observer);
-  void RemoveTaskObserver(TaskObserver* task_observer);
-
-  // When this functionality is enabled, the queue time will be recorded for
-  // posted tasks.
-  void SetAddQueueTimeToTasks(bool enable);
-
-  // Returns true if the message loop is idle (ignoring delayed tasks). This is
-  // the same condition which triggers DoWork() to return false: i.e.
-  // out of tasks which can be processed at the current run-level -- there might
-  // be deferred non-nestable tasks remaining if currently in a nested run
-  // level.
-  bool IsIdleForTesting();
-
   // Runs the specified PendingTask.
   void RunTask(PendingTask* pending_task);
 
@@ -106,12 +94,6 @@
   // Returns whether there are any pending tasks owned by MessageLoop.
   bool HasTasks();
 
-  // Explicitly allow or disallow task execution. Task execution is disallowed
-  // implicitly when we enter a nested RunLoop.
-  void SetTaskExecutionAllowed(bool allowed);
-  // Whether task execution is allowed at the moment.
-  bool IsTaskExecutionAllowed() const;
-
   //----------------------------------------------------------------------------
  protected:
   std::unique_ptr<MessagePump> pump_;
@@ -164,6 +146,8 @@
   bool DoDelayedWork(TimeTicks* next_delayed_work_time) override;
   bool DoIdleWork() override;
 
+  const MessageLoopBase::Type type_;
+
 #if defined(OS_WIN)
   // Tracks if we have requested high resolution timers. Its only use is to
   // turn off the high resolution timer upon loop destruction.
@@ -223,6 +207,8 @@
   std::unique_ptr<internal::ScopedSetSequenceLocalStorageMapForCurrentThread>
       scoped_set_sequence_local_storage_map_for_current_thread_;
 
+  ObserverList<DestructionObserver>::Unchecked destruction_observers_;
+
   // Verifies that calls are made on the thread on which BindToCurrentThread()
   // was invoked.
   THREAD_CHECKER(bound_thread_checker_);
diff --git a/base/message_loop/message_loop_task_runner_unittest.cc b/base/message_loop/message_loop_task_runner_unittest.cc
index 72645832..77d85f4 100644
--- a/base/message_loop/message_loop_task_runner_unittest.cc
+++ b/base/message_loop/message_loop_task_runner_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/debug/leak_annotations.h"
 #include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_impl.h"
 #include "base/message_loop/message_loop_task_runner.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
@@ -34,6 +35,10 @@
     return thread->message_loop();
   }
 
+  MessageLoopBase* MessageLoopBaseForThread(base::Thread* thread) {
+    return thread->message_loop()->GetMessageLoopBase();
+  }
+
  protected:
   void SetUp() override {
     // Use SetUp() instead of the constructor to avoid posting a task to a
@@ -59,26 +64,26 @@
   // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
   class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
    public:
-    LoopRecorder(MessageLoop** run_on,
-                 MessageLoop** deleted_on,
+    LoopRecorder(MessageLoopBase** run_on,
+                 MessageLoopBase** deleted_on,
                  int* destruct_order)
         : run_on_(run_on),
           deleted_on_(deleted_on),
           destruct_order_(destruct_order) {}
 
     void RecordRun() {
-      *run_on_ = MessageLoopCurrent::Get()->ToMessageLoopDeprecated();
+      *run_on_ = MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated();
     }
 
    private:
     friend class RefCountedThreadSafe<LoopRecorder>;
     ~LoopRecorder() {
-      *deleted_on_ = MessageLoopCurrent::Get()->ToMessageLoopDeprecated();
+      *deleted_on_ = MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated();
       *destruct_order_ = g_order.GetNext();
     }
 
-    MessageLoop** run_on_;
-    MessageLoop** deleted_on_;
+    MessageLoopBase** run_on_;
+    MessageLoopBase** deleted_on_;
     int* destruct_order_;
   };
 
@@ -107,11 +112,11 @@
 AtomicSequenceNumber MessageLoopTaskRunnerTest::g_order;
 
 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_Basic) {
-  MessageLoop* task_run_on = nullptr;
-  MessageLoop* task_deleted_on = nullptr;
+  MessageLoopBase* task_run_on = nullptr;
+  MessageLoopBase* task_deleted_on = nullptr;
   int task_delete_order = -1;
-  MessageLoop* reply_run_on = nullptr;
-  MessageLoop* reply_deleted_on = nullptr;
+  MessageLoopBase* reply_run_on = nullptr;
+  MessageLoopBase* reply_deleted_on = nullptr;
   int reply_delete_order = -1;
 
   scoped_refptr<LoopRecorder> task_recorder =
@@ -132,19 +137,19 @@
   UnblockTaskThread();
   RunLoop().Run();
 
-  EXPECT_EQ(MessageLoopForThread(&task_thread_), task_run_on);
-  EXPECT_EQ(MessageLoopForThread(&task_thread_), task_deleted_on);
-  EXPECT_EQ(current_loop_.get(), reply_run_on);
-  EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+  EXPECT_EQ(MessageLoopBaseForThread(&task_thread_), task_run_on);
+  EXPECT_EQ(MessageLoopBaseForThread(&task_thread_), task_deleted_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), reply_run_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), reply_deleted_on);
   EXPECT_LT(task_delete_order, reply_delete_order);
 }
 
 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
-  MessageLoop* task_run_on = nullptr;
-  MessageLoop* task_deleted_on = nullptr;
+  MessageLoopBase* task_run_on = nullptr;
+  MessageLoopBase* task_deleted_on = nullptr;
   int task_delete_order = -1;
-  MessageLoop* reply_run_on = nullptr;
-  MessageLoop* reply_deleted_on = nullptr;
+  MessageLoopBase* reply_run_on = nullptr;
+  MessageLoopBase* reply_deleted_on = nullptr;
   int reply_delete_order = -1;
 
   scoped_refptr<LoopRecorder> task_recorder =
@@ -174,11 +179,11 @@
 }
 
 TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_SameLoop) {
-  MessageLoop* task_run_on = nullptr;
-  MessageLoop* task_deleted_on = nullptr;
+  MessageLoopBase* task_run_on = nullptr;
+  MessageLoopBase* task_deleted_on = nullptr;
   int task_delete_order = -1;
-  MessageLoop* reply_run_on = nullptr;
-  MessageLoop* reply_deleted_on = nullptr;
+  MessageLoopBase* reply_run_on = nullptr;
+  MessageLoopBase* reply_deleted_on = nullptr;
   int reply_delete_order = -1;
 
   scoped_refptr<LoopRecorder> task_recorder =
@@ -199,10 +204,10 @@
 
   RunLoop().Run();
 
-  EXPECT_EQ(current_loop_.get(), task_run_on);
-  EXPECT_EQ(current_loop_.get(), task_deleted_on);
-  EXPECT_EQ(current_loop_.get(), reply_run_on);
-  EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), task_run_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), task_deleted_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), reply_run_on);
+  EXPECT_EQ(current_loop_->GetMessageLoopBase(), reply_deleted_on);
   EXPECT_LT(task_delete_order, reply_delete_order);
 }
 
@@ -211,11 +216,11 @@
        DISABLED_PostTaskAndReply_DeadReplyTaskRunnerBehavior) {
   // Annotate the scope as having memory leaks to suppress heapchecker reports.
   ANNOTATE_SCOPED_MEMORY_LEAK;
-  MessageLoop* task_run_on = nullptr;
-  MessageLoop* task_deleted_on = nullptr;
+  MessageLoopBase* task_run_on = nullptr;
+  MessageLoopBase* task_deleted_on = nullptr;
   int task_delete_order = -1;
-  MessageLoop* reply_run_on = nullptr;
-  MessageLoop* reply_deleted_on = nullptr;
+  MessageLoopBase* reply_run_on = nullptr;
+  MessageLoopBase* reply_deleted_on = nullptr;
   int reply_delete_order = -1;
 
   scoped_refptr<LoopRecorder> task_recorder =
@@ -248,8 +253,8 @@
   // Even if the reply task runner is already gone, the original task should
   // already be deleted. However, the reply which hasn't executed yet should
   // leak to avoid thread-safety issues.
-  EXPECT_EQ(task_loop, task_run_on);
-  EXPECT_EQ(task_loop, task_deleted_on);
+  EXPECT_EQ(task_loop->GetMessageLoopBase(), task_run_on);
+  EXPECT_EQ(task_loop->GetMessageLoopBase(), task_deleted_on);
   EXPECT_FALSE(reply_run_on);
   ASSERT_FALSE(reply_deleted_on);
 
diff --git a/base/metrics/user_metrics.cc b/base/metrics/user_metrics.cc
index 9fcc9e8a..a0c88a4 100644
--- a/base/metrics/user_metrics.cc
+++ b/base/metrics/user_metrics.cc
@@ -13,6 +13,7 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 namespace {
@@ -29,6 +30,7 @@
 }
 
 void RecordComputedAction(const std::string& action) {
+  TRACE_EVENT_INSTANT1("ui", "UserEvent", TRACE_EVENT_SCOPE_GLOBAL, "action", action);
   if (!g_task_runner.Get()) {
     DCHECK(g_callbacks.Get().empty());
     return;
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 7dab006..e42bec7 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -62,7 +62,7 @@
   // CreateUnboundSequenceManager(nullptr). Must not be called in any other
   // circumstances. Note it's assumed |message_loop| outlives the
   // SequenceManager.
-  virtual void BindToMessageLoop(MessageLoop* message_loop) = 0;
+  virtual void BindToMessageLoop(MessageLoopBase* message_loop_base) = 0;
 
   // Finishes the initialization for a SequenceManager created via
   // CreateUnboundSequenceManagerWithPump(). Must not be called in any other
@@ -158,7 +158,7 @@
   // Returns true iff this SequenceManager has no immediate work to do
   // (tasks with unexpired delay are fine, tasks with zero delay and
   // expired delay are not).
-  virtual bool IsIdleForTesting() const = 0;
+  virtual bool IsIdleForTesting() = 0;
 
   // The total number of posted tasks that haven't executed yet.
   virtual size_t GetPendingTaskCountForTesting() const = 0;
@@ -188,7 +188,7 @@
 // Remove when we get rid of MessageLoop.
 // TODO(scheduler-dev): Change this to CreateUnboundSequenceManagerWithPump.
 BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
-    MessageLoop* message_loop);
+    MessageLoopBase* message_loop_base);
 
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 5acde7e..3c0576c6 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -43,8 +43,8 @@
 }
 
 std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
-    MessageLoop* message_loop) {
-  return internal::SequenceManagerImpl::CreateUnbound(message_loop);
+    MessageLoopBase* message_loop_base) {
+  return internal::SequenceManagerImpl::CreateUnbound(message_loop_base);
 }
 
 namespace internal {
@@ -132,6 +132,10 @@
   // Let interested parties have one last shot at accessing this.
   for (auto& observer : main_thread_only().destruction_observers)
     observer.WillDestroyCurrentMessageLoop();
+
+  // OK, now make it so that no one can find us.
+  if (GetMessagePump())
+    MessageLoopCurrent::UnbindFromCurrentThreadInternal(this);
 }
 
 SequenceManagerImpl::AnyThread::AnyThread() = default;
@@ -150,8 +154,8 @@
 // static
 std::unique_ptr<SequenceManagerImpl>
 SequenceManagerImpl::CreateOnCurrentThread() {
-  auto manager =
-      CreateUnbound(MessageLoopCurrent::Get()->ToMessageLoopDeprecated());
+  std::unique_ptr<SequenceManagerImpl> manager =
+      CreateUnbound(MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated());
   manager->BindToCurrentThread();
   manager->CompleteInitializationOnBoundThread();
   return manager;
@@ -159,9 +163,9 @@
 
 // static
 std::unique_ptr<SequenceManagerImpl> SequenceManagerImpl::CreateUnbound(
-    MessageLoop* message_loop) {
+    MessageLoopBase* message_loop_base) {
   return WrapUnique(new SequenceManagerImpl(
-      ThreadControllerImpl::Create(message_loop,
+      ThreadControllerImpl::Create(message_loop_base,
                                    DefaultTickClock::GetInstance()),
       MessageLoop::Type::TYPE_DEFAULT));
 }
@@ -175,8 +179,9 @@
       type));
 }
 
-void SequenceManagerImpl::BindToMessageLoop(MessageLoop* message_loop) {
-  controller_->BindToCurrentThread(message_loop);
+void SequenceManagerImpl::BindToMessageLoop(
+    MessageLoopBase* message_loop_base) {
+  controller_->BindToCurrentThread(message_loop_base);
   CompleteInitializationOnBoundThread();
 }
 
@@ -194,6 +199,8 @@
 void SequenceManagerImpl::CompleteInitializationOnBoundThread() {
   controller_->AddNestingObserver(this);
   main_thread_only().nesting_observer_registered_ = true;
+  if (GetMessagePump())
+    MessageLoopCurrent::BindToCurrentThreadInternal(this);
 }
 
 void SequenceManagerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
@@ -844,7 +851,13 @@
   return controller_->IsTaskExecutionAllowed();
 }
 
-bool SequenceManagerImpl::IsIdleForTesting() const {
+#if defined(OS_IOS)
+void SequenceManagerImpl::AttachToMessagePump() {
+  return controller_->AttachToMessagePump();
+}
+#endif
+
+bool SequenceManagerImpl::IsIdleForTesting() {
   LazyNow lazy_now(controller_->GetClock());
   return DelayTillNextTask(&lazy_now) != TimeDelta();
 }
@@ -869,7 +882,11 @@
 
 void SequenceManagerImpl::SetTaskRunner(
     scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  NOTREACHED();
+  controller_->SetDefaultTaskRunner(task_runner);
+}
+
+scoped_refptr<SingleThreadTaskRunner> SequenceManagerImpl::GetTaskRunner() {
+  return controller_->GetDefaultTaskRunner();
 }
 
 std::string SequenceManagerImpl::GetThreadName() const {
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index 15524db..87a4c661 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -34,6 +34,7 @@
 #include "base/task/sequence_manager/task_queue_selector.h"
 #include "base/task/sequence_manager/thread_controller.h"
 #include "base/threading/thread_checker.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -73,7 +74,8 @@
     : public SequenceManager,
       public internal::SequencedTaskSource,
       public internal::TaskQueueSelector::Observer,
-      public RunLoop::NestingObserver {
+      public RunLoop::NestingObserver,
+      public MessageLoopBase {
  public:
   using Observer = SequenceManager::Observer;
 
@@ -94,15 +96,15 @@
   //
   // This function should be called only once per MessageLoop.
   static std::unique_ptr<SequenceManagerImpl> CreateUnbound(
-      MessageLoop* message_loop);
+      MessageLoopBase* message_loop_base);
 
   static std::unique_ptr<SequenceManagerImpl> CreateUnboundWithPump(
       MessageLoop::Type type);
 
   // SequenceManager implementation:
   void BindToCurrentThread() override;
-  void BindToMessageLoop(MessageLoop* message_loop) override;
-  void BindToMessagePump(std::unique_ptr<MessagePump> message_loop) override;
+  void BindToMessageLoop(MessageLoopBase* message_loop_base) override;
+  void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override;
   void CompleteInitializationOnBoundThread() override;
   void SetObserver(Observer* observer) override;
   void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
@@ -123,34 +125,38 @@
   const MetricRecordingSettings& GetMetricRecordingSettings() const override;
   void DeletePendingTasks() override;
   bool HasTasks() override;
-  bool IsIdleForTesting() const override;
   size_t GetPendingTaskCountForTesting() const override;
 
-  // Implementation of SequencedTaskSource:
+  // SequencedTaskSource implementation:
   Optional<PendingTask> TakeTask() override;
   void DidRunTask() override;
   TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override;
   bool HasPendingHighResolutionTasks() override;
   bool OnSystemIdle() override;
 
-  // Methods needed for MessageLoopCurrent support.
-  // TOOD(alexclarke): Introduce MessageLoopBase and make SequenceManagerImpl
-  // inherit from it, and mark these as override.
-  void AddTaskObserver(MessageLoop::TaskObserver* task_observer);
-  void RemoveTaskObserver(MessageLoop::TaskObserver* task_observer);
+  // MessageLoopBase implementation:
+  void AddTaskObserver(MessageLoop::TaskObserver* task_observer) override;
+  void RemoveTaskObserver(MessageLoop::TaskObserver* task_observer) override;
   void AddDestructionObserver(
-      MessageLoopCurrent::DestructionObserver* destruction_observer);
+      MessageLoopCurrent::DestructionObserver* destruction_observer) override;
   void RemoveDestructionObserver(
-      MessageLoopCurrent::DestructionObserver* destruction_observer);
+      MessageLoopCurrent::DestructionObserver* destruction_observer) override;
   // TODO(alexclarke): Remove this as part of https://crbug.com/825327.
-  void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
-  std::string GetThreadName() const;
-  bool IsBoundToCurrentThread() const;
-  MessagePump* GetMessagePump() const;
-  bool IsType(MessageLoop::Type type) const;
-  void SetAddQueueTimeToTasks(bool enable);
-  void SetTaskExecutionAllowed(bool allowed);
-  bool IsTaskExecutionAllowed() const;
+  void SetTaskRunner(
+      scoped_refptr<SingleThreadTaskRunner> task_runner) override;
+  // TODO(alexclarke): Remove this as part of https://crbug.com/825327.
+  scoped_refptr<SingleThreadTaskRunner> GetTaskRunner() override;
+  std::string GetThreadName() const override;
+  bool IsBoundToCurrentThread() const override;
+  MessagePump* GetMessagePump() const override;
+  bool IsType(MessageLoop::Type type) const override;
+  void SetAddQueueTimeToTasks(bool enable) override;
+  void SetTaskExecutionAllowed(bool allowed) override;
+  bool IsTaskExecutionAllowed() const override;
+#if defined(OS_IOS)
+  void AttachToMessagePump() override;
+#endif
+  bool IsIdleForTesting() override;
 
   // Requests that a task to process work is posted on the main task runner.
   // These tasks are de-duplicated in two buckets: main-thread and all other
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index afd12ea9..6e2c839 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -174,7 +174,8 @@
     start_time_ = mock_clock_.NowTicks();
 
     manager_ = SequenceManagerForTest::Create(
-        message_loop_.get(), ThreadTaskRunnerHandle::Get(), &mock_clock_);
+        message_loop_->GetMessageLoopBase(), ThreadTaskRunnerHandle::Get(),
+        &mock_clock_);
   }
 
   void SetUpWithMessagePump() {
@@ -3209,7 +3210,7 @@
       MakeRefCounted<TestSimpleTaskRunner>();
   {
     std::unique_ptr<SequenceManagerForTest> manager =
-        SequenceManagerForTest::Create(&message_loop,
+        SequenceManagerForTest::Create(message_loop.GetMessageLoopBase(),
                                        message_loop.task_runner(), nullptr);
     manager->SetDefaultTaskRunner(custom_task_runner);
     DCHECK_EQ(custom_task_runner, message_loop.task_runner());
@@ -3398,71 +3399,6 @@
   EXPECT_TRUE(run);
 }
 
-class ThreadForOffThreadInitializationTest : public Thread {
- public:
-  ThreadForOffThreadInitializationTest()
-      : base::Thread("ThreadForOffThreadInitializationTest") {}
-
-  void SequenceManagerCreated(
-      base::sequence_manager::SequenceManager* sequence_manager) {
-    // This executes on the creating thread.
-    DCHECK_CALLED_ON_VALID_SEQUENCE(creating_sequence_checker_);
-
-    queue_ = sequence_manager->CreateTaskQueue<TestTaskQueue>(
-        TaskQueue::Spec("default"));
-
-    // TaskQueue should not run tasks on the creating thread.
-    EXPECT_FALSE(queue_->RunsTasksInCurrentSequence());
-
-    // Override the default task runner before the thread is started.
-    sequence_manager->SetDefaultTaskRunner(queue_->task_runner());
-    EXPECT_EQ(queue_->task_runner(), message_loop()->task_runner());
-
-    // Post a task to the queue.
-    message_loop()->task_runner()->PostTask(
-        FROM_HERE,
-        Bind([](bool* did_run_task) { *did_run_task = true; }, &did_run_task_));
-  }
-
- private:
-  void Init() override {
-    // Queue should already be bound to this thread.
-    EXPECT_TRUE(queue_->RunsTasksInCurrentSequence());
-    EXPECT_EQ(queue_->task_runner(), ThreadTaskRunnerHandle::Get());
-  }
-
-  void Run(base::RunLoop* run_loop) override {
-    // Run the posted task.
-    Thread::Run(run_loop);
-    EXPECT_TRUE(did_run_task_);
-
-    // The |queue_| should be destructed on the creating thread.
-    queue_ = nullptr;
-  }
-
-  scoped_refptr<SingleThreadTaskRunner> original_task_runner_;
-  scoped_refptr<TestTaskQueue> queue_;
-  bool did_run_task_ = false;
-
-  SEQUENCE_CHECKER(creating_sequence_checker_);
-};
-
-// Verifies the integration of off-thread SequenceManager and MessageLoop
-// initialization when starting a base::Thread.
-TEST_P(SequenceManagerTestWithCustomInitialization, OffThreadInitialization) {
-  ThreadForOffThreadInitializationTest thread;
-
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
-  options.on_sequence_manager_created = base::BindRepeating(
-      &ThreadForOffThreadInitializationTest::SequenceManagerCreated,
-      base::Unretained(&thread));
-  ASSERT_TRUE(thread.StartWithOptions(options));
-
-  // Waits for the thread to complete execution.
-  thread.Stop();
-}
-
 TEST_P(SequenceManagerTestWithCustomInitialization,
        SequenceManagerCreatedBeforeMessageLoop) {
   std::unique_ptr<SequenceManager> manager =
@@ -3477,7 +3413,7 @@
   EXPECT_THAT(default_task_queue.get(), testing::NotNull());
 
   std::unique_ptr<MessageLoop> message_loop(new MessageLoop());
-  manager->BindToMessageLoop(message_loop.get());
+  manager->BindToMessageLoop(message_loop->GetMessageLoopBase());
 
   // Check that task posting works.
   std::vector<EnqueueOrder> run_order;
diff --git a/base/task/sequence_manager/sequence_manager_perftest.cc b/base/task/sequence_manager/sequence_manager_perftest.cc
index af81063..7e5ec20 100644
--- a/base/task/sequence_manager/sequence_manager_perftest.cc
+++ b/base/task/sequence_manager/sequence_manager_perftest.cc
@@ -148,7 +148,7 @@
   explicit SequenceManagerWithMessageLoopPerfTestDelegate(const char* name)
       : name_(name), message_loop_(new MessageLoopType()) {
     SetSequenceManager(SequenceManagerForTest::Create(
-        message_loop_.get(), message_loop_->task_runner(),
+        message_loop_->GetMessageLoopBase(), message_loop_->task_runner(),
         DefaultTickClock::GetInstance()));
   }
 
diff --git a/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc b/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
index 48efdd3..7ab9968 100644
--- a/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
+++ b/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
@@ -12,23 +12,24 @@
 namespace sequence_manager {
 
 LazyThreadControllerForTest::LazyThreadControllerForTest()
-    : ThreadControllerImpl(MessageLoopCurrent::Get()->ToMessageLoopDeprecated(),
-                           nullptr,
-                           DefaultTickClock::GetInstance()),
+    : ThreadControllerImpl(
+          MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated(),
+          nullptr,
+          DefaultTickClock::GetInstance()),
       thread_ref_(PlatformThread::CurrentRef()) {
-  if (message_loop_)
-    task_runner_ = message_loop_->task_runner();
+  if (message_loop_base_)
+    task_runner_ = message_loop_base_->GetTaskRunner();
 }
 
 LazyThreadControllerForTest::~LazyThreadControllerForTest() = default;
 
 void LazyThreadControllerForTest::EnsureMessageLoop() {
-  if (message_loop_)
+  if (message_loop_base_)
     return;
   DCHECK(RunsTasksInCurrentSequence());
-  message_loop_ = MessageLoopCurrent::Get()->ToMessageLoopDeprecated();
-  DCHECK(message_loop_);
-  task_runner_ = message_loop_->task_runner();
+  message_loop_base_ = MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated();
+  DCHECK(message_loop_base_);
+  task_runner_ = message_loop_base_->GetTaskRunner();
   if (pending_observer_) {
     RunLoop::AddNestingObserverOnCurrentThread(this);
     pending_observer_ = false;
@@ -40,7 +41,7 @@
 }
 
 bool LazyThreadControllerForTest::HasMessageLoop() {
-  return !!message_loop_;
+  return !!message_loop_base_;
 }
 
 void LazyThreadControllerForTest::AddNestingObserver(
@@ -86,9 +87,10 @@
     return;
   }
   // TODO(altimin): Refactor this to use STE::LifetimeObserver.
-  // We can't use message_loop_->IsBoundToCurrentThread as |message_loop_|
-  // might be deleted.
-  if (MessageLoopCurrent::Get()->ToMessageLoopDeprecated() != message_loop_)
+  // We can't use message_loop_base_->IsBoundToCurrentThread as
+  // |message_loop_base_| might be deleted.
+  if (MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated() !=
+      message_loop_base_)
     return;
   RunLoop::RemoveNestingObserverOnCurrentThread(this);
 }
@@ -119,11 +121,13 @@
 
 void LazyThreadControllerForTest::RestoreDefaultTaskRunner() {
   pending_default_task_runner_ = nullptr;
-  // We can't use message_loop_->IsBoundToCurrentThread as |message_loop_|
-  // might be deleted.
+  // We can't use message_loop_base_->IsBoundToCurrentThread as
+  // |message_loop_base_| might be deleted.
   if (HasMessageLoop() &&
-      MessageLoopCurrent::Get()->ToMessageLoopDeprecated() == message_loop_)
+      MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated() ==
+          message_loop_base_) {
     ThreadControllerImpl::RestoreDefaultTaskRunner();
+  }
 }
 
 }  // namespace sequence_manager
diff --git a/base/task/sequence_manager/test/sequence_manager_for_test.cc b/base/task/sequence_manager/test/sequence_manager_for_test.cc
index 05c46e50..ae2c1cc 100644
--- a/base/task/sequence_manager/test/sequence_manager_for_test.cc
+++ b/base/task/sequence_manager/test/sequence_manager_for_test.cc
@@ -13,21 +13,21 @@
 
 class ThreadControllerForTest : public internal::ThreadControllerImpl {
  public:
-  ThreadControllerForTest(MessageLoop* message_loop,
+  ThreadControllerForTest(MessageLoopBase* message_loop_base,
                           scoped_refptr<SingleThreadTaskRunner> task_runner,
                           const TickClock* time_source)
-      : ThreadControllerImpl(message_loop,
+      : ThreadControllerImpl(message_loop_base,
                              std::move(task_runner),
                              time_source) {}
 
   void AddNestingObserver(RunLoop::NestingObserver* observer) override {
-    if (!message_loop_)
+    if (!message_loop_base_)
       return;
     ThreadControllerImpl::AddNestingObserver(observer);
   }
 
   void RemoveNestingObserver(RunLoop::NestingObserver* observer) override {
-    if (!message_loop_)
+    if (!message_loop_base_)
       return;
     ThreadControllerImpl::RemoveNestingObserver(observer);
   }
@@ -44,12 +44,12 @@
 
 // static
 std::unique_ptr<SequenceManagerForTest> SequenceManagerForTest::Create(
-    MessageLoop* message_loop,
+    MessageLoopBase* message_loop_base,
     scoped_refptr<SingleThreadTaskRunner> task_runner,
     const TickClock* clock) {
   std::unique_ptr<SequenceManagerForTest> manager(
       new SequenceManagerForTest(std::make_unique<ThreadControllerForTest>(
-          message_loop, std::move(task_runner), clock)));
+          message_loop_base, std::move(task_runner), clock)));
   manager->BindToCurrentThread();
   manager->CompleteInitializationOnBoundThread();
   return manager;
diff --git a/base/task/sequence_manager/test/sequence_manager_for_test.h b/base/task/sequence_manager/test/sequence_manager_for_test.h
index 836c34b..7479c24 100644
--- a/base/task/sequence_manager/test/sequence_manager_for_test.h
+++ b/base/task/sequence_manager/test/sequence_manager_for_test.h
@@ -13,8 +13,6 @@
 
 namespace base {
 
-class MessageLoop;
-
 namespace sequence_manager {
 
 class SequenceManagerForTest : public internal::SequenceManagerImpl {
@@ -25,7 +23,7 @@
   // the given arguments. ThreadControllerImpl is slightly overridden to skip
   // nesting observers registration if message loop is absent.
   static std::unique_ptr<SequenceManagerForTest> Create(
-      MessageLoop* message_loop,
+      MessageLoopBase* message_loop_base,
       scoped_refptr<SingleThreadTaskRunner> task_runner,
       const TickClock* clock);
 
diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h
index 249d140..edef302 100644
--- a/base/task/sequence_manager/thread_controller.h
+++ b/base/task/sequence_manager/thread_controller.h
@@ -10,10 +10,11 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/sequence_manager/lazy_now.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 
 namespace base {
 
-class MessageLoop;
+class MessageLoopBase;
 class MessagePump;
 class TickClock;
 struct PendingTask;
@@ -70,12 +71,12 @@
   virtual void SetTimerSlack(TimerSlack timer_slack) = 0;
 
   // Completes delayed initialization of unbound ThreadControllers.
-  // BindToCurrentThread(MessageLoop*) or BindToCurrentThread(MessagePump*)
+  // BindToCurrentThread(MessageLoopBase*) or BindToCurrentThread(MessagePump*)
   // may only be called once.
-  virtual void BindToCurrentThread(MessageLoop* message_loop) = 0;
+  virtual void BindToCurrentThread(MessageLoopBase* message_loop_base) = 0;
 
   // Completes delayed initialization of unbound ThreadControllers.
-  // BindToCurrentThread(MessageLoop*) or BindToCurrentThread(MessagePump*)
+  // BindToCurrentThread(MessageLoopBase*) or BindToCurrentThread(MessagePump*)
   // may only be called once.
   virtual void BindToCurrentThread(
       std::unique_ptr<MessagePump> message_pump) = 0;
@@ -87,6 +88,13 @@
   // Returns the MessagePump we're bound to if any.
   virtual MessagePump* GetBoundMessagePump() const = 0;
 
+#if defined(OS_IOS)
+  // On iOS, the main message loop cannot be Run().  Instead call
+  // AttachToMessagePump(), which connects this ThreadController to the
+  // UI thread's CFRunLoop and allows PostTask() to work.
+  virtual void AttachToMessagePump() = 0;
+#endif
+
   // TODO(altimin): Get rid of the methods below.
   // These methods exist due to current integration of SequenceManager
   // with MessageLoop.
@@ -94,6 +102,7 @@
   virtual bool RunsTasksInCurrentSequence() = 0;
   virtual const TickClock* GetClock() = 0;
   virtual void SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner>) = 0;
+  virtual scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() = 0;
   virtual void RestoreDefaultTaskRunner() = 0;
   virtual void AddNestingObserver(RunLoop::NestingObserver* observer) = 0;
   virtual void RemoveNestingObserver(RunLoop::NestingObserver* observer) = 0;
diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc
index 9f3dce8..969b533 100644
--- a/base/task/sequence_manager/thread_controller_impl.cc
+++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -20,14 +20,14 @@
 namespace internal {
 
 ThreadControllerImpl::ThreadControllerImpl(
-    MessageLoop* message_loop,
+    MessageLoopBase* message_loop_base,
     scoped_refptr<SingleThreadTaskRunner> task_runner,
     const TickClock* time_source)
-    : message_loop_(message_loop),
+    : message_loop_base_(message_loop_base),
       task_runner_(task_runner),
       associated_thread_(AssociatedThreadId::CreateUnbound()),
-      message_loop_task_runner_(message_loop ? message_loop->task_runner()
-                                             : nullptr),
+      message_loop_task_runner_(
+          message_loop_base ? message_loop_base->GetTaskRunner() : nullptr),
       time_source_(time_source),
       weak_factory_(this) {
   immediate_do_work_closure_ =
@@ -49,10 +49,11 @@
 ThreadControllerImpl::MainSequenceOnly::~MainSequenceOnly() = default;
 
 std::unique_ptr<ThreadControllerImpl> ThreadControllerImpl::Create(
-    MessageLoop* message_loop,
+    MessageLoopBase* message_loop_base,
     const TickClock* time_source) {
   return WrapUnique(new ThreadControllerImpl(
-      message_loop, message_loop ? message_loop->task_runner() : nullptr,
+      message_loop_base,
+      message_loop_base ? message_loop_base->GetTaskRunner() : nullptr,
       time_source));
 }
 
@@ -65,9 +66,9 @@
 }
 
 void ThreadControllerImpl::SetTimerSlack(TimerSlack timer_slack) {
-  if (!message_loop_)
+  if (!message_loop_base_)
     return;
-  message_loop_->SetTimerSlack(timer_slack);
+  message_loop_base_->SetTimerSlack(timer_slack);
 }
 
 void ThreadControllerImpl::ScheduleWork() {
@@ -141,26 +142,32 @@
 #if DCHECK_IS_ON()
   default_task_runner_set_ = true;
 #endif
-  if (!message_loop_)
+  if (!message_loop_base_)
     return;
-  message_loop_->SetTaskRunner(task_runner);
+  message_loop_base_->SetTaskRunner(task_runner);
+}
+
+scoped_refptr<SingleThreadTaskRunner>
+ThreadControllerImpl::GetDefaultTaskRunner() {
+  return message_loop_base_->GetTaskRunner();
 }
 
 void ThreadControllerImpl::RestoreDefaultTaskRunner() {
-  if (!message_loop_)
+  if (!message_loop_base_)
     return;
-  message_loop_->SetTaskRunner(message_loop_task_runner_);
+  message_loop_base_->SetTaskRunner(message_loop_task_runner_);
 }
 
-void ThreadControllerImpl::BindToCurrentThread(MessageLoop* message_loop) {
-  DCHECK(!message_loop_);
-  DCHECK(message_loop);
+void ThreadControllerImpl::BindToCurrentThread(
+    MessageLoopBase* message_loop_base) {
+  DCHECK(!message_loop_base_);
+  DCHECK(message_loop_base);
 #if DCHECK_IS_ON()
   DCHECK(!default_task_runner_set_) << "This would undo SetDefaultTaskRunner";
 #endif
-  message_loop_ = message_loop;
-  task_runner_ = message_loop->task_runner();
-  message_loop_task_runner_ = message_loop->task_runner();
+  message_loop_base_ = message_loop_base;
+  task_runner_ = message_loop_base->GetTaskRunner();
+  message_loop_task_runner_ = message_loop_base->GetTaskRunner();
 }
 
 void ThreadControllerImpl::BindToCurrentThread(
@@ -322,6 +329,12 @@
   return nullptr;
 }
 
+#if defined(OS_IOS)
+void ThreadControllerImpl::AttachToMessagePump() {
+  NOTREACHED();
+}
+#endif
+
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h
index e0b8c86..710de65 100644
--- a/base/task/sequence_manager/thread_controller_impl.h
+++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -16,12 +16,13 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/sequence_manager/associated_thread_id.h"
 #include "base/task/sequence_manager/thread_controller.h"
+#include "build/build_config.h"
 
 namespace base {
 
 // TODO(kraynov): https://crbug.com/828835
 // Consider going away from using MessageLoop in the renderer process.
-class MessageLoop;
+class MessageLoopBase;
 
 namespace sequence_manager {
 namespace internal {
@@ -33,14 +34,14 @@
   ~ThreadControllerImpl() override;
 
   static std::unique_ptr<ThreadControllerImpl> Create(
-      MessageLoop* message_loop,
+      MessageLoopBase* message_loop_base,
       const TickClock* time_source);
 
   // ThreadController:
   void SetWorkBatchSize(int work_batch_size) override;
   void WillQueueTask(PendingTask* pending_task) override;
   void ScheduleWork() override;
-  void BindToCurrentThread(MessageLoop* message_loop) override;
+  void BindToCurrentThread(MessageLoopBase* message_loop_base) override;
   void BindToCurrentThread(std::unique_ptr<MessagePump> message_pump) override;
   void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override;
   void SetSequencedTaskSource(SequencedTaskSource* sequence) override;
@@ -48,6 +49,7 @@
   bool RunsTasksInCurrentSequence() override;
   const TickClock* GetClock() override;
   void SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner>) override;
+  scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override;
   void RestoreDefaultTaskRunner() override;
   void AddNestingObserver(RunLoop::NestingObserver* observer) override;
   void RemoveNestingObserver(RunLoop::NestingObserver* observer) override;
@@ -55,19 +57,22 @@
   void SetTaskExecutionAllowed(bool allowed) override;
   bool IsTaskExecutionAllowed() const override;
   MessagePump* GetBoundMessagePump() const override;
+#if defined(OS_IOS)
+  void AttachToMessagePump() override;
+#endif
 
   // RunLoop::NestingObserver:
   void OnBeginNestedRunLoop() override;
   void OnExitNestedRunLoop() override;
 
  protected:
-  ThreadControllerImpl(MessageLoop* message_loop,
+  ThreadControllerImpl(MessageLoopBase* message_loop_base,
                        scoped_refptr<SingleThreadTaskRunner> task_runner,
                        const TickClock* time_source);
 
   // TODO(altimin): Make these const. Blocked on removing
   // lazy initialisation support.
-  MessageLoop* message_loop_;
+  MessageLoopBase* message_loop_base_;
   scoped_refptr<SingleThreadTaskRunner> task_runner_;
 
   RunLoop::NestingObserver* nesting_observer_ = nullptr;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 590de21..1acf107f 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -10,6 +10,10 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
+#if defined(OS_MACOSX)
+#include "base/message_loop/message_pump_mac.h"
+#endif
+
 namespace base {
 namespace sequence_manager {
 namespace internal {
@@ -66,7 +70,7 @@
 }
 
 void ThreadControllerWithMessagePumpImpl::BindToCurrentThread(
-    MessageLoop* message_loop) {
+    MessageLoopBase* message_loop_base) {
   NOTREACHED()
       << "ThreadControllerWithMessagePumpImpl doesn't support MessageLoops";
 }
@@ -161,16 +165,24 @@
     scoped_refptr<SingleThreadTaskRunner> task_runner) {
   if (associated_thread_->thread_id == kInvalidThreadId) {
     // Save task runner, it will be set in BindToCurrentThread.
-    task_runner_to_set_ = task_runner;
+    task_runner_to_set_ = std::move(task_runner);
   } else {
     // Only one ThreadTaskRunnerHandle can exist at any time,
     // so reset the old one.
     main_thread_only().thread_task_runner_handle.reset();
     main_thread_only().thread_task_runner_handle =
         std::make_unique<ThreadTaskRunnerHandle>(task_runner);
+    AutoLock lock(task_runner_lock_);
+    task_runner_ = std::move(task_runner);
   }
 }
 
+scoped_refptr<SingleThreadTaskRunner>
+ThreadControllerWithMessagePumpImpl::GetDefaultTaskRunner() {
+  AutoLock lock(task_runner_lock_);
+  return task_runner_;
+}
+
 void ThreadControllerWithMessagePumpImpl::RestoreDefaultTaskRunner() {
   // There's no default task runner unlike with the MessageLoop.
   main_thread_only().thread_task_runner_handle.reset();
@@ -369,6 +381,12 @@
   return pump_.get();
 }
 
+#if defined(OS_IOS)
+void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() {
+  static_cast<MessagePumpUIApplication*>(pump_.get())->Attach(this);
+}
+#endif
+
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 9799c66..3cf9b45 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -14,9 +14,11 @@
 #include "base/task/sequence_manager/moveable_auto_lock.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/thread_controller.h"
+#include "base/thread_annotations.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/sequence_local_storage_map.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace sequence_manager {
@@ -40,7 +42,7 @@
 
   // ThreadController implementation:
   void SetSequencedTaskSource(SequencedTaskSource* task_source) override;
-  void BindToCurrentThread(MessageLoop* message_loop) override;
+  void BindToCurrentThread(MessageLoopBase* message_loop_base) override;
   void BindToCurrentThread(std::unique_ptr<MessagePump> message_pump) override;
   void SetWorkBatchSize(int work_batch_size) override;
   void WillQueueTask(PendingTask* pending_task) override;
@@ -51,6 +53,7 @@
   bool RunsTasksInCurrentSequence() override;
   void SetDefaultTaskRunner(
       scoped_refptr<SingleThreadTaskRunner> task_runner) override;
+  scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override;
   void RestoreDefaultTaskRunner() override;
   void AddNestingObserver(RunLoop::NestingObserver* observer) override;
   void RemoveNestingObserver(RunLoop::NestingObserver* observer) override;
@@ -58,7 +61,9 @@
   void SetTaskExecutionAllowed(bool allowed) override;
   bool IsTaskExecutionAllowed() const override;
   MessagePump* GetBoundMessagePump() const override;
-
+#if defined(OS_IOS)
+  void AttachToMessagePump() override;
+#endif
   // RunLoop::NestingObserver:
   void OnBeginNestedRunLoop() override;
   void OnExitNestedRunLoop() override;
@@ -136,6 +141,10 @@
   scoped_refptr<AssociatedThreadId> associated_thread_;
   MainThreadOnly main_thread_only_;
 
+  mutable Lock task_runner_lock_;
+  scoped_refptr<SingleThreadTaskRunner> task_runner_
+      GUARDED_BY(task_runner_lock_);
+
   // Protects |pump_| and |should_schedule_work_after_bind_| as work can be
   // scheduled from another thread before we create the pump.
   // TODO(altimin, crbug.com/901345): Remove this lock.
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 0bc6bf2..a228216 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/task/sequence_manager/sequence_manager.h"
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_local.h"
@@ -103,12 +102,6 @@
   message_loop_ = message_loop_owned.get();
   start_event_.Reset();
 
-  if (options.on_sequence_manager_created) {
-    sequence_manager_ =
-        sequence_manager::CreateUnboundSequenceManager(message_loop_);
-    options.on_sequence_manager_created.Run(sequence_manager_.get());
-  }
-
   // Hold |thread_lock_| while starting the new thread to synchronize with
   // Stop() while it's not guaranteed to be sequenced (until crbug/629139 is
   // fixed).
@@ -300,26 +293,12 @@
   PlatformThread::SetName(name_.c_str());
   ANNOTATE_THREAD_NAME(name_.c_str());  // Tell the name to race detector.
 
-  if (sequence_manager_) {
-    // Bind the SequenceManager before binding the MessageLoop, so that the
-    // TaskQueues are bound before the MessageLoop. This is required as one of
-    // the TaskQueues may have already replaced the MessageLoop's TaskRunner,
-    // and the MessageLoop's TaskRunner needs to be associated with this thread
-    // when we call MessageLoop::BindToCurrentThread().
-    sequence_manager_->BindToCurrentThread();
-  }
-
   // Lazily initialize the |message_loop| so that it can run on this thread.
   DCHECK(message_loop_);
   std::unique_ptr<MessageLoop> message_loop(message_loop_);
   message_loop_->BindToCurrentThread();
   message_loop_->SetTimerSlack(timer_slack_);
 
-  if (sequence_manager_) {
-    sequence_manager_->SetTimerSlack(timer_slack_);
-    sequence_manager_->CompleteInitializationOnBoundThread();
-  }
-
 #if defined(OS_POSIX) && !defined(OS_NACL)
   // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API.
   std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher;
@@ -364,8 +343,6 @@
   com_initializer.reset();
 #endif
 
-  sequence_manager_.reset();
-
   if (message_loop->type() != MessageLoop::TYPE_CUSTOM) {
     // Assert that RunLoop::QuitWhenIdle was called by ThreadQuitHelper. Don't
     // check for custom message pumps, because their shutdown might not allow
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 1133455..b8b890b 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -29,10 +29,6 @@
 class MessagePump;
 class RunLoop;
 
-namespace sequence_manager {
-class SequenceManager;
-}  // namespace sequence_manager
-
 // IMPORTANT: Instead of creating a base::Thread, consider using
 // base::Create(Sequenced|SingleThread)TaskRunnerWithTraits().
 //
@@ -65,8 +61,6 @@
  public:
   struct BASE_EXPORT Options {
     typedef Callback<std::unique_ptr<MessagePump>()> MessagePumpFactory;
-    using SequenceManagerCreatedCallback =
-        RepeatingCallback<void(sequence_manager::SequenceManager*)>;
 
     Options();
     Options(MessageLoop::Type type, size_t size);
@@ -86,12 +80,6 @@
     // MessageLoop::Type to TYPE_CUSTOM.
     MessagePumpFactory message_pump_factory;
 
-    // If set, the Thread will create a SequenceManager on the MessageLoop and
-    // execute the provided callback right after it was created. The callback
-    // will be executed on the creator thread before the new Thread is started.
-    // It is typically used to create TaskQueues for the SequenceManager.
-    SequenceManagerCreatedCallback on_sequence_manager_created;
-
     // Specifies the maximum stack size that the thread is allowed to use.
     // This does not necessarily correspond to the thread's initial stack size.
     // A value of 0 indicates that the default maximum should be used.
@@ -341,9 +329,6 @@
   // cleanup logic as required.
   bool using_external_message_loop_ = false;
 
-  // Optionally stores a SequenceManager that manages Tasks on the MessageLoop.
-  std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_;
-
   // Stores Options::timer_slack_ until the sequence manager has been bound to
   // a thread.
   TimerSlack timer_slack_ = TIMER_SLACK_NONE;
diff --git a/base/trace_event/auto_open_close_event.cc b/base/trace_event/auto_open_close_event.cc
deleted file mode 100644
index 1879700..0000000
--- a/base/trace_event/auto_open_close_event.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/trace_event/auto_open_close_event.h"
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-
-namespace base {
-namespace trace_event {
-
-AutoOpenCloseEvent::AutoOpenCloseEvent(AutoOpenCloseEvent::Type type,
-  const char* category, const char* event_name):
-  category_(category),
-  event_name_(event_name),
-  weak_factory_(this) {
-  base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
-      weak_factory_.GetWeakPtr());
-}
-
-AutoOpenCloseEvent::~AutoOpenCloseEvent() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  base::trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(
-      this);
-}
-
-void AutoOpenCloseEvent::Begin() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  start_time_ = TRACE_TIME_TICKS_NOW();
-  TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
-      category_, event_name_, static_cast<void*>(this), start_time_);
-}
-
-void AutoOpenCloseEvent::End() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT_ASYNC_END0(category_, event_name_, static_cast<void*>(this));
-  start_time_ = base::TimeTicks();
-}
-
-void AutoOpenCloseEvent::OnTraceLogEnabled() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (start_time_.ToInternalValue() != 0)
-    TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
-        category_, event_name_, static_cast<void*>(this), start_time_);
-}
-
-void AutoOpenCloseEvent::OnTraceLogDisabled() {}
-
-}  // namespace trace_event
-}  // namespace base
diff --git a/base/trace_event/auto_open_close_event.h b/base/trace_event/auto_open_close_event.h
index 795a4948..2c985ca 100644
--- a/base/trace_event/auto_open_close_event.h
+++ b/base/trace_event/auto_open_close_event.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_AUTO_OPEN_CLOSE_EVENT_H_
-#define BASE_AUTO_OPEN_CLOSE_EVENT_H_
+#ifndef BASE_TRACE_EVENT_AUTO_OPEN_CLOSE_EVENT_H_
+#define BASE_TRACE_EVENT_AUTO_OPEN_CLOSE_EVENT_H_
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -21,8 +21,10 @@
 // "auto-closing" = if the trace event is started but not ended by the time
 // tracing ends, then the trace event will be automatically closed at the
 // end of tracing.
-class BASE_EXPORT AutoOpenCloseEvent
-    : public TraceLog::AsyncEnabledStateObserver {
+// |category| must be known at compile-time in order to be used in trace macros.
+// Hence, it's passed as a class templace argument.
+template <const char* category>
+class AutoOpenCloseEvent : public TraceLog::AsyncEnabledStateObserver {
  public:
   enum Type {
     ASYNC
@@ -31,18 +33,40 @@
   // As in the rest of the tracing macros, the const char* arguments here
   // must be pointers to indefinitely lived strings (e.g. hard-coded string
   // literals are okay, but not strings created by c_str())
-  AutoOpenCloseEvent(Type type, const char* category, const char* event_name);
-  ~AutoOpenCloseEvent() override;
+  AutoOpenCloseEvent(Type type, const char* event_name)
+      : event_name_(event_name), weak_factory_(this) {
+    base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
+        weak_factory_.GetWeakPtr());
+  }
+  ~AutoOpenCloseEvent() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    base::trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(
+        this);
+  }
 
-  void Begin();
-  void End();
+  void Begin() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    start_time_ = TRACE_TIME_TICKS_NOW();
+    TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
+        category, event_name_, static_cast<void*>(this), start_time_);
+  }
+  void End() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    TRACE_EVENT_ASYNC_END0(category, event_name_, static_cast<void*>(this));
+    start_time_ = base::TimeTicks();
+  }
 
   // AsyncEnabledStateObserver implementation
-  void OnTraceLogEnabled() override;
-  void OnTraceLogDisabled() override;
+  void OnTraceLogEnabled() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    if (!start_time_.is_null()) {
+      TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
+          category, event_name_, static_cast<void*>(this), start_time_);
+    }
+  }
+  void OnTraceLogDisabled() override {}
 
  private:
-  const char* const category_;
   const char* const event_name_;
   base::TimeTicks start_time_;
   base::ThreadChecker thread_checker_;
@@ -54,4 +78,4 @@
 }  // namespace trace_event
 }  // namespace base
 
-#endif  // BASE_AUTO_OPEN_CLOSE_EVENT_H_
\ No newline at end of file
+#endif  // BASE_TRACE_EVENT_AUTO_OPEN_CLOSE_EVENT_H_
diff --git a/base/trace_event/builtin_categories.cc b/base/trace_event/builtin_categories.cc
new file mode 100644
index 0000000..bc1c3e3b
--- /dev/null
+++ b/base/trace_event/builtin_categories.cc
@@ -0,0 +1,14 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/builtin_categories.h"
+
+namespace base {
+namespace trace_event {
+
+constexpr const char* BuiltinCategories::kBuiltinCategories[];
+constexpr const char* BuiltinCategories::kCategoriesForTesting[];
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
new file mode 100644
index 0000000..4cb1748
--- /dev/null
+++ b/base/trace_event/builtin_categories.h
@@ -0,0 +1,372 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
+#define BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
+
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/trace_event/common/trace_event_common.h"
+#include "build/build_config.h"
+
+// List of builtin category names. If you want to use a new category name in
+// your code and you get a static assert, this is the right place to register
+// the name. If the name is going to be used only for testing, please add it to
+// |kIgnoredCategoriesForTesting| instead.
+// Parameter |X| must be a *macro* that takes a single |name| string argument,
+// denoting a category name.
+#define INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(X)                        \
+  /* These entries must go first to be consistent with the               \
+   * CategoryRegistry::kCategory* consts.*/                              \
+  X("tracing categories exhausted; must increase kMaxCategories")        \
+  X("tracing already shutdown")                                          \
+  X("__metadata")                                                        \
+  /* The rest of the list is in alphabetical order */                    \
+  X("accessibility")                                                     \
+  X("AccountFetcherService")                                             \
+  X("android_webview")                                                   \
+  X("audio")                                                             \
+  X("base")                                                              \
+  X("benchmark")                                                         \
+  X("blink")                                                             \
+  X("blink.animations")                                                  \
+  X("blink.console")                                                     \
+  X("blink_gc")                                                          \
+  X("blink.net")                                                         \
+  X("blink_style")                                                       \
+  X("blink.user_timing")                                                 \
+  X("Blob")                                                              \
+  X("browser")                                                           \
+  X("browsing_data")                                                     \
+  X("CacheStorage")                                                      \
+  X("camera")                                                            \
+  X("cast_perf_test")                                                    \
+  X("cast.stream")                                                       \
+  X("cc")                                                                \
+  X("cc.debug")                                                          \
+  X("cdp.perf")                                                          \
+  X("chromeos")                                                          \
+  X("cma")                                                               \
+  X("compositor")                                                        \
+  X("content")                                                           \
+  X("cpu_profiler")                                                      \
+  X("devtools")                                                          \
+  X("devtools.timeline")                                                 \
+  X("devtools.timeline.async")                                           \
+  X("download")                                                          \
+  X("download_service")                                                  \
+  X("drm")                                                               \
+  X("drmcursor")                                                         \
+  X("dwrite")                                                            \
+  X("DXVA Decoding")                                                     \
+  X("EarlyJava")                                                         \
+  X("evdev")                                                             \
+  X("event")                                                             \
+  X("exo")                                                               \
+  X("explore_sites")                                                     \
+  X("FileSystem")                                                        \
+  X("file_system_provider")                                              \
+  X("font_loader")                                                       \
+  X("font_service")                                                      \
+  X("GAMEPAD")                                                           \
+  X("gpu")                                                               \
+  X("gpu.capture")                                                       \
+  X("headless")                                                          \
+  X("hwoverlays")                                                        \
+  X("identity")                                                          \
+  X("IndexedDB")                                                         \
+  X("input")                                                             \
+  X("io")                                                                \
+  X("ipc")                                                               \
+  X("Java")                                                              \
+  X("jni")                                                               \
+  X("jpeg")                                                              \
+  X("latency")                                                           \
+  X("latencyInfo")                                                       \
+  X("leveldb")                                                           \
+  X("loader")                                                            \
+  X("loading")                                                           \
+  X("login")                                                             \
+  X("media")                                                             \
+  X("media_router")                                                      \
+  X("memory")                                                            \
+  X("midi")                                                              \
+  X("mojom")                                                             \
+  X("mus")                                                               \
+  X("native")                                                            \
+  X("navigation")                                                        \
+  X("net")                                                               \
+  X("netlog")                                                            \
+  X("offline_pages")                                                     \
+  X("omnibox")                                                           \
+  X("oobe")                                                              \
+  X("ozone")                                                             \
+  X("p2p")                                                               \
+  X("page-serialization")                                                \
+  X("pepper")                                                            \
+  X("ppapi")                                                             \
+  X("ppapi proxy")                                                       \
+  X("rail")                                                              \
+  X("renderer")                                                          \
+  X("renderer_host")                                                     \
+  X("renderer.scheduler")                                                \
+  X("RLZ")                                                               \
+  X("safe_browsing")                                                     \
+  X("screenlock_monitor")                                                \
+  X("sequence_manager")                                                  \
+  X("service_manager")                                                   \
+  X("ServiceWorker")                                                     \
+  X("shell")                                                             \
+  X("shortcut_viewer")                                                   \
+  X("shutdown")                                                          \
+  X("SiteEngagement")                                                    \
+  X("skia")                                                              \
+  X("startup")                                                           \
+  X("sync")                                                              \
+  X("sync_lock_contention")                                              \
+  X("task_scheduler")                                                    \
+  X("test_gpu")                                                          \
+  X("test_tracing")                                                      \
+  X("toplevel")                                                          \
+  X("ui")                                                                \
+  X("v8")                                                                \
+  X("v8.execute")                                                        \
+  X("ValueStoreFrontend::Backend")                                       \
+  X("views")                                                             \
+  X("views.frame")                                                       \
+  X("viz")                                                               \
+  X("vk")                                                                \
+  X("wayland")                                                           \
+  X("Wayland")                                                           \
+  X("webaudio")                                                          \
+  X("WebCore")                                                           \
+  X("webrtc")                                                            \
+  X(TRACE_DISABLED_BY_DEFAULT("animation-worklet"))                      \
+  X(TRACE_DISABLED_BY_DEFAULT("audio-worklet"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.debug"))                            \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"))                     \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"))               \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.feature_usage"))                    \
+  X(TRACE_DISABLED_BY_DEFAULT("blink_gc"))                               \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"))                   \
+  X(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"))                     \
+  X(TRACE_DISABLED_BY_DEFAULT("cc"))                                     \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug"))                               \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"))                      \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"))                 \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.picture"))                       \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"))                     \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"))              \
+  X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"))                 \
+  X(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"))                    \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"))                      \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.frame"))                \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking")) \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers"))               \
+  X(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"))              \
+  X(TRACE_DISABLED_BY_DEFAULT("file"))                                   \
+  X(TRACE_DISABLED_BY_DEFAULT("gpu_cmd_queue"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("gpu.debug"))                              \
+  X(TRACE_DISABLED_BY_DEFAULT("gpu_decoder"))                            \
+  X(TRACE_DISABLED_BY_DEFAULT("gpu.device"))                             \
+  X(TRACE_DISABLED_BY_DEFAULT("gpu.service"))                            \
+  X(TRACE_DISABLED_BY_DEFAULT("ipc.flow"))                               \
+  X(TRACE_DISABLED_BY_DEFAULT("layer-element"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("lighthouse"))                             \
+  X(TRACE_DISABLED_BY_DEFAULT("loading"))                                \
+  X(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"))                     \
+  X(TRACE_DISABLED_BY_DEFAULT("memory-infra"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"))             \
+  X(TRACE_DISABLED_BY_DEFAULT("net"))                                    \
+  X(TRACE_DISABLED_BY_DEFAULT("network"))                                \
+  X(TRACE_DISABLED_BY_DEFAULT("paint-worklet"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("power"))                                  \
+  X(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"))                     \
+  X(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"))               \
+  X(TRACE_DISABLED_BY_DEFAULT("sequence_manager"))                       \
+  X(TRACE_DISABLED_BY_DEFAULT("sequence_manager.debug"))                 \
+  X(TRACE_DISABLED_BY_DEFAULT("sequence_manager.verbose_snapshots"))     \
+  X(TRACE_DISABLED_BY_DEFAULT("skia"))                                   \
+  X(TRACE_DISABLED_BY_DEFAULT("skia.gpu"))                               \
+  X(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"))                         \
+  X(TRACE_DISABLED_BY_DEFAULT("SyncFileSystem"))                         \
+  X(TRACE_DISABLED_BY_DEFAULT("system_stats"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("task_scheduler_diagnostics"))             \
+  X(TRACE_DISABLED_BY_DEFAULT("task_scheduler.flow"))                    \
+  X(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.compile"))                             \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"))                        \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler.hires"))                  \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.gc"))                                  \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.gc_stats"))                            \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.ic_stats"))                            \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.runtime"))                             \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats"))                       \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats_sampling"))              \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"))               \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"))                   \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.quads"))                              \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"))                    \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"))                   \
+  X(TRACE_DISABLED_BY_DEFAULT("viz.triangles"))                          \
+  X(TRACE_DISABLED_BY_DEFAULT("worker.scheduler"))
+
+#define INTERNAL_TRACE_INIT_CATEGORY_NAME(name) name,
+
+#define INTERNAL_TRACE_INIT_CATEGORY(name) {0, 0, name},
+
+namespace base {
+namespace trace_event {
+
+// Constexpr version of string comparison operator. |a| and |b| must be valid
+// C-style strings known at compile-time.
+constexpr bool StrEqConstexpr(const char* a, const char* b) {
+  for (; *a != '\0' && *b != '\0'; ++a, ++b) {
+    if (*a != *b)
+      return false;
+  }
+  return *a == *b;
+}
+
+// Tests for |StrEqConstexpr()|.
+static_assert(StrEqConstexpr("foo", "foo"), "strings should be equal");
+static_assert(!StrEqConstexpr("foo", "Foo"), "strings should not be equal");
+static_assert(!StrEqConstexpr("foo", "foo1"), "strings should not be equal");
+static_assert(!StrEqConstexpr("foo2", "foo"), "strings should not be equal");
+static_assert(StrEqConstexpr("", ""), "strings should be equal");
+static_assert(!StrEqConstexpr("foo", ""), "strings should not be equal");
+static_assert(!StrEqConstexpr("", "foo"), "strings should not be equal");
+static_assert(!StrEqConstexpr("ab", "abc"), "strings should not be equal");
+static_assert(!StrEqConstexpr("abc", "ab"), "strings should not be equal");
+
+// Static-only class providing access to the compile-time registry of trace
+// categories.
+class BuiltinCategories {
+ public:
+  // Returns a built-in category name at |index| in the registry.
+  static constexpr const char* At(int index) {
+    return kBuiltinCategories[index];
+  }
+
+  // Returns the amount of built-in categories in the registry.
+  static constexpr size_t Size() { return base::size(kBuiltinCategories); }
+
+  // Returns whether the category is either:
+  // - Properly registered in the builtin list.
+  // - Constists of several categories separated by commas.
+  // - Used only in tests.
+  // All trace categories are checked against this. A static_assert is triggered
+  // if at least one category fails this check.
+  static constexpr bool IsAllowedCategory(const char* category) {
+#if defined(OS_WIN) && defined(COMPONENT_BUILD)
+    return true;
+#else
+    return IsBuiltinCategory(category) ||
+           IsCommaSeparatedCategoryGroup(category) ||
+           IsCategoryForTesting(category);
+#endif
+  }
+
+ private:
+  // The array of built-in category names used for compile-time lookup.
+  static constexpr const char* kBuiltinCategories[] = {
+      INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(
+          INTERNAL_TRACE_INIT_CATEGORY_NAME)};
+
+  // The array of category names used only for testing. It's kept separately
+  // from the main list to avoid allocating the space for them in the binary.
+  static constexpr const char* kCategoriesForTesting[] = {
+      "\001\002\003\n\r",
+      "a",
+      "all",
+      "b",
+      "b1",
+      "c",
+      "c0",
+      "c1",
+      "c2",
+      "c3",
+      "c4",
+      "cat",
+      "cat1",
+      "cat2",
+      "cat3",
+      "cat4",
+      "cat5",
+      "cat6",
+      "category",
+      "drink",
+      "excluded_cat",
+      "filtered_cat",
+      "foo",
+      "inc",
+      "inc2",
+      "included",
+      "inc_wildcard_",
+      "inc_wildcard_abc",
+      "inc_wildchar_bla_end",
+      "inc_wildchar_x_end",
+      "kTestCategory",
+      "log",
+      "noise",
+      "other_included",
+      "test",
+      "test_category",
+      "Testing",
+      "TraceEventAgentTestCategory",
+      "unfiltered_cat",
+      "whitewashed",
+      "x",
+      TRACE_DISABLED_BY_DEFAULT("c9"),
+      TRACE_DISABLED_BY_DEFAULT("cat"),
+      TRACE_DISABLED_BY_DEFAULT("filtered_cat"),
+      TRACE_DISABLED_BY_DEFAULT("NotTesting"),
+      TRACE_DISABLED_BY_DEFAULT("Testing"),
+      TRACE_DISABLED_BY_DEFAULT("unfiltered_cat")};
+
+  // Returns whether |str| is in |array| of |array_len|.
+  static constexpr bool IsStringInArray(const char* str,
+                                        const char* const array[],
+                                        size_t array_len) {
+    for (size_t i = 0; i < array_len; ++i) {
+      if (StrEqConstexpr(str, array[i]))
+        return true;
+    }
+    return false;
+  }
+
+  // Returns whether |category_group| contains a ',' symbol, denoting that an
+  // event belongs to several categories. We don't add such strings in the
+  // builtin list but allow them to pass the static assert.
+  static constexpr bool IsCommaSeparatedCategoryGroup(
+      const char* category_group) {
+    for (; *category_group != '\0'; ++category_group) {
+      if (*category_group == ',')
+        return true;
+    }
+    return false;
+  }
+
+  // Returns whether |category| is used only for testing.
+  static constexpr bool IsCategoryForTesting(const char* category) {
+    return IsStringInArray(category, kCategoriesForTesting,
+                           base::size(kCategoriesForTesting));
+  }
+
+  // Returns whether |category| is registered in the builtin list.
+  static constexpr bool IsBuiltinCategory(const char* category) {
+    return IsStringInArray(category, kBuiltinCategories,
+                           base::size(kBuiltinCategories));
+  }
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(BuiltinCategories);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc
index e7c1460..2e4ef47 100644
--- a/base/trace_event/category_registry.cc
+++ b/base/trace_event/category_registry.cc
@@ -8,48 +8,33 @@
 
 #include <type_traits>
 
-#include "base/atomicops.h"
 #include "base/debug/leak_annotations.h"
 #include "base/logging.h"
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/trace_event/trace_category.h"
 
 namespace base {
 namespace trace_event {
 
 namespace {
 
-constexpr size_t kMaxCategories = 200;
-const int kNumBuiltinCategories = 4;
-
-// |g_categories| might end up causing creating dynamic initializers if not POD.
+// |categories_| might end up causing creating dynamic initializers if not POD.
 static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
 
-// These entries must be kept consistent with the kCategory* consts below.
-TraceCategory g_categories[kMaxCategories] = {
-    {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
-    {0, 0, "tracing already shutdown"},  // See kCategoryAlreadyShutdown below.
-    {0, 0, "__metadata"},                // See kCategoryMetadata below.
-    {0, 0, "toplevel"},                  // Warmup the toplevel category.
-};
-
-base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
-
-bool IsValidCategoryPtr(const TraceCategory* category) {
-  // If any of these are hit, something has cached a corrupt category pointer.
-  uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
-  return ptr % sizeof(void*) == 0 &&
-         ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
-         ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
-}
-
 }  // namespace
 
 // static
-TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0];
+TraceCategory CategoryRegistry::categories_[kMaxCategories] = {
+    INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(INTERNAL_TRACE_INIT_CATEGORY)};
+
+// static
+base::subtle::AtomicWord CategoryRegistry::category_index_ =
+    BuiltinCategories::Size();
+
+// static
+TraceCategory* const CategoryRegistry::kCategoryExhausted = &categories_[0];
 TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown =
-    &g_categories[1];
-TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2];
+    &categories_[1];
+TraceCategory* const CategoryRegistry::kCategoryMetadata = &categories_[2];
 
 // static
 void CategoryRegistry::Initialize() {
@@ -58,11 +43,11 @@
   // traced or not, so we allow races on the enabled flag to keep the trace
   // macros fast.
   for (size_t i = 0; i < kMaxCategories; ++i) {
-    ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
+    ANNOTATE_BENIGN_RACE(categories_[i].state_ptr(),
                          "trace_event category enabled");
     // If this DCHECK is hit in a test it means that ResetForTesting() is not
     // called and the categories state leaks between test fixtures.
-    DCHECK(!g_categories[i].is_enabled());
+    DCHECK(!categories_[i].is_enabled());
   }
 }
 
@@ -72,7 +57,7 @@
   // categories themselves cannot be cleared up because the static pointers
   // injected by the macros still point to them and cannot be reset.
   for (size_t i = 0; i < kMaxCategories; ++i)
-    g_categories[i].reset_for_testing();
+    categories_[i].reset_for_testing();
 }
 
 // static
@@ -80,13 +65,13 @@
   DCHECK(!strchr(category_name, '"'))
       << "Category names may not contain double quote";
 
-  // The g_categories is append only, avoid using a lock for the fast path.
-  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+  // The categories_ is append only, avoid using a lock for the fast path.
+  size_t category_index = base::subtle::Acquire_Load(&category_index_);
 
   // Search for pre-existing category group.
   for (size_t i = 0; i < category_index; ++i) {
-    if (strcmp(g_categories[i].name(), category_name) == 0) {
-      return &g_categories[i];
+    if (strcmp(categories_[i].name(), category_name) == 0) {
+      return &categories_[i];
     }
   }
   return nullptr;
@@ -104,7 +89,7 @@
     return false;
 
   // Create a new category.
-  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+  size_t category_index = base::subtle::Acquire_Load(&category_index_);
   if (category_index >= kMaxCategories) {
     NOTREACHED() << "must increase kMaxCategories";
     *category = kCategoryExhausted;
@@ -117,14 +102,14 @@
   const char* category_name_copy = strdup(category_name);
   ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
 
-  *category = &g_categories[category_index];
+  *category = &categories_[category_index];
   DCHECK(!(*category)->is_valid());
   DCHECK(!(*category)->is_enabled());
   (*category)->set_name(category_name_copy);
   category_initializer_fn(*category);
 
   // Update the max index now.
-  base::subtle::Release_Store(&g_category_index, category_index + 1);
+  base::subtle::Release_Store(&category_index_, category_index + 1);
   return true;
 }
 
@@ -139,17 +124,25 @@
 // static
 bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
   DCHECK(IsValidCategoryPtr(category));
-  return category < &g_categories[kNumBuiltinCategories];
+  return category < &categories_[BuiltinCategories::Size()];
 }
 
 // static
 CategoryRegistry::Range CategoryRegistry::GetAllCategories() {
-  // The |g_categories| array is append only. We have to only guarantee to
+  // The |categories_| array is append only. We have to only guarantee to
   // not return an index to a category which is being initialized by
   // GetOrCreateCategoryByName().
-  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
-  return CategoryRegistry::Range(&g_categories[0],
-                                 &g_categories[category_index]);
+  size_t category_index = base::subtle::Acquire_Load(&category_index_);
+  return CategoryRegistry::Range(&categories_[0], &categories_[category_index]);
+}
+
+// static
+bool CategoryRegistry::IsValidCategoryPtr(const TraceCategory* category) {
+  // If any of these are hit, something has cached a corrupt category pointer.
+  uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
+  return ptr % sizeof(void*) == 0 &&
+         ptr >= reinterpret_cast<uintptr_t>(&categories_[0]) &&
+         ptr <= reinterpret_cast<uintptr_t>(&categories_[kMaxCategories - 1]);
 }
 
 }  // namespace trace_event
diff --git a/base/trace_event/category_registry.h b/base/trace_event/category_registry.h
index 9c08efa..ddf346c98 100644
--- a/base/trace_event/category_registry.h
+++ b/base/trace_event/category_registry.h
@@ -8,13 +8,18 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/trace_event/builtin_categories.h"
+#include "base/trace_event/common/trace_event_common.h"
+#include "base/trace_event/trace_category.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace trace_event {
 
-struct TraceCategory;
 class TraceCategoryTest;
 class TraceLog;
 
@@ -60,6 +65,24 @@
   // pointer and use it for checks in their fast-paths.
   static TraceCategory* GetCategoryByName(const char* category_name);
 
+  // Returns a built-in category from its name or nullptr if not found at
+  // compile-time. The return value is an undefinitely lived pointer to the
+  // TraceCategory owned by the registry.
+  static constexpr TraceCategory* GetBuiltinCategoryByName(
+      const char* category_group) {
+#if defined(OS_WIN) && defined(COMPONENT_BUILD)
+    // The address cannot be evaluated at compile-time in Windows compoment
+    // builds.
+    return nullptr;
+#else
+    for (size_t i = 0; i < BuiltinCategories::Size(); ++i) {
+      if (StrEqConstexpr(category_group, BuiltinCategories::At(i)))
+        return &categories_[i];
+    }
+    return nullptr;
+#endif
+  }
+
   static bool IsBuiltinCategory(const TraceCategory*);
 
  private:
@@ -67,6 +90,13 @@
   friend class TraceLog;
   using CategoryInitializerFn = void (*)(TraceCategory*);
 
+  // The max number of trace categories that can be recorded.
+  static constexpr size_t kMaxCategories = 250;
+
+  // Checks that there is enough space for all builtin categories.
+  static_assert(BuiltinCategories::Size() <= kMaxCategories,
+                "kMaxCategories must be greater than kNumBuiltinCategories");
+
   // Only for debugging/testing purposes, is a no-op on release builds.
   static void Initialize();
 
@@ -85,6 +115,15 @@
   // Allows to iterate over the valid categories in a for-each loop.
   // This includes builtin categories such as __metadata.
   static Range GetAllCategories();
+
+  // Returns whether |category| correctly points at |categories_| array entry.
+  static bool IsValidCategoryPtr(const TraceCategory* category);
+
+  // The static array of trace categories.
+  static TraceCategory categories_[kMaxCategories];
+
+  // Contains the number of created categories.
+  static base::subtle::AtomicWord category_index_;
 };
 
 }  // namespace trace_event
diff --git a/base/trace_event/cpufreq_monitor_android.cc b/base/trace_event/cpufreq_monitor_android.cc
index b5c2fd10..c0c3147 100644
--- a/base/trace_event/cpufreq_monitor_android.cc
+++ b/base/trace_event/cpufreq_monitor_android.cc
@@ -26,7 +26,7 @@
 namespace {
 
 const size_t kNumBytesToReadForSampling = 32;
-const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("power");
+constexpr const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("power");
 const char kEventTitle[] = "CPU Frequency";
 
 }  // namespace
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index d3b7287a..4898b14d 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -65,8 +65,7 @@
 }  // namespace
 
 // static
-const char* const MemoryDumpManager::kTraceCategory =
-    TRACE_DISABLED_BY_DEFAULT("memory-infra");
+constexpr const char* MemoryDumpManager::kTraceCategory;
 
 // static
 const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 6033cfb..0c753d6 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -42,7 +42,8 @@
   using RequestGlobalDumpFunction =
       RepeatingCallback<void(MemoryDumpType, MemoryDumpLevelOfDetail)>;
 
-  static const char* const kTraceCategory;
+  static constexpr const char* const kTraceCategory =
+      TRACE_DISABLED_BY_DEFAULT("memory-infra");
 
   // This value is returned as the tracing id of the child processes by
   // GetTracingProcessId() when tracing is not enabled.
diff --git a/base/trace_event/trace_category.h b/base/trace_event/trace_category.h
index 792bc5e..9d325bda 100644
--- a/base/trace_event/trace_category.h
+++ b/base/trace_event/trace_category.h
@@ -47,7 +47,7 @@
   // today TRACE_EVENT* macros cache the state ptr. They should just cache the
   // full TraceCategory ptr, which is immutable, and use these helper function
   // here. This will get rid of the need of this awkward ptr getter completely.
-  const uint8_t* state_ptr() const {
+  constexpr const uint8_t* state_ptr() const {
     return const_cast<const uint8_t*>(&state_);
   }
 
diff --git a/base/trace_event/trace_category_unittest.cc b/base/trace_event/trace_category_unittest.cc
index 1370f5e..ed6fda9 100644
--- a/base/trace_event/trace_category_unittest.cc
+++ b/base/trace_event/trace_category_unittest.cc
@@ -57,6 +57,11 @@
     GetOrCreateCategoryByName("__test_race", &cat);
     EXPECT_NE(nullptr, cat);
   }
+
+  static constexpr TraceCategory* GetBuiltinCategoryByName(
+      const char* category_group) {
+    return CategoryRegistry::GetBuiltinCategoryByName(category_group);
+  }
 };
 
 TEST_F(TraceCategoryTest, Basic) {
@@ -151,5 +156,18 @@
   ASSERT_EQ(1, num_times_seen);
 }
 
+// Tests getting trace categories by name at compile-time.
+TEST_F(TraceCategoryTest, GetCategoryAtCompileTime) {
+  static_assert(GetBuiltinCategoryByName("nonexistent") == nullptr,
+                "nonexistent found");
+#if defined(OS_WIN) && defined(COMPONENT_BUILD)
+  static_assert(GetBuiltinCategoryByName("toplevel") == nullptr,
+                "toplevel found");
+#else
+  static_assert(GetBuiltinCategoryByName("toplevel") != nullptr,
+                "toplevel not found");
+#endif
+}
+
 }  // namespace trace_event
 }  // namespace base
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index e11f968..d52b08d 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/time/time_override.h"
+#include "base/trace_event/builtin_categories.h"
 #include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/trace_category.h"
@@ -234,25 +235,41 @@
 // No barriers are needed, because this code is designed to operate safely
 // even when the unsigned char* points to garbage data (which may be the case
 // on processors without cache coherency).
-#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \
-    category_group, atomic, category_group_enabled) \
-    category_group_enabled = \
-        reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD( \
-            atomic)); \
-    if (UNLIKELY(!category_group_enabled)) { \
-      category_group_enabled = \
-          TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \
-      TRACE_EVENT_API_ATOMIC_STORE(atomic, \
-          reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>( \
-              category_group_enabled)); \
-    }
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(    \
+    category_group, atomic, category_group_enabled)                 \
+  category_group_enabled = reinterpret_cast<const unsigned char*>(  \
+      TRACE_EVENT_API_ATOMIC_LOAD(atomic));                         \
+  if (UNLIKELY(!category_group_enabled)) {                          \
+    category_group_enabled =                                        \
+        TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \
+    TRACE_EVENT_API_ATOMIC_STORE(                                   \
+        atomic, reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>(      \
+                    category_group_enabled));                       \
+  }
 
-#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_MAYBE_AT_COMPILE_TIME(        \
+    category_group, k_category_group_enabled, category_group_enabled)        \
+  if (k_category_group_enabled) {                                            \
+    category_group_enabled = k_category_group_enabled;                       \
+  } else {                                                                   \
     static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \
-    const unsigned char* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \
-    INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(category_group, \
-        INTERNAL_TRACE_EVENT_UID(atomic), \
-        INTERNAL_TRACE_EVENT_UID(category_group_enabled));
+    INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(                 \
+        category_group, INTERNAL_TRACE_EVENT_UID(atomic),                    \
+        category_group_enabled);                                             \
+  }
+
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group)                 \
+  static_assert(                                                               \
+      base::trace_event::BuiltinCategories::IsAllowedCategory(category_group), \
+      "Unknown tracing category is used. Please register your "                \
+      "category in base/trace_event/builtin_categories.h");                    \
+  constexpr const unsigned char* INTERNAL_TRACE_EVENT_UID(                     \
+      k_category_group_enabled) =                                              \
+      base::trace_event::TraceLog::GetBuiltinCategoryEnabled(category_group);  \
+  const unsigned char* INTERNAL_TRACE_EVENT_UID(category_group_enabled);       \
+  INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_MAYBE_AT_COMPILE_TIME(                \
+      category_group, INTERNAL_TRACE_EVENT_UID(k_category_group_enabled),      \
+      INTERNAL_TRACE_EVENT_UID(category_group_enabled));
 
 // Implementation detail: internal macro to return unoverridden
 // base::TimeTicks::Now(). This is important because in headless VirtualTime can
@@ -1153,26 +1170,23 @@
 namespace base {
 namespace trace_event {
 
-template<typename IDType> class TraceScopedTrackableObject {
+template <typename IDType, const char* category>
+class TraceScopedTrackableObject {
  public:
-  TraceScopedTrackableObject(const char* category_group, const char* name,
-      IDType id)
-    : category_group_(category_group),
-      name_(name),
-      id_(id) {
-    TRACE_EVENT_OBJECT_CREATED_WITH_ID(category_group_, name_, id_);
+  TraceScopedTrackableObject(const char* name, IDType id)
+      : name_(name), id_(id) {
+    TRACE_EVENT_OBJECT_CREATED_WITH_ID(category, name_, id_);
   }
 
   template <typename ArgType> void snapshot(ArgType snapshot) {
-    TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(category_group_, name_, id_, snapshot);
+    TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(category, name_, id_, snapshot);
   }
 
   ~TraceScopedTrackableObject() {
-    TRACE_EVENT_OBJECT_DELETED_WITH_ID(category_group_, name_, id_);
+    TRACE_EVENT_OBJECT_DELETED_WITH_ID(category, name_, id_);
   }
 
  private:
-  const char* category_group_;
   const char* name_;
   IDType id_;
 
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 4f51f97..f9c4df52 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -64,7 +64,8 @@
 const int kFlowId = 7;
 const char kFlowIdStr[] = "0x7";
 
-const  char kRecordAllCategoryFilter[] = "*";
+constexpr const char kRecordAllCategoryFilter[] = "*";
+constexpr const char kAllCategory[] = "all";
 
 class TraceEventTestFixture : public testing::Test {
  public:
@@ -391,7 +392,7 @@
   return hits;
 }
 
-const char kControlCharacters[] = "\001\002\003\n\r";
+constexpr const char kControlCharacters[] = "\001\002\003\n\r";
 
 void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
   {
@@ -486,8 +487,8 @@
         "all", "tracked object 1", 0x42, "hello");
     TRACE_EVENT_OBJECT_DELETED_WITH_ID("all", "tracked object 1", 0x42);
 
-    TraceScopedTrackableObject<int> trackable("all", "tracked object 2",
-                                              0x2128506);
+    TraceScopedTrackableObject<int, kAllCategory> trackable("tracked object 2",
+                                                            0x2128506);
     trackable.snapshot("world");
 
     TRACE_EVENT_OBJECT_CREATED_WITH_ID(
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 1a3a320..9cb9d609 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -33,7 +33,6 @@
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "base/trace_event/category_registry.h"
 #include "base/trace_event/event_name_filter.h"
 #include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
index 9f89750..3db3e74 100644
--- a/base/trace_event/trace_log.h
+++ b/base/trace_event/trace_log.h
@@ -20,6 +20,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time_override.h"
+#include "base/trace_event/category_registry.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/trace_config.h"
 #include "base/trace_event/trace_event_impl.h"
@@ -200,6 +201,14 @@
   static const unsigned char* GetCategoryGroupEnabled(const char* name);
   static const char* GetCategoryGroupName(
       const unsigned char* category_group_enabled);
+  static constexpr const unsigned char* GetBuiltinCategoryEnabled(
+      const char* name) {
+    TraceCategory* builtin_category =
+        CategoryRegistry::GetBuiltinCategoryByName(name);
+    if (builtin_category)
+      return builtin_category->state_ptr();
+    return nullptr;
+  }
 
   // Called by TRACE_EVENT* macros, don't call this directly.
   // If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 498fcf8b..79e282d 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2433,6 +2433,13 @@
           proguard_configs += [ "//build/android/multidex.flags" ]
         }
         proguard_mapping_path = _proguard_mapping_path
+        if (_use_chromium_linker) {
+          proguard_configs +=
+              [ "//base/android/proguard/explicit_jni_registration.flags" ]
+        } else {
+          proguard_configs +=
+              [ "//base/android/proguard/implicit_jni_registration.flags" ]
+        }
       }
 
       # Don't depend on the runtime_deps target in order to avoid having to
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index cf4050d..f5c88d6 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -388,7 +388,6 @@
   libfontconfig1:i386
   libglib2.0-0:i386
   libgpm2:i386
-  libgtk-3-0:i386
   libncurses5:i386
   lib32ncurses5-dev
   libnss3:i386
diff --git a/cc/base/devtools_instrumentation.cc b/cc/base/devtools_instrumentation.cc
index d6f77f0..fab22da7 100644
--- a/cc/base/devtools_instrumentation.cc
+++ b/cc/base/devtools_instrumentation.cc
@@ -8,9 +8,8 @@
 namespace devtools_instrumentation {
 
 namespace internal {
-const char kCategory[] = TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
-const char kCategoryFrame[] =
-    TRACE_DISABLED_BY_DEFAULT("devtools.timeline.frame");
+constexpr const char CategoryName::CategoryName::kTimeline[];
+constexpr const char CategoryName::CategoryName::kTimelineFrame[];
 const char kData[] = "data";
 const char kFrameId[] = "frameId";
 const char kLayerId[] = "layerId";
@@ -36,13 +35,14 @@
     : decode_type_(decode_type),
       task_type_(task_type),
       start_time_(base::TimeTicks::Now()) {
-  TRACE_EVENT_BEGIN1(internal::kCategory, internal::kImageDecodeTask,
-                     internal::kPixelRefId,
+  TRACE_EVENT_BEGIN1(internal::CategoryName::kTimeline,
+                     internal::kImageDecodeTask, internal::kPixelRefId,
                      reinterpret_cast<uint64_t>(image_ptr));
 }
 
 ScopedImageDecodeTask::~ScopedImageDecodeTask() {
-  TRACE_EVENT_END0(internal::kCategory, internal::kImageDecodeTask);
+  TRACE_EVENT_END0(internal::CategoryName::kTimeline,
+                   internal::kImageDecodeTask);
   base::TimeDelta duration = base::TimeTicks::Now() - start_time_;
   switch (task_type_) {
     case kInRaster:
diff --git a/cc/base/devtools_instrumentation.h b/cc/base/devtools_instrumentation.h
index e0b9314f..0697422 100644
--- a/cc/base/devtools_instrumentation.h
+++ b/cc/base/devtools_instrumentation.h
@@ -19,8 +19,14 @@
 namespace devtools_instrumentation {
 
 namespace internal {
-CC_BASE_EXPORT extern const char kCategory[];
-CC_BASE_EXPORT extern const char kCategoryFrame[];
+struct CC_BASE_EXPORT CategoryName {
+  // Put these strings into a struct to allow external linkage.
+  static constexpr const char kTimeline[] =
+      TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
+  static constexpr const char kTimelineFrame[] =
+      TRACE_DISABLED_BY_DEFAULT("devtools.timeline.frame");
+};
+
 CC_BASE_EXPORT extern const char kData[];
 CC_BASE_EXPORT extern const char kFrameId[];
 CC_BASE_EXPORT extern const char kLayerId[];
@@ -44,10 +50,12 @@
  public:
   ScopedLayerTask(const char* event_name, int layer_id)
       : event_name_(event_name) {
-    TRACE_EVENT_BEGIN1(internal::kCategory, event_name_, internal::kLayerId,
-                       layer_id);
+    TRACE_EVENT_BEGIN1(internal::CategoryName::kTimeline, event_name_,
+                       internal::kLayerId, layer_id);
   }
-  ~ScopedLayerTask() { TRACE_EVENT_END0(internal::kCategory, event_name_); }
+  ~ScopedLayerTask() {
+    TRACE_EVENT_END0(internal::CategoryName::kTimeline, event_name_);
+  }
 
  private:
   const char* event_name_;
@@ -78,10 +86,13 @@
                       int layer_id,
                       int layer_tree_host_id)
       : event_name_(event_name) {
-    TRACE_EVENT_BEGIN2(internal::kCategory, event_name_, internal::kLayerId,
-                       layer_id, internal::kLayerTreeId, layer_tree_host_id);
+    TRACE_EVENT_BEGIN2(internal::CategoryName::kTimeline, event_name_,
+                       internal::kLayerId, layer_id, internal::kLayerTreeId,
+                       layer_tree_host_id);
   }
-  ~ScopedLayerTreeTask() { TRACE_EVENT_END0(internal::kCategory, event_name_); }
+  ~ScopedLayerTreeTask() {
+    TRACE_EVENT_END0(internal::CategoryName::kTimeline, event_name_);
+  }
 
  private:
   const char* event_name_;
@@ -92,11 +103,13 @@
 struct CC_BASE_EXPORT ScopedCommitTrace {
  public:
   explicit ScopedCommitTrace(int layer_tree_host_id) {
-    TRACE_EVENT_BEGIN1(internal::kCategory, internal::kCompositeLayers,
-                       internal::kLayerTreeId, layer_tree_host_id);
+    TRACE_EVENT_BEGIN1(internal::CategoryName::kTimeline,
+                       internal::kCompositeLayers, internal::kLayerTreeId,
+                       layer_tree_host_id);
   }
   ~ScopedCommitTrace() {
-    TRACE_EVENT_END0(internal::kCategory, internal::kCompositeLayers);
+    TRACE_EVENT_END0(internal::CategoryName::kTimeline,
+                     internal::kCompositeLayers);
   }
 
  private:
@@ -104,11 +117,13 @@
 };
 
 struct CC_BASE_EXPORT ScopedLayerObjectTracker
-    : public base::trace_event::TraceScopedTrackableObject<int> {
+    : public base::trace_event::
+          TraceScopedTrackableObject<int, internal::CategoryName::kTimeline> {
   explicit ScopedLayerObjectTracker(int layer_id)
-      : base::trace_event::TraceScopedTrackableObject<int>(internal::kCategory,
-                                                           internal::kLayerId,
-                                                           layer_id) {}
+      : base::trace_event::
+            TraceScopedTrackableObject<int, internal::CategoryName::kTimeline>(
+                internal::kLayerId,
+                layer_id) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScopedLayerObjectTracker);
@@ -116,26 +131,27 @@
 
 inline void CC_BASE_EXPORT DidActivateLayerTree(int layer_tree_host_id,
                                                 int frame_id) {
-  TRACE_EVENT_INSTANT2(internal::kCategoryFrame, internal::kActivateLayerTree,
-                       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId,
-                       layer_tree_host_id, internal::kFrameId, frame_id);
+  TRACE_EVENT_INSTANT2(internal::CategoryName::kTimelineFrame,
+                       internal::kActivateLayerTree, TRACE_EVENT_SCOPE_THREAD,
+                       internal::kLayerTreeId, layer_tree_host_id,
+                       internal::kFrameId, frame_id);
 }
 
 inline void CC_BASE_EXPORT DidBeginFrame(int layer_tree_host_id) {
-  TRACE_EVENT_INSTANT1(internal::kCategoryFrame, internal::kBeginFrame,
-                       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId,
-                       layer_tree_host_id);
+  TRACE_EVENT_INSTANT1(internal::CategoryName::kTimelineFrame,
+                       internal::kBeginFrame, TRACE_EVENT_SCOPE_THREAD,
+                       internal::kLayerTreeId, layer_tree_host_id);
 }
 
 inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id) {
-  TRACE_EVENT_INSTANT1(internal::kCategoryFrame, internal::kDrawFrame,
-                       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId,
-                       layer_tree_host_id);
+  TRACE_EVENT_INSTANT1(internal::CategoryName::kTimelineFrame,
+                       internal::kDrawFrame, TRACE_EVENT_SCOPE_THREAD,
+                       internal::kLayerTreeId, layer_tree_host_id);
 }
 
 inline void CC_BASE_EXPORT DidRequestMainThreadFrame(int layer_tree_host_id) {
   TRACE_EVENT_INSTANT1(
-      internal::kCategoryFrame, internal::kRequestMainThreadFrame,
+      internal::CategoryName::kTimelineFrame, internal::kRequestMainThreadFrame,
       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId, layer_tree_host_id);
 }
 
@@ -150,7 +166,7 @@
 inline void CC_BASE_EXPORT WillBeginMainThreadFrame(int layer_tree_host_id,
                                                     int frame_id) {
   TRACE_EVENT_INSTANT2(
-      internal::kCategoryFrame, internal::kBeginMainThreadFrame,
+      internal::CategoryName::kTimelineFrame, internal::kBeginMainThreadFrame,
       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId, layer_tree_host_id,
       internal::kData, BeginMainThreadFrameData(frame_id));
 }
@@ -166,7 +182,7 @@
 inline void CC_BASE_EXPORT NeedsBeginFrameChanged(int layer_tree_host_id,
                                                   bool new_value) {
   TRACE_EVENT_INSTANT2(
-      internal::kCategoryFrame, internal::kNeedsBeginFrameChanged,
+      internal::CategoryName::kTimelineFrame, internal::kNeedsBeginFrameChanged,
       TRACE_EVENT_SCOPE_THREAD, internal::kLayerTreeId, layer_tree_host_id,
       internal::kData, NeedsBeginFrameData(new_value));
 }
diff --git a/cc/benchmarks/benchmark_instrumentation.h b/cc/benchmarks/benchmark_instrumentation.h
index 9b5e5ef..d946c6e6 100644
--- a/cc/benchmarks/benchmark_instrumentation.h
+++ b/cc/benchmarks/benchmark_instrumentation.h
@@ -18,7 +18,11 @@
 // The benchmarks search for events and their arguments by name.
 
 namespace internal {
-const char kCategory[] = "cc,benchmark";
+constexpr const char* Category() {
+  // Declared as a constexpr function to have an external linkage and to be
+  // known at compile-time.
+  return "cc,benchmark";
+}
 const char kBeginFrameId[] = "begin_frame_id";
 }  // namespace internal
 
@@ -31,11 +35,11 @@
  public:
   ScopedBeginFrameTask(const char* event_name, unsigned int begin_frame_id)
       : event_name_(event_name) {
-    TRACE_EVENT_BEGIN1(internal::kCategory, event_name_,
+    TRACE_EVENT_BEGIN1(internal::Category(), event_name_,
                        internal::kBeginFrameId, begin_frame_id);
   }
   ~ScopedBeginFrameTask() {
-    TRACE_EVENT_END0(internal::kCategory, event_name_);
+    TRACE_EVENT_END0(internal::Category(), event_name_);
   }
 
  private:
diff --git a/cc/tiles/frame_viewer_instrumentation.cc b/cc/tiles/frame_viewer_instrumentation.cc
index 09ac5b6..c9418202 100644
--- a/cc/tiles/frame_viewer_instrumentation.cc
+++ b/cc/tiles/frame_viewer_instrumentation.cc
@@ -9,13 +9,10 @@
 namespace cc {
 namespace frame_viewer_instrumentation {
 
-const char kCategoryLayerTree[] =
-    TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
-        "viz.quads") "," TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers");
-
 namespace {
 
-const char kCategory[] = "cc," TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
+constexpr const char kCategory[] =
+    "cc," TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
 const char kTileData[] = "tileData";
 const char kLayerId[] = "layerId";
 const char kTileId[] = "tileId";
@@ -69,7 +66,7 @@
 
 bool IsTracingLayerTreeSnapshots() {
   bool category_enabled;
-  TRACE_EVENT_CATEGORY_GROUP_ENABLED(kCategoryLayerTree, &category_enabled);
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED(CategoryLayerTree(), &category_enabled);
   return category_enabled;
 }
 
diff --git a/cc/tiles/frame_viewer_instrumentation.h b/cc/tiles/frame_viewer_instrumentation.h
index d4cef01..2f389be 100644
--- a/cc/tiles/frame_viewer_instrumentation.h
+++ b/cc/tiles/frame_viewer_instrumentation.h
@@ -12,7 +12,12 @@
 namespace cc {
 namespace frame_viewer_instrumentation {
 
-extern const char kCategoryLayerTree[];
+constexpr const char* CategoryLayerTree() {
+  // Declared as a constexpr function to have an external linkage and to be
+  // known at compile-time.
+  return TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
+      "viz.quads") "," TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers");
+}
 
 class ScopedAnalyzeTask {
  public:
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7c912838..59f9e54 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2145,7 +2145,7 @@
   {
     TRACE_EVENT0("cc", "DrawLayers.FrameViewerTracing");
     TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
-        frame_viewer_instrumentation::kCategoryLayerTree,
+        frame_viewer_instrumentation::CategoryLayerTree(),
         "cc::LayerTreeHostImpl", id_, AsValueWithFrame(frame));
   }
 
@@ -2611,11 +2611,15 @@
 static void PopulateHitTestRegion(viz::HitTestRegion* hit_test_region,
                                   const LayerImpl* layer,
                                   uint32_t flags,
+                                  uint32_t async_hit_test_reasons,
                                   const gfx::Rect& rect,
                                   const viz::SurfaceId& surface_id,
                                   float device_scale_factor) {
   hit_test_region->frame_sink_id = surface_id.frame_sink_id();
   hit_test_region->flags = flags;
+  hit_test_region->async_hit_test_reasons = async_hit_test_reasons;
+  DCHECK_EQ(!!async_hit_test_reasons,
+            !!(flags & viz::HitTestRegionFlags::kHitTestAsk));
 
   hit_test_region->rect = rect;
   // The transform of hit test region maps a point from parent hit test region
@@ -2677,8 +2681,12 @@
           surface_layer->ScreenSpaceTransform(),
           gfx::Rect(surface_layer->bounds()));
       auto flag = GetFlagsForSurfaceLayer(surface_layer);
-      if (overlapping_region.Intersects(layer_screen_space_rect))
+      uint32_t async_hit_test_reasons =
+          viz::AsyncHitTestReasons::kNotAsyncHitTest;
+      if (overlapping_region.Intersects(layer_screen_space_rect)) {
         flag |= viz::HitTestRegionFlags::kHitTestAsk;
+        async_hit_test_reasons |= viz::AsyncHitTestReasons::kOverlappedRegion;
+      }
       if (surface_layer->is_clipped()) {
         bool layer_hit_test_region_is_rectangle =
             active_tree()
@@ -2689,13 +2697,16 @@
         content_rect =
             gfx::ScaleToEnclosingRect(surface_layer->visible_layer_rect(),
                                       device_scale_factor, device_scale_factor);
-        if (!layer_hit_test_region_is_rectangle)
+        if (!layer_hit_test_region_is_rectangle) {
           flag |= viz::HitTestRegionFlags::kHitTestAsk;
+          async_hit_test_reasons |= viz::AsyncHitTestReasons::kIrregularClip;
+        }
       }
       const auto& surface_id = surface_layer->range().end();
       hit_test_region_list->regions.emplace_back();
       PopulateHitTestRegion(&hit_test_region_list->regions.back(), layer, flag,
-                            content_rect, surface_id, device_scale_factor);
+                            async_hit_test_reasons, content_rect, surface_id,
+                            device_scale_factor);
       continue;
     }
     // TODO(sunxd): Submit all overlapping layer bounds as hit test regions.
diff --git a/chrome/DEPS b/chrome/DEPS
index df51072d..5998970 100644
--- a/chrome/DEPS
+++ b/chrome/DEPS
@@ -2,7 +2,8 @@
   "+crypto",
   "+gpu",
   "+net",
-  "+pdf",
+  "+pdf/buildflags.h",
+  "+pdf/pdf_features.h",
   "+printing",
   "+sql",
   # Browser, renderer, common and tests access V8 for various purposes.
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index ee0921846..2cea2d3 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1150,6 +1150,10 @@
         </receiver>
 
 
+        <service android:name="org.chromium.chrome.browser.tracing.TracingNotificationService"
+            android:exported="false"/>
+
+
         <meta-data android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
             android:value="org.chromium.content_public.browser.SmartClipProvider"/>
 
diff --git a/chrome/android/java/res/xml/developer_preferences.xml b/chrome/android/java/res/xml/developer_preferences.xml
index e619047..021f254 100644
--- a/chrome/android/java/res/xml/developer_preferences.xml
+++ b/chrome/android/java/res/xml/developer_preferences.xml
@@ -4,5 +4,8 @@
      found in the LICENSE file. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- TODO(eseckler): Add tracing preferences. -->
+    <Preference
+        android:fragment="org.chromium.chrome.browser.preferences.developer.TracingPreferences"
+        android:key="tracing"
+        android:title="@string/prefs_tracing"/>
 </PreferenceScreen>
diff --git a/chrome/android/java/res/xml/tracing_preferences.xml b/chrome/android/java/res/xml/tracing_preferences.xml
new file mode 100644
index 0000000..ca36165
--- /dev/null
+++ b/chrome/android/java/res/xml/tracing_preferences.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- TODO(eseckler): Add configuration options for categories and tracing mode. -->
+    <org.chromium.chrome.browser.preferences.ButtonPreference
+        android:key="start_recording"/>
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:key="tracing_status"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+</PreferenceScreen>
diff --git a/chrome/android/java/res_template/xml/file_paths.xml b/chrome/android/java/res_template/xml/file_paths.xml
index fbe8d24..82b4712 100644
--- a/chrome/android/java/res_template/xml/file_paths.xml
+++ b/chrome/android/java/res_template/xml/file_paths.xml
@@ -10,6 +10,7 @@
     <files-path name="images" path="images/"/>
     <cache-path name="cache" path="net-export/"/>
     <cache-path name="passwords" path="passwords/"/>
+    <cache-path name="traces" path="traces/"/>
     <cache-path name="webapk" path="webapks/" />
     <cache-path name="offline-cache" path="Offline Pages/archives/" />
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
new file mode 100644
index 0000000..50a09c4f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -0,0 +1,143 @@
+// 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.autofill_assistant;
+
+import android.os.Bundle;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/** Facade for starting Autofill Assistant on a custom tab. */
+public class AutofillAssistantFacade {
+    private static final String RFC_3339_FORMAT_WITHOUT_TIMEZONE = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
+
+    /** Prefix for Intent extras relevant to this feature. */
+    private static final String INTENT_EXTRA_PREFIX =
+            "org.chromium.chrome.browser.autofill_assistant.";
+
+    /** Autofill Assistant Study name. */
+    private static final String STUDY_NAME = "AutofillAssistant";
+
+    /** Variation url parameter name. */
+    private static final String URL_PARAMETER_NAME = "url";
+
+    /** Special parameter that enables the feature. */
+    private static final String PARAMETER_ENABLED = "ENABLED";
+
+    /** Returns true if all conditions are satisfied to start Autofill Assistant. */
+    public static boolean isConfigured(Bundle intentExtras) {
+        return getBooleanParameter(intentExtras, PARAMETER_ENABLED)
+                && !AutofillAssistantStudy.getUrl().isEmpty()
+                && ContextUtils.getAppSharedPreferences().getBoolean(
+                           AutofillAssistantPreferences.PREF_AUTOFILL_ASSISTANT_SWITCH, true);
+    }
+
+    /** Starts Autofill Assistant on the given {@code activity}. */
+    public static void start(CustomTabActivity activity) {
+        Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
+        parameters.remove(PARAMETER_ENABLED);
+
+        AutofillAssistantUiController controller =
+                new AutofillAssistantUiController(activity, parameters);
+        AutofillAssistantUiDelegate uiDelegate =
+                new AutofillAssistantUiDelegate(activity, controller);
+        UiDelegateHolder delegateHolder = new UiDelegateHolder(controller, uiDelegate);
+        controller.setUiDelegateHolder(delegateHolder);
+        initTabObservers(activity, delegateHolder);
+
+        AutofillAssistantUiDelegate.Details initialDetails = makeDetailsFromParameters(parameters);
+        controller.maybeUpdateDetails(initialDetails);
+
+        uiDelegate.startOrSkipInitScreen();
+    }
+
+    private static void initTabObservers(
+            CustomTabActivity activity, UiDelegateHolder delegateHolder) {
+        // Shut down Autofill Assistant when the tab is detached from the activity.
+        Tab activityTab = activity.getActivityTab();
+        activityTab.addObserver(new EmptyTabObserver() {
+            @Override
+            public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
+                if (!isAttached) {
+                    activityTab.removeObserver(this);
+                    delegateHolder.shutdown();
+                }
+            }
+        });
+
+        // Shut down Autofill Assistant when the selected tab (foreground tab) is changed.
+        TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel();
+        currentTabModel.addObserver(new EmptyTabModelObserver() {
+            @Override
+            public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
+                currentTabModel.removeObserver(this);
+                delegateHolder.giveUp();
+            }
+        });
+    }
+
+    /** Return the value if the given boolean parameter from the extras. */
+    private static boolean getBooleanParameter(Bundle extras, String parameterName) {
+        return extras.getBoolean(INTENT_EXTRA_PREFIX + parameterName, false);
+    }
+
+    /** Returns a map containing the extras starting with {@link #INTENT_EXTRA_PREFIX}. */
+    private static Map<String, String> extractParameters(Bundle extras) {
+        Map<String, String> result = new HashMap<>();
+        for (String key : extras.keySet()) {
+            if (key.startsWith(INTENT_EXTRA_PREFIX)) {
+                result.put(key.substring(INTENT_EXTRA_PREFIX.length()), extras.get(key).toString());
+            }
+        }
+        return result;
+    }
+
+    // TODO(crbug.com/806868): Create a fallback when there are no parameters for details.
+    // TODO(crbug.com/806868): Create a fallback when there are no parameters for details.
+    private static AutofillAssistantUiDelegate.Details makeDetailsFromParameters(
+            Map<String, String> parameters) {
+        String title = "";
+        String description = "";
+        Date date = null;
+        for (String key : parameters.keySet()) {
+            if (key.contains("E_NAME")) {
+                title = parameters.get(key);
+                continue;
+            }
+
+            if (key.contains("R_NAME")) {
+                description = parameters.get(key);
+                continue;
+            }
+
+            if (key.contains("DATETIME")) {
+                try {
+                    // The parameter contains the timezone shift from the current location, that we
+                    // don't care about.
+                    date = new SimpleDateFormat(RFC_3339_FORMAT_WITHOUT_TIMEZONE, Locale.ROOT)
+                                   .parse(parameters.get(key));
+                } catch (ParseException e) {
+                    // Ignore.
+                }
+            }
+        }
+
+        return new AutofillAssistantUiDelegate.Details(
+                title, /* url= */ "", date, description, /* isFinal= */ false);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index ae5d47cd..bdb8810 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -5,40 +5,28 @@
 package org.chromium.chrome.browser.autofill_assistant;
 
 import android.accounts.Account;
+import android.content.Context;
 import android.graphics.RectF;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.ThreadUtils;
+import org.chromium.base.LocaleUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
-import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.tab.EmptyTabObserver;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
-import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentOptions;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
-import java.util.Queue;
 
 /**
  * Bridge to native side autofill_assistant::UiControllerAndroid. It allows native side to control
@@ -57,25 +45,19 @@
     private static final String AUTH_TOKEN_TYPE =
             "oauth2:https://www.googleapis.com/auth/userinfo.profile";
 
-    /** Display the final message for that long before shutting everything down. */
-    private static final long GRACEFUL_SHUTDOWN_DELAY_MS = 5000;
-
-    private static final String RFC_3339_FORMAT_WITHOUT_TIMEZONE = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
-
     private final WebContents mWebContents;
-    private final UiDelegateHolder mUiDelegateHolder;
     private final String mInitialUrl;
 
     // TODO(crbug.com/806868): Move mCurrentDetails and mStatusMessage to a Model (refactor to MVC).
-    AutofillAssistantUiDelegate.Details mCurrentDetails =
+    private AutofillAssistantUiDelegate.Details mCurrentDetails =
             AutofillAssistantUiDelegate.Details.getEmptyDetails();
     private String mStatusMessage;
 
-    /**
-     * Native pointer to the UIController.
-     */
+    /** Native pointer to the UIController. */
     private final long mUiControllerAndroid;
 
+    private UiDelegateHolder mUiDelegateHolder;
+
     /**
      * Indicates whether {@link mAccount} has been initialized.
      */
@@ -93,69 +75,30 @@
     private boolean mShouldFetchAccessToken;
 
     /**
-     * Returns true if all conditions are satisfied to construct an AutofillAssistantUiController.
-     *
-     * @return True if a controller can be constructed.
-     */
-    public static boolean isConfigured(Bundle intentExtras) {
-        return getBooleanParameter(intentExtras, PARAMETER_ENABLED)
-                && !AutofillAssistantStudy.getUrl().isEmpty()
-                && ContextUtils.getAppSharedPreferences().getBoolean(
-                           AutofillAssistantPreferences.PREF_AUTOFILL_ASSISTANT_SWITCH, true);
-    }
-
-    @Override
-    public void onInitOk() {
-        nativeStart(mUiControllerAndroid, mInitialUrl);
-    }
-
-    /**
      * Construct Autofill Assistant UI controller.
      *
      * @param activity The CustomTabActivity of the controller associated with.
      */
-    public AutofillAssistantUiController(CustomTabActivity activity) {
-        // Set mUiDelegate before nativeInit, as it can be accessed through native methods from
-        // nativeInit already.
-        mUiDelegateHolder = new UiDelegateHolder(new AutofillAssistantUiDelegate(activity, this));
-        chooseAccountAsync(activity.getInitialIntent().getExtras());
-
-        Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
-        parameters.remove(PARAMETER_ENABLED);
-
-        maybeUpdateDetails(makeDetailsFromParameters(parameters));
-
-        Tab activityTab = activity.getActivityTab();
-        mWebContents = activityTab.getWebContents();
+    public AutofillAssistantUiController(
+            CustomTabActivity activity, Map<String, String> parameters) {
+        mWebContents = activity.getActivityTab().getWebContents();
         mInitialUrl = activity.getInitialIntent().getDataString();
-
         mUiControllerAndroid =
                 nativeInit(mWebContents, parameters.keySet().toArray(new String[parameters.size()]),
-                        parameters.values().toArray(new String[parameters.size()]));
+                        parameters.values().toArray(new String[parameters.size()]),
+                        LocaleUtils.getDefaultLocaleString(), getCountryIso());
 
-        mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.startOrSkipInitScreen());
+        chooseAccountAsync(activity.getInitialIntent().getExtras());
+    }
 
-        // Shut down Autofill Assistant when the tab is detached from the activity.
-        activityTab.addObserver(new EmptyTabObserver() {
-            @Override
-            public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
-                if (!isAttached) {
-                    activityTab.removeObserver(this);
-                    mUiDelegateHolder.shutdown();
-                }
-            }
-        });
+    void setUiDelegateHolder(UiDelegateHolder uiDelegateHolder) {
+        mUiDelegateHolder = uiDelegateHolder;
+    }
 
-        // Shut down Autofill Assistant when the selected tab (foreground tab) is changed.
-        TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel();
-        currentTabModel.addObserver(new EmptyTabModelObserver() {
-            @Override
-            public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
-                currentTabModel.removeObserver(this);
-
-                nativeGiveUp(mUiControllerAndroid);
-            }
-        });
+    @Override
+    public void onInitOk() {
+        assert mUiDelegateHolder != null;
+        nativeStart(mUiControllerAndroid, mInitialUrl);
     }
 
     @Override
@@ -198,60 +141,25 @@
         return nativeOnRequestDebugContext(mUiControllerAndroid);
     }
 
-    /** Return the value if the given boolean parameter from the extras. */
-    private static boolean getBooleanParameter(Bundle extras, String parameterName) {
-        return extras.getBoolean(INTENT_EXTRA_PREFIX + parameterName, false);
-    }
-
-    /** Returns a map containing the extras starting with {@link #INTENT_EXTRA_PREFIX}. */
-    private static Map<String, String> extractParameters(Bundle extras) {
-        Map<String, String> result = new HashMap<>();
-        for (String key : extras.keySet()) {
-            if (key.startsWith(INTENT_EXTRA_PREFIX)) {
-                result.put(key.substring(INTENT_EXTRA_PREFIX.length()), extras.get(key).toString());
-            }
-        }
-        return result;
-    }
-
-    // TODO(crbug.com/806868): Create a fallback when there are no parameters for details.
-    private static AutofillAssistantUiDelegate.Details makeDetailsFromParameters(
-            Map<String, String> parameters) {
-        String title = "";
-        String description = "";
-        Date date = null;
-        for (String key : parameters.keySet()) {
-            if (key.contains("E_NAME")) {
-                title = parameters.get(key);
-                continue;
-            }
-
-            if (key.contains("R_NAME")) {
-                description = parameters.get(key);
-                continue;
-            }
-
-            if (key.contains("DATETIME")) {
-                try {
-                    // The parameter contains the timezone shift from the current location, that we
-                    // don't care about.
-                    date = new SimpleDateFormat(RFC_3339_FORMAT_WITHOUT_TIMEZONE, Locale.ROOT)
-                                   .parse(parameters.get(key));
-                } catch (ParseException e) {
-                    // Ignore.
-                }
-            }
-        }
-
-        return new AutofillAssistantUiDelegate.Details(
-                title, /* url= */ "", date, description, /* isFinal= */ false);
-    }
-
     @Override
     public void onClickOverlay() {
         // TODO(crbug.com/806868): Notify native side.
     }
 
+    /**
+     * Immediately and unconditionally destroys the UI Controller.
+     *
+     * <p>Call {@link UiDelegateHolder#shutdown} to shutdown Autofill Assistant properly and safely.
+     *
+     * <p>Destroy is different from shutdown in that {@code unsafeDestroy} just deletes the native
+     * controller and all the objects it owns, without changing the state of the UI. When that
+     * happens, everything stops irrevocably on the native side. Doing this at the wrong time will
+     * cause crashes.
+     */
+    void unsafeDestroy() {
+        nativeDestroy(mUiControllerAndroid);
+    }
+
     @CalledByNative
     private void onShowStatusMessage(String message) {
         mStatusMessage = message;
@@ -345,9 +253,7 @@
                             // A failed payment request flow indicates that the UI was either
                             // dismissed or the back button was clicked. In that case we gracefully
                             // shut down.
-                            onHideOverlay();
-                            uiDelegate.showGiveUpMessage();
-                            onShutdownGracefully();
+                            mUiDelegateHolder.giveUp();
                         }
                     }));
         });
@@ -358,7 +264,7 @@
      *
      * @return false if details were rejected.
      */
-    private boolean maybeUpdateDetails(AutofillAssistantUiDelegate.Details newDetails) {
+    boolean maybeUpdateDetails(AutofillAssistantUiDelegate.Details newDetails) {
         if (!mCurrentDetails.isSimilarTo(newDetails)) {
             return false;
         }
@@ -418,131 +324,6 @@
                 uiDelegate -> { uiDelegate.updateTouchableArea(enabled, boxes); });
     }
 
-    /**
-     * Class holder for the AutofillAssistantUiDelegate to make sure we don't make UI changes when
-     * we are in a pause state (i.e. few seconds before stopping completely).
-     */
-    private class UiDelegateHolder {
-        private final AutofillAssistantUiDelegate mUiDelegate;
-
-        private boolean mShouldQueueUiOperations = false;
-        private boolean mHasBeenShutdown = false;
-        private boolean mIsShuttingDown = false;
-        private SnackbarManager.SnackbarController mDismissSnackbar;
-        private final Queue<Callback<AutofillAssistantUiDelegate>> mPendingUiOperations =
-                new ArrayDeque<>();
-
-        private UiDelegateHolder(AutofillAssistantUiDelegate uiDelegate) {
-            mUiDelegate = uiDelegate;
-        }
-
-        /**
-         * Perform a UI operation:
-         *  - directly if we are not in a pause state.
-         *  - later if the shutdown is cancelled.
-         *  - never if Autofill Assistant is shut down.
-         */
-        public void performUiOperation(Callback<AutofillAssistantUiDelegate> operation) {
-            if (mHasBeenShutdown || mIsShuttingDown) {
-                return;
-            }
-
-            if (mShouldQueueUiOperations) {
-                mPendingUiOperations.add(operation);
-                return;
-            }
-
-            operation.onResult(mUiDelegate);
-        }
-
-        /**
-         * Handles the dismiss operation.
-         *
-         * In normal mode, hides the UI, pauses UI operations and, unless undone within the time
-         * delay, eventually destroy everything. In graceful shutdown mode, shutdown immediately.
-         */
-        public void dismiss(int stringResourceId, Object... formatArgs) {
-            assert !mHasBeenShutdown;
-
-            if (mIsShuttingDown) {
-                shutdown();
-                return;
-            }
-
-            if (mDismissSnackbar != null) {
-                // Remove duplicate calls.
-                return;
-            }
-
-            pauseUiOperations();
-            mUiDelegate.hide();
-            mDismissSnackbar = new SnackbarManager.SnackbarController() {
-                @Override
-                public void onAction(Object actionData) {
-                    // Shutdown was cancelled.
-                    mDismissSnackbar = null;
-                    mUiDelegate.show();
-                    unpauseUiOperations();
-                }
-
-                @Override
-                public void onDismissNoAction(Object actionData) {
-                    shutdown();
-                }
-            };
-            mUiDelegate.showAutofillAssistantStoppedSnackbar(
-                    mDismissSnackbar, stringResourceId, formatArgs);
-        }
-
-        /** Enters graceful shutdown mode once we can again perform UI operations. */
-        public void enterGracefulShutdownMode() {
-            mUiDelegateHolder.performUiOperation(uiDelegate -> {
-                mIsShuttingDown = true;
-                mPendingUiOperations.clear();
-                uiDelegate.enterGracefulShutdownMode();
-                ThreadUtils.postOnUiThreadDelayed(this ::shutdown, GRACEFUL_SHUTDOWN_DELAY_MS);
-            });
-        }
-
-        /**
-         * Hides the UI and destroys everything.
-         *
-         * <p>Shutdown is final: After this call from the C++ side, as it's been deleted and no UI
-         * operation can run.
-         */
-        public void shutdown() {
-            if (mHasBeenShutdown) {
-                return;
-            }
-
-            mHasBeenShutdown = true;
-            mPendingUiOperations.clear();
-            if (mDismissSnackbar != null) {
-                mUiDelegate.dismissSnackbar(mDismissSnackbar);
-            }
-            mUiDelegate.hide();
-            nativeDestroy(mUiControllerAndroid);
-        }
-
-        /**
-         * Pause all UI operations such that they can potentially be ran later using {@link
-         * #unpauseUiOperations()}.
-         */
-        private void pauseUiOperations() {
-            mShouldQueueUiOperations = true;
-        }
-
-        /**
-         * Unpause and trigger all UI operations received by {@link #performUiOperation(Callback)}
-         * since the last {@link #pauseUiOperations()}.
-         */
-        private void unpauseUiOperations() {
-            mShouldQueueUiOperations = false;
-            while (!mPendingUiOperations.isEmpty()) {
-                mPendingUiOperations.remove().onResult(mUiDelegate);
-            }
-        }
-    }
 
     @CalledByNative
     private void fetchAccessToken() {
@@ -631,12 +412,27 @@
         return null;
     }
 
+    /** Returns the country that the device is currently located in. This currently only works
+     * for devices with active SIM cards. For a more general solution, we should probably use
+     * the LocationManager together with the Geocoder.*/
+    private String getCountryIso() {
+        TelephonyManager telephonyManager =
+                (TelephonyManager) ContextUtils.getApplicationContext().getSystemService(
+                        Context.TELEPHONY_SERVICE);
+
+        // According to API, location for CDMA networks is unreliable
+        if (telephonyManager != null
+                && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA)
+            return telephonyManager.getNetworkCountryIso();
+        else
+            return null;
+    }
+
     // native methods.
-    private native long nativeInit(
-            WebContents webContents, String[] parameterNames, String[] parameterValues);
+    private native long nativeInit(WebContents webContents, String[] parameterNames,
+            String[] parameterValues, String locale, String countryCode);
     private native void nativeStart(long nativeUiControllerAndroid, String initialUrl);
     private native void nativeDestroy(long nativeUiControllerAndroid);
-    private native void nativeGiveUp(long nativeUiControllerAndroid);
     private native void nativeOnScriptSelected(long nativeUiControllerAndroid, String scriptPath);
     private native void nativeOnAddressSelected(long nativeUiControllerAndroid, String guid);
     private native void nativeOnCardSelected(long nativeUiControllerAndroid, String guid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index 8125d7a..c0b3d18 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -523,6 +523,8 @@
         // hacks to give enough space to display long messages.
         mStatusMessageView.setMaxLines(4);
         mBottomBar.findViewById(R.id.feedback_button).setVisibility(View.GONE);
+        hideOverlay();
+        mTouchEventFilter.setVisibility(View.GONE);
         hideProgressBar();
         hideDetails();
         mBottomBarAnimations.hideCarousel();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java
new file mode 100644
index 0000000..be4d1ef
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java
@@ -0,0 +1,150 @@
+// 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.autofill_assistant;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Class holder for the AutofillAssistantUiDelegate to make sure we don't make UI changes when
+ * we are in a pause state (i.e. few seconds before stopping completely).
+ */
+class UiDelegateHolder {
+    /** Display the final message for that long before shutting everything down. */
+    private static final long GRACEFUL_SHUTDOWN_DELAY_MS = 5_000;
+
+    private final AutofillAssistantUiController mUiController;
+    private final AutofillAssistantUiDelegate mUiDelegate;
+
+    private boolean mPaused;
+    private boolean mHasBeenShutdown;
+    private boolean mIsShuttingDown;
+    private SnackbarManager.SnackbarController mDismissSnackbar;
+    private final Queue<Callback<AutofillAssistantUiDelegate>> mPendingUiOperations =
+            new ArrayDeque<>();
+
+    UiDelegateHolder(
+            AutofillAssistantUiController controller, AutofillAssistantUiDelegate uiDelegate) {
+        mUiController = controller;
+        mUiDelegate = uiDelegate;
+    }
+
+    /**
+     * Perform a UI operation:
+     *  - directly if we are not in a pause state.
+     *  - later if the shutdown is cancelled.
+     *  - never if Autofill Assistant is shut down.
+     */
+    void performUiOperation(Callback<AutofillAssistantUiDelegate> operation) {
+        if (mHasBeenShutdown || mIsShuttingDown) {
+            return;
+        }
+
+        if (mPaused) {
+            mPendingUiOperations.add(operation);
+            return;
+        }
+
+        operation.onResult(mUiDelegate);
+    }
+
+    /**
+     * Handles the dismiss operation.
+     *
+     * In normal mode, hides the UI, pauses UI operations and, unless undone within the time
+     * delay, eventually destroy everything. In graceful shutdown mode, shutdown immediately.
+     */
+    void dismiss(int stringResourceId, Object... formatArgs) {
+        assert !mHasBeenShutdown;
+
+        if (mIsShuttingDown) {
+            shutdown();
+            return;
+        }
+
+        if (mDismissSnackbar != null) {
+            // Remove duplicate calls.
+            return;
+        }
+
+        pauseUiOperations();
+        mUiDelegate.hide();
+        mDismissSnackbar = new SnackbarManager.SnackbarController() {
+            @Override
+            public void onAction(Object actionData) {
+                // Shutdown was cancelled.
+                mDismissSnackbar = null;
+                mUiDelegate.show();
+                unpauseUiOperations();
+            }
+
+            @Override
+            public void onDismissNoAction(Object actionData) {
+                shutdown();
+            }
+        };
+        mUiDelegate.showAutofillAssistantStoppedSnackbar(
+                mDismissSnackbar, stringResourceId, formatArgs);
+    }
+
+    /** Displays the give up message and enter graceful shutdown mode. */
+    void giveUp() {
+        performUiOperation(uiDelegate -> uiDelegate.showGiveUpMessage());
+        enterGracefulShutdownMode();
+    }
+
+    /** Enters graceful shutdown mode once we can again perform UI operations. */
+    void enterGracefulShutdownMode() {
+        performUiOperation(uiDelegate -> {
+            mIsShuttingDown = true;
+            mPendingUiOperations.clear();
+            uiDelegate.enterGracefulShutdownMode();
+            ThreadUtils.postOnUiThreadDelayed(this ::shutdown, GRACEFUL_SHUTDOWN_DELAY_MS);
+        });
+    }
+
+    /**
+     * Hides the UI and destroys everything.
+     *
+     * <p>Shutdown is final: After this call from the C++ side, as it's been deleted and no UI
+     * operation can run.
+     */
+    void shutdown() {
+        if (mHasBeenShutdown) {
+            return;
+        }
+
+        mHasBeenShutdown = true;
+        mPendingUiOperations.clear();
+        if (mDismissSnackbar != null) {
+            mUiDelegate.dismissSnackbar(mDismissSnackbar);
+        }
+        mUiDelegate.hide();
+        mUiController.unsafeDestroy();
+    }
+
+    /**
+     * Pause all UI operations such that they can potentially be ran later using {@link
+     * #unpauseUiOperations()}.
+     */
+    private void pauseUiOperations() {
+        mPaused = true;
+    }
+
+    /**
+     * Unpause and trigger all UI operations received by {@link #performUiOperation(Callback)}
+     * since the last {@link #pauseUiOperations()}.
+     */
+    private void unpauseUiOperations() {
+        mPaused = false;
+        while (!mPendingUiOperations.isEmpty()) {
+            mPendingUiOperations.remove().onResult(mUiDelegate);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestUI.java
index ed39e7b..f8aecce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestUI.java
@@ -35,7 +35,6 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPaymentRequest;
 import org.chromium.chrome.browser.payments.ShippingStrings;
@@ -349,46 +348,39 @@
         mShippingOptionSection.setCanAddItems(false);
         mPaymentMethodSection.setCanAddItems(canAddCards);
 
-        // Put payment method section on top of address section for
-        // WEB_PAYMENTS_METHOD_SECTION_ORDER_V2.
-        boolean methodSectionOrderV2 =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_PAYMENTS_METHOD_SECTION_ORDER_V2);
-
         // Add the necessary sections to the layout.
         mPaymentContainerLayout.addView(mOrderSummarySection,
                 new LinearLayout.LayoutParams(
                         LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        if (methodSectionOrderV2) {
-            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
-            mPaymentContainerLayout.addView(mPaymentMethodSection,
-                    new LinearLayout.LayoutParams(
-                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        }
-        if (mRequestShipping) {
-            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
-            // The shipping breakout sections are only added if they are needed.
-            mPaymentContainerLayout.addView(mShippingAddressSection,
-                    new LinearLayout.LayoutParams(
-                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        }
-        if (!methodSectionOrderV2) {
-            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
-            mPaymentContainerLayout.addView(mPaymentMethodSection,
-                    new LinearLayout.LayoutParams(
-                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        }
+
         if (mRequestContactDetails) {
-            // Contact details are optional, depending on the merchant website.
-            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
             mPaymentContainerLayout.addView(mContactDetailsSection,
                     new LinearLayout.LayoutParams(
                             LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
         }
 
+        if (mRequestShipping) {
+            if (mRequestContactDetails)
+                mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
+            // The shipping breakout sections are only added if they are needed.
+            mPaymentContainerLayout.addView(mShippingAddressSection,
+                    new LinearLayout.LayoutParams(
+                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        }
+
+        if (mRequestContactDetails || mRequestShipping)
+            mSectionSeparators.add(new SectionSeparator(mPaymentContainerLayout));
+        mPaymentContainerLayout.addView(mPaymentMethodSection,
+                new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+
         mRequestView.addOnLayoutChangeListener(new PeekingAnimator());
 
         // Enabled in updatePayButtonEnabled() when the user has selected all payment options.
         mPayButton.setEnabled(false);
+
+        // Force the initial appearance of edit chevrons next to all sections.
+        updateSectionVisibility();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index e17cdaec..fd47dc24 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -63,7 +63,7 @@
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.WebContentsFactory;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
-import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
+import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler;
 import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
 import org.chromium.chrome.browser.browserservices.PostMessageHandler;
@@ -189,8 +189,6 @@
 
     private WebappCustomTabTimeSpentLogger mWebappTimeSpentLogger;
 
-    private AutofillAssistantUiController mAutofillAssistantUiController;
-
     @Nullable
     private ModuleEntryPoint mModuleEntryPoint;
     @Nullable
@@ -700,10 +698,9 @@
                     getActivityTab().getWebContents());
         }
 
-        if (mAutofillAssistantUiController == null
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ASSISTANT)
-                && AutofillAssistantUiController.isConfigured(getInitialIntent().getExtras())) {
-            mAutofillAssistantUiController = new AutofillAssistantUiController(this);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_ASSISTANT)
+                && AutofillAssistantFacade.isConfigured(getInitialIntent().getExtras())) {
+            AutofillAssistantFacade.start(this);
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && useSeparateTask()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
index 637a3297..dbaedf1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
@@ -199,6 +199,7 @@
         setTitleScrollable(mParams.titleScrollable);
         setMessage(mParams.message);
         setCustomView(mParams.customView);
+        setCancelOnTouchOutside(mParams.cancelOnTouchOutside);
 
         Resources resources = mDialogView.getResources();
         assert(mParams.positiveButtonTextId == 0 || mParams.positiveButtonText == null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
index 6591a5b8..71732ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
@@ -304,7 +304,7 @@
         for (URI uriMethodName : uriMethods) {
             if (!methodToAppsMapping.containsKey(uriMethodName.toString())) continue;
 
-            if (!mParser.isNativeInitialized()) mParser.createNative();
+            if (!mParser.isNativeInitialized()) mParser.createNative(mWebContents);
 
             // Initialize the native side of the downloader, once we know that a manifest file needs
             // to be downloaded.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java
new file mode 100644
index 0000000..c03bfc6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java
@@ -0,0 +1,83 @@
+// 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.preferences.developer;
+
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.PreferenceUtils;
+import org.chromium.chrome.browser.tracing.TracingController;
+import org.chromium.chrome.browser.tracing.TracingNotificationManager;
+
+/**
+ * Settings fragment that shows options for recording a performance trace.
+ */
+public class TracingPreferences extends PreferenceFragment implements TracingController.Observer {
+    @VisibleForTesting
+    static final String UI_PREF_START_RECORDING = "start_recording";
+    @VisibleForTesting
+    static final String UI_PREF_TRACING_STATUS = "tracing_status";
+
+    private Preference mPrefStartRecording;
+    private Preference mPrefTracingStatus;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getActivity().setTitle(R.string.prefs_tracing);
+        PreferenceUtils.addPreferencesFromResource(this, R.xml.tracing_preferences);
+
+        mPrefStartRecording = findPreference(UI_PREF_START_RECORDING);
+        mPrefTracingStatus = findPreference(UI_PREF_TRACING_STATUS);
+
+        mPrefStartRecording.setOnPreferenceClickListener(preference -> {
+            TracingController.getInstance().startRecording();
+            updatePreferences();
+            return true;
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updatePreferences();
+        TracingController.getInstance().addObserver(this);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        TracingController.getInstance().removeObserver(this);
+    }
+
+    @Override
+    public void onTracingStateChanged(@TracingController.State int state) {
+        updatePreferences();
+    }
+
+    private void updatePreferences() {
+        @TracingController.State
+        int state = TracingController.getInstance().getState();
+        boolean initialized = state != TracingController.State.INITIALIZING;
+        boolean idle = state == TracingController.State.IDLE || !initialized;
+        boolean notificationsEnabled = TracingNotificationManager.browserNotificationsEnabled();
+
+        mPrefStartRecording.setEnabled(idle && initialized && notificationsEnabled);
+
+        if (!notificationsEnabled) {
+            mPrefStartRecording.setTitle(R.string.prefs_tracing_start);
+            mPrefTracingStatus.setTitle(R.string.tracing_notifications_disabled);
+        } else if (idle) {
+            mPrefStartRecording.setTitle(R.string.prefs_tracing_start);
+            mPrefTracingStatus.setTitle(R.string.prefs_tracing_privacy_notice);
+        } else {
+            mPrefStartRecording.setTitle(R.string.prefs_tracing_active);
+            mPrefTracingStatus.setTitle(R.string.prefs_tracing_active_summary);
+        }
+    }
+}
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 2df8e7d..7cca885b 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
@@ -547,13 +547,16 @@
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == ADD_ACCOUNT_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            if (data == null) return;
+            String addedAccountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+            if (addedAccountName == null) return;
+
+            // Found the account name, dismiss the account picker dialog if it is shown.
             AccountPickerDialogFragment accountPickerFragment = getAccountPickerDialogFragment();
             if (accountPickerFragment != null) {
                 accountPickerFragment.dismiss();
             }
 
-            String addedAccountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
-            if (addedAccountName == null) return;
             // Wait for the account cache to be updated and select newly-added account.
             AccountManagerFacade.get().waitForPendingUpdates(() -> {
                 mAccountSelectionPending = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/tracing/OWNERS
new file mode 100644
index 0000000..8d74ba0a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/OWNERS
@@ -0,0 +1,6 @@
+file://base/trace_event/OWNERS
+eseckler@chromium.org
+skyostil@chromium.org
+
+# TEAM: tracing@chromium.org
+# COMPONENT: Speed>Tracing
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingController.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingController.java
new file mode 100644
index 0000000..6911b4e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingController.java
@@ -0,0 +1,281 @@
+// 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.tracing;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.ObserverList;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.chrome.R;
+import org.chromium.content_public.browser.TracingControllerAndroid;
+import org.chromium.ui.widget.Toast;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TimeZone;
+
+/**
+ * Coordinates recording and saving/sharing Chrome performance traces.
+ */
+public class TracingController {
+    /**
+     * Observer that is notified when the controller's tracing state changes.
+     */
+    public interface Observer {
+        /**
+         * Called by the TracingController when its state changes.
+         *
+         * @param state the new state of the Controller.
+         */
+        void onTracingStateChanged(@State int state);
+    }
+
+    /**
+     * State of the controller. There can only be one active tracing session at the same time.
+     */
+    @IntDef({State.INITIALIZING, State.IDLE, State.STARTING, State.RECORDING, State.STOPPING,
+            State.STOPPED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+        int INITIALIZING = 0;
+        int IDLE = 1;
+        int STARTING = 2;
+        int RECORDING = 3;
+        int STOPPING = 4;
+        int STOPPED = 5;
+    }
+
+    private static final String TAG = "TracingController";
+    private static final String TEMP_FILE_DIR = "/traces";
+    private static final String TEMP_FILE_PREFIX = "chrome-trace-";
+    private static final String TEMP_FILE_EXT = ".json.gz";
+
+    private static TracingController sInstance;
+
+    // Only set while a trace is in progress to avoid leaking native resources.
+    private TracingControllerAndroid mNativeController;
+
+    private ObserverList<Observer> mObservers = new ObserverList<>();
+    private @State int mState = State.INITIALIZING;
+    private Set<String> mKnownCategories;
+    private File mTracingTempFile;
+
+    private TracingController() {}
+
+    /**
+     * @return the singleton instance of TracingController, creating and initializing it if needed.
+     */
+    public static TracingController getInstance() {
+        if (sInstance == null) {
+            sInstance = new TracingController();
+            sInstance.initialize();
+        }
+        return sInstance;
+    }
+
+    private void initialize() {
+        mNativeController = TracingControllerAndroid.create(ContextUtils.getApplicationContext());
+        mNativeController.getKnownCategories(categories -> {
+            mKnownCategories = new HashSet<>(Arrays.asList(categories));
+
+            // Also cleans up the controller.
+            setState(State.IDLE);
+        });
+    }
+
+    /**
+     * Add the given observer to the controller's observer list.
+     *
+     * @param obs the observer to add.
+     */
+    public void addObserver(Observer obs) {
+        mObservers.addObserver(obs);
+    }
+
+    /**
+     * Remove the given observer from the controller's observer list.
+     *
+     * @param obs the observer to add.
+     */
+    public void removeObserver(Observer obs) {
+        mObservers.removeObserver(obs);
+    }
+
+    /**
+     * @return the current state of the controller.
+     */
+    public @State int getState() {
+        return mState;
+    }
+
+    /**
+     * @return the temporary file that the trace is written into.
+     */
+    @VisibleForTesting
+    public File getTracingTempFile() {
+        return mTracingTempFile;
+    }
+
+    /**
+     * Should only be called after the controller was initialized.
+     * @see TracingController#getState()
+     * @see Observer#onTracingStateChanged(int)
+     *
+     * @return the set of known tracing categories.
+     */
+    public Set<String> getKnownCategories() {
+        assert mKnownCategories != null;
+        return mKnownCategories;
+    }
+
+    /**
+     * Starts recording a trace into a temporary file and shows a persistent tracing notification.
+     * Should only be called when in IDLE state.
+     */
+    public void startRecording() {
+        assert mState == State.IDLE;
+        assert mNativeController == null;
+        assert TracingNotificationManager.browserNotificationsEnabled();
+
+        mNativeController = TracingControllerAndroid.create(ContextUtils.getApplicationContext());
+
+        setState(State.STARTING);
+        TracingNotificationManager.showTracingActiveNotification();
+
+        new CreateTempFileAndStartTraceTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    private class CreateTempFileAndStartTraceTask extends AsyncTask<File> {
+        @Override
+        protected File doInBackground() {
+            File cacheDir =
+                    new File(ContextUtils.getApplicationContext().getCacheDir() + TEMP_FILE_DIR);
+            cacheDir.mkdir();
+            File tracingTempFile;
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US);
+            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+            try {
+                tracingTempFile = new File(
+                        cacheDir, TEMP_FILE_PREFIX + formatter.format(new Date()) + TEMP_FILE_EXT);
+                tracingTempFile.createNewFile();
+            } catch (IOException e) {
+                Log.e(TAG, "Couldn't create chrome-trace temp file: %s", e.getMessage());
+                return null;
+            }
+            return tracingTempFile;
+        }
+
+        @Override
+        protected void onPostExecute(File result) {
+            if (result == null) {
+                showErrorToast();
+                setState(State.IDLE);
+                return;
+            }
+            mTracingTempFile = result;
+            startNativeTrace();
+        }
+    }
+
+    private void startNativeTrace() {
+        assert mState == State.STARTING;
+
+        // TODO(eseckler): Support configuring these.
+        String categories = "*";
+        String options = "record-until-full";
+
+        if (!mNativeController.startTracing(
+                    mTracingTempFile.getPath(), false, categories, options, true)) {
+            Log.e(TAG, "Native error while trying to start tracing");
+            showErrorToast();
+            setState(State.IDLE);
+            return;
+        }
+
+        setState(State.RECORDING);
+    }
+
+    /**
+     * Stops an active trace recording and updates the tracing notification with stopping status.
+     * Should only be called when in RECORDING state.
+     */
+    public void stopRecording() {
+        assert mState == State.RECORDING;
+
+        setState(State.STOPPING);
+        TracingNotificationManager.showTracingStoppingNotification();
+
+        mNativeController.stopTracing((Void v) -> {
+            assert mState == State.STOPPING;
+
+            setState(State.STOPPED);
+            TracingNotificationManager.showTracingCompleteNotification();
+        });
+    }
+
+    // TODO(eseckler): Add a way to download and/or share the trace.
+
+    /**
+     * Discards a recorded trace and cleans up the temporary trace file.
+     * Should only be called when in STOPPED state.
+     */
+    public void discardTrace() {
+        assert mState == State.STOPPED;
+        // Setting the state also cleans up the temp file.
+        setState(State.IDLE);
+    }
+
+    private void setState(@State int state) {
+        Log.d(TAG, "State changing to %d", state);
+        mState = state;
+        if (mState == State.IDLE) {
+            TracingNotificationManager.dismissNotification();
+            if (mTracingTempFile != null) {
+                new DeleteTempFileTask(mTracingTempFile)
+                        .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                mTracingTempFile = null;
+            }
+
+            // Clean up the controller while idle to avoid leaking native resources.
+            mNativeController.destroy();
+            mNativeController = null;
+        }
+        for (Observer obs : mObservers) {
+            obs.onTracingStateChanged(state);
+        }
+    }
+
+    private class DeleteTempFileTask extends AsyncTask<Void> {
+        private File mTracingTempFile;
+
+        public DeleteTempFileTask(File tracingTempFile) {
+            mTracingTempFile = tracingTempFile;
+        }
+
+        @Override
+        protected Void doInBackground() {
+            mTracingTempFile.delete();
+            return null;
+        }
+    }
+
+    private void showErrorToast() {
+        Context context = ContextUtils.getApplicationContext();
+        Toast.makeText(context, context.getString(R.string.tracing_error_toast), Toast.LENGTH_SHORT)
+                .show();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
new file mode 100644
index 0000000..58951d34
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
@@ -0,0 +1,166 @@
+// 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.tracing;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
+import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
+import org.chromium.chrome.browser.notifications.NotificationManagerProxy;
+import org.chromium.chrome.browser.notifications.NotificationManagerProxyImpl;
+import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
+
+/**
+ * Manages notifications displayed while tracing and once tracing is complete.
+ */
+public class TracingNotificationManager {
+    private static final String TRACING_NOTIFICATION_TAG = "tracing_status";
+    private static final int TRACING_NOTIFICATION_ID = 100;
+
+    private static NotificationManagerProxy sNotificationManagerOverride;
+
+    // TODO(eseckler): Consider recording UMAs, see e.g. IncognitoNotificationManager.
+
+    private static NotificationManagerProxy getNotificationManager(Context context) {
+        return sNotificationManagerOverride != null ? sNotificationManagerOverride
+                                                    : new NotificationManagerProxyImpl(context);
+    }
+
+    /**
+     * Instruct the TracingNotificationManager to use a different NotificationManager during a test.
+     *
+     * @param notificationManager the manager to use instead.
+     */
+    @VisibleForTesting
+    public static void overrideNotificationManagerForTesting(
+            NotificationManagerProxy notificationManager) {
+        sNotificationManagerOverride = notificationManager;
+    }
+
+    /**
+     * @return whether notifications posted to the BROWSER notification channel are enabled by
+     * the user. True if the state can't be determined.
+     */
+    public static boolean browserNotificationsEnabled() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+            // Can't determine the state, so assume they are enabled.
+            return true;
+        }
+
+        if (!getNotificationManager(ContextUtils.getApplicationContext())
+                        .areNotificationsEnabled()) {
+            return false;
+        }
+
+        // On Android O and above, the BROWSER channel may have independently been disabled, too.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            return notificationChannelEnabled(ChannelDefinitions.ChannelId.BROWSER);
+        }
+
+        return true;
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private static boolean notificationChannelEnabled(String channelId) {
+        NotificationChannel channel = getNotificationManager(ContextUtils.getApplicationContext())
+                                              .getNotificationChannel(channelId);
+        // Can't determine the state if the channel doesn't exist, assume notifications are enabled.
+        if (channel == null) return true;
+        return channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
+    }
+
+    /**
+     * Replace the tracing notification with one indicating that a trace is being recorded.
+     */
+    public static void showTracingActiveNotification() {
+        Context context = ContextUtils.getApplicationContext();
+        String title = context.getResources().getString(R.string.tracing_active_notification_title);
+        // TODO(eseckler): Update the buffer usage in the notification periodically.
+        int bufferUsagePercentage = 0;
+        String message = context.getResources().getString(
+                R.string.tracing_active_notification_message, bufferUsagePercentage);
+
+        ChromeNotificationBuilder builder =
+                createNotificationBuilder()
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setOngoing(true)
+                        .addAction(R.drawable.ic_stop_white_36dp,
+                                ContextUtils.getApplicationContext().getResources().getString(
+                                        R.string.tracing_stop),
+                                TracingNotificationService.getStopRecordingIntent(context));
+        showNotification(builder.build());
+    }
+
+    /**
+     * Replace the tracing notification with one indicating that a trace is being finalized.
+     */
+    public static void showTracingStoppingNotification() {
+        Context context = ContextUtils.getApplicationContext();
+        String title =
+                context.getResources().getString(R.string.tracing_stopping_notification_title);
+        String message =
+                context.getResources().getString(R.string.tracing_stopping_notification_message);
+
+        ChromeNotificationBuilder builder = createNotificationBuilder()
+                                                    .setContentTitle(title)
+                                                    .setContentText(message)
+                                                    .setOngoing(true);
+        showNotification(builder.build());
+    }
+
+    /**
+     * Replace the tracing notification with one indicating that a trace was recorded successfully.
+     */
+    public static void showTracingCompleteNotification() {
+        Context context = ContextUtils.getApplicationContext();
+        String title =
+                context.getResources().getString(R.string.tracing_complete_notification_title);
+        String message =
+                context.getResources().getString(R.string.tracing_complete_notification_message);
+
+        // TODO(eseckler): Show download / share trace buttons in this notification.
+        ChromeNotificationBuilder builder =
+                createNotificationBuilder()
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setOngoing(false)
+                        .setDeleteIntent(TracingNotificationService.getDiscardTraceIntent(context));
+        showNotification(builder.build());
+    }
+
+    /**
+     * Dismiss any active tracing notification if there is one.
+     */
+    public static void dismissNotification() {
+        NotificationManagerProxy manager =
+                getNotificationManager(ContextUtils.getApplicationContext());
+        manager.cancel(TRACING_NOTIFICATION_TAG, TRACING_NOTIFICATION_ID);
+    }
+
+    private static ChromeNotificationBuilder createNotificationBuilder() {
+        return NotificationBuilderFactory
+                .createChromeNotificationBuilder(
+                        true /* preferCompat */, ChannelDefinitions.ChannelId.BROWSER)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setSmallIcon(R.drawable.ic_chrome)
+                .setShowWhen(false)
+                .setLocalOnly(true);
+    }
+
+    private static void showNotification(Notification notification) {
+        NotificationManagerProxy manager =
+                getNotificationManager(ContextUtils.getApplicationContext());
+        manager.notify(TRACING_NOTIFICATION_TAG, TRACING_NOTIFICATION_ID, notification);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationService.java
new file mode 100644
index 0000000..92a662a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationService.java
@@ -0,0 +1,68 @@
+// 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.tracing;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+import org.chromium.base.ThreadUtils;
+
+/**
+ * Service that handles the actions on tracing notifications.
+ */
+public class TracingNotificationService extends IntentService {
+    private static final String TAG = "tracing_notification";
+
+    private static final String ACTION_STOP_RECORDING =
+            "com.google.android.apps.chrome.tracing.STOP_RECORDING";
+
+    private static final String ACTION_DISCARD_TRACE =
+            "com.google.android.apps.chrome.tracing.DISCARD_TRACE";
+
+    /**
+     * Get the intent to send to stop a trace recording.
+     *
+     * @param context the application's context.
+     * @return the intent.
+     */
+    public static PendingIntent getStopRecordingIntent(Context context) {
+        Intent intent = new Intent(context, TracingNotificationService.class);
+        intent.setAction(ACTION_STOP_RECORDING);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    /**
+     * Get the intent to discard a recorded trace.
+     *
+     * @param context the application's context.
+     * @return the intent.
+     */
+    public static PendingIntent getDiscardTraceIntent(Context context) {
+        Intent intent = new Intent(context, TracingNotificationService.class);
+        intent.setAction(ACTION_DISCARD_TRACE);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    /**
+     * Construct the service. Called by Android.
+     */
+    public TracingNotificationService() {
+        super(TAG);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (ACTION_STOP_RECORDING.equals(intent.getAction())) {
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> { TracingController.getInstance().stopRecording(); });
+        } else {
+            assert ACTION_DISCARD_TRACE.equals(intent.getAction());
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> { TracingController.getInstance().discardTrace(); });
+        }
+    }
+}
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index f24d77e2f..ab3bad15 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1407,6 +1407,52 @@
         Developer options
       </message>
 
+      <!-- Tracing preferences (not translated, since part of developer preferences) -->
+      <message name="IDS_PREFS_TRACING" translateable="false" desc="Title for the Tracing preferences page in Chrome settings, which allows configuring and recording a Chrome performance trace. [CHAR-LIMIT=32]">
+        Tracing
+      </message>
+      <message name="IDS_PREFS_TRACING_PRIVACY_NOTICE" translateable="false" desc="The summary of a text preference that highlights that Chrome traces may contain privacy sensitive content.">
+        Traces may contain user or site data related to the active browsing session, including incognito tabs.
+      </message>
+      <message name="IDS_PREFS_TRACING_START" translateable="false" desc="Caption of a button that starts recording a trace.">
+        Record trace
+      </message>
+      <message name="IDS_PREFS_TRACING_ACTIVE" translateable="false" desc="The title of a preference shown when a Chrome trace is being recorded.">
+        Recording…
+      </message>
+      <message name="IDS_PREFS_TRACING_ACTIVE_SUMMARY" translateable="false" desc="The summary of a text preference shown when a Chrome trace is being recorded.">
+        A trace is being recorded. Use the notification to stop and share the result.
+      </message>
+
+      <!-- Tracing notifications (not translated, since part of developer preferences) -->
+      <message name="IDS_TRACING_NOTIFICATIONS_DISABLED" translateable="false" desc="Message of a toast shown when tracing could not be started because the Chrome app's notifications are disabled.">
+        Please enable Chrome browser notifications to record a trace.
+      </message>
+      <message name="IDS_TRACING_ACTIVE_NOTIFICATION_TITLE" translateable="false" desc="Title of a notification that indicates that a Chrome trace is being recorded.">
+        Chrome trace is being recorded
+      </message>
+      <message name="IDS_TRACING_ACTIVE_NOTIFICATION_MESSAGE" translateable="false" desc="Body of a notification that indicates that a Chrome trace is being recorded, showing the percentage of the trace buffer that has been filled with data.">
+        Trace buffer usage: <ph name="BUFFER_USAGE_PERCENTAGE">%1$d<ex>50</ex></ph>%%
+      </message>
+      <message name="IDS_TRACING_STOPPING_NOTIFICATION_TITLE" translateable="false" desc="Title of a notification that indicates that a Chrome trace is being stopped.">
+        Chrome trace is stopping
+      </message>
+      <message name="IDS_TRACING_STOPPING_NOTIFICATION_MESSAGE" translateable="false" desc="Body of a notification that indicates that a Chrome trace is being stopped.">
+        Trace data is being collected and compressed.
+      </message>
+      <message name="IDS_TRACING_COMPLETE_NOTIFICATION_TITLE" translateable="false" desc="Title of a notification that indicates that a Chrome trace was completed.">
+        Chrome trace is complete
+      </message>
+      <message name="IDS_TRACING_COMPLETE_NOTIFICATION_MESSAGE" translateable="false" desc="Body of a notification that indicates that a Chrome trace was completed and is ready to be downloaded or shared.">
+        The trace is ready to share.
+      </message>
+      <message name="IDS_TRACING_STOP" translateable="false" desc="Caption of a button that stops recording a trace.">
+        Stop recording
+      </message>
+      <message name="IDS_TRACING_ERROR_TOAST" translateable="false" desc="Message of a toast shown when there was an error during tracing.">
+        Error occurred while recording Chrome trace, see log for details.
+      </message>
+
       <!-- About Chrome preferences -->
       <message name="IDS_PREFS_ABOUT_CHROME" desc="Title for the About Chrome page. [CHAR-LIMIT=32]">
         About Chrome
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index cd54845..4eb1cbc9 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -120,12 +120,14 @@
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequest.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantStudy.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java",
-  "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantStudy.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/InitScreenController.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/ui/BottomBarAnimations.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestBottomBar.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/ui/PaymentRequestUI.java",
@@ -1297,6 +1299,7 @@
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java",
   "java/src/org/chromium/chrome/browser/preferences/developer/DeveloperPreferences.java",
+  "java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/download/DownloadDirectoryAdapter.java",
   "java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreferenceAdapter.java",
@@ -1603,6 +1606,9 @@
   "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java",
   "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java",
   "java/src/org/chromium/chrome/browser/toolbar/top/ViewShiftingActionBarDelegate.java",
+  "java/src/org/chromium/chrome/browser/tracing/TracingController.java",
+  "java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java",
+  "java/src/org/chromium/chrome/browser/tracing/TracingNotificationService.java",
   "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java",
   "java/src/org/chromium/chrome/browser/upgrade/PackageReplacedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/upgrade/UpgradeActivity.java",
@@ -2135,6 +2141,7 @@
   "javatests/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreferenceTest.java",
+  "javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataBridgeTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
index d2672de..b29c205 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
@@ -67,7 +67,7 @@
     @Before
     public void setUp() throws Throwable {
         mRule.startMainActivityOnBlankPage();
-        mRule.runOnUiThread((Runnable) () -> mParser.createNative());
+        mRule.runOnUiThread((Runnable) () -> mParser.createNative(mRule.getWebContents()));
         mWebAppManifestUris = null;
         mSupportedOrigins = null;
         mAllOriginsSupported = false;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java
new file mode 100644
index 0000000..d1028e1
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java
@@ -0,0 +1,266 @@
+// 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.preferences.developer;
+
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.preference.PreferenceFragment;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.v4.app.NotificationCompat;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.ButtonPreference;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.preferences.PreferencesTest;
+import org.chromium.chrome.browser.preferences.TextMessagePreference;
+import org.chromium.chrome.browser.tracing.TracingController;
+import org.chromium.chrome.browser.tracing.TracingNotificationManager;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.notifications.MockNotificationManagerProxy;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for the Tracing settings menu.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class TracingPreferencesTest {
+    @Rule
+    public final ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private MockNotificationManagerProxy mMockNotificationManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockNotificationManager = new MockNotificationManagerProxy();
+        TracingNotificationManager.overrideNotificationManagerForTesting(mMockNotificationManager);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        TracingNotificationManager.overrideNotificationManagerForTesting(null);
+    }
+
+    /**
+     * Waits until a notification has been displayed and then returns a NotificationEntry object to
+     * the caller. Requires that only a single notification is displayed.
+     *
+     * @return The NotificationEntry object tracked by the MockNotificationManagerProxy.
+     */
+    private MockNotificationManagerProxy.NotificationEntry waitForNotification() {
+        waitForNotificationManagerMutation();
+        List<MockNotificationManagerProxy.NotificationEntry> notifications =
+                mMockNotificationManager.getNotifications();
+        Assert.assertEquals(1, notifications.size());
+        return notifications.get(0);
+    }
+
+    /**
+     * Waits for a mutation to occur in the mocked notification manager. This indicates that Chrome
+     * called into Android to notify or cancel a notification.
+     */
+    private void waitForNotificationManagerMutation() {
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mMockNotificationManager.getMutationCountAndDecrement() > 0;
+            }
+        }, scaleTimeout(15000) /* maxTimeoutMs */, 50 /* checkIntervalMs */);
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Preferences"})
+    @Features.EnableFeatures(ChromeFeatureList.DEVELOPER_PREFERENCES)
+    @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/899894")
+    public void testRecordTrace() throws Exception {
+        Context context = ContextUtils.getApplicationContext();
+        mActivityTestRule.startMainActivityOnBlankPage();
+        Preferences activity =
+                mActivityTestRule.startPreferences(TracingPreferences.class.getName());
+        final PreferenceFragment fragment = (PreferenceFragment) activity.getFragmentForTest();
+        final ButtonPreference startTracingButton = (ButtonPreference) fragment.findPreference(
+                TracingPreferences.UI_PREF_START_RECORDING);
+
+        // Wait for TracingController to initialize and button to get enabled.
+        CallbackHelper callbackHelper = new CallbackHelper();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            if (TracingController.getInstance().getState()
+                    == TracingController.State.INITIALIZING) {
+                Assert.assertFalse(startTracingButton.isEnabled());
+                TracingController.getInstance().addObserver(new TracingController.Observer() {
+                    @Override
+                    public void onTracingStateChanged(@TracingController.State int state) {
+                        callbackHelper.notifyCalled();
+                        TracingController.getInstance().removeObserver(this);
+                    }
+                });
+                return;
+            }
+            // Already initialized.
+            callbackHelper.notifyCalled();
+        });
+        callbackHelper.waitForCallback(0 /* currentCallCount */);
+
+        // Setting to IDLE state tries to dismiss the (non-existent) notification.
+        waitForNotificationManagerMutation();
+        Assert.assertEquals(0, mMockNotificationManager.getNotifications().size());
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals(
+                    TracingController.State.IDLE, TracingController.getInstance().getState());
+            Assert.assertTrue(startTracingButton.isEnabled());
+            Assert.assertEquals(
+                    context.getString(R.string.prefs_tracing_start), startTracingButton.getTitle());
+
+            // Tap the button to start recording a trace.
+            PreferencesTest.clickPreference(fragment, startTracingButton);
+
+            Assert.assertEquals(
+                    TracingController.State.STARTING, TracingController.getInstance().getState());
+            Assert.assertFalse(startTracingButton.isEnabled());
+            Assert.assertEquals(context.getString(R.string.prefs_tracing_active),
+                    startTracingButton.getTitle());
+
+            // Observe state changes to RECORDING, STOPPING, STOPPED, and IDLE.
+            TracingController.getInstance().addObserver(new TracingController.Observer() {
+                @TracingController.State
+                int mExpectedState = TracingController.State.RECORDING;
+
+                @Override
+                public void onTracingStateChanged(@TracingController.State int state) {
+                    // onTracingStateChanged() should be called four times in total, with the right
+                    // order of state changes:
+                    Assert.assertEquals(mExpectedState, state);
+                    if (state == TracingController.State.RECORDING) {
+                        mExpectedState = TracingController.State.STOPPING;
+                    } else if (state == TracingController.State.STOPPING) {
+                        mExpectedState = TracingController.State.STOPPED;
+                    } else if (state == TracingController.State.STOPPED) {
+                        mExpectedState = TracingController.State.IDLE;
+                    } else {
+                        TracingController.getInstance().removeObserver(this);
+                    }
+
+                    callbackHelper.notifyCalled();
+                }
+            });
+        });
+
+        // Wait for state change to RECORDING.
+        callbackHelper.waitForCallback(1 /* currentCallCount */);
+
+        // Recording started, a notification with a stop button should be displayed.
+        Notification notification = waitForNotification().notification;
+        Assert.assertEquals(FLAG_ONGOING_EVENT, notification.flags & FLAG_ONGOING_EVENT);
+        Assert.assertEquals(null, notification.deleteIntent);
+        Assert.assertEquals(1, NotificationCompat.getActionCount(notification));
+        PendingIntent stopIntent = NotificationCompat.getAction(notification, 0).actionIntent;
+
+        // Initiate stopping the recording and wait for state changes to STOPPING and STOPPED.
+        stopIntent.send();
+        callbackHelper.waitForCallback(2 /* currentCallCount */, 2 /* numberOfCallsToWaitFor */,
+                scaleTimeout(15000) /* timeout */, TimeUnit.MILLISECONDS);
+
+        // Notification should be replaced twice, once with an "is stopping" notification and then
+        // with a notification to share the trace. Because the former is temporary, we can't
+        // verify it reliably, so we skip over it and simply expect two notification mutations.
+        waitForNotification();
+        notification = waitForNotification().notification;
+        Assert.assertEquals(0, notification.flags & FLAG_ONGOING_EVENT);
+        Assert.assertNotEquals(null, notification.deleteIntent);
+        Assert.assertEquals(0, NotificationCompat.getActionCount(notification));
+        PendingIntent deleteIntent = notification.deleteIntent;
+
+        // The temporary tracing output file should now exist.
+        File tempFile = TracingController.getInstance().getTracingTempFile();
+        Assert.assertNotEquals(null, tempFile);
+        Assert.assertTrue(tempFile.exists());
+
+        // Discard the trace and wait for state change back to IDLE.
+        deleteIntent.send();
+        callbackHelper.waitForCallback(4 /* currentCallCount */);
+
+        // The temporary file should be deleted.
+        Assert.assertFalse(tempFile.exists());
+
+        // Notification should be deleted, too.
+        waitForNotificationManagerMutation();
+        Assert.assertEquals(0, mMockNotificationManager.getNotifications().size());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Features.EnableFeatures(ChromeFeatureList.DEVELOPER_PREFERENCES)
+    public void testNotificationsDisabledMessage() throws Exception {
+        mMockNotificationManager.setNotificationsEnabled(false);
+
+        Context context = ContextUtils.getApplicationContext();
+        Preferences activity =
+                mActivityTestRule.startPreferences(TracingPreferences.class.getName());
+        final PreferenceFragment fragment = (PreferenceFragment) activity.getFragmentForTest();
+        final ButtonPreference startTracingButton = (ButtonPreference) fragment.findPreference(
+                TracingPreferences.UI_PREF_START_RECORDING);
+        final TextMessagePreference statusPreference =
+                (TextMessagePreference) fragment.findPreference(
+                        TracingPreferences.UI_PREF_TRACING_STATUS);
+
+        // Wait for TracingController to initialize.
+        CallbackHelper callbackHelper = new CallbackHelper();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            if (TracingController.getInstance().getState()
+                    == TracingController.State.INITIALIZING) {
+                Assert.assertFalse(startTracingButton.isEnabled());
+                TracingController.getInstance().addObserver(new TracingController.Observer() {
+                    @Override
+                    public void onTracingStateChanged(@TracingController.State int state) {
+                        callbackHelper.notifyCalled();
+                        TracingController.getInstance().removeObserver(this);
+                    }
+                });
+                return;
+            }
+            callbackHelper.notifyCalled();
+        });
+        callbackHelper.waitForCallback(0 /* currentCallCount */);
+
+        Assert.assertFalse(startTracingButton.isEnabled());
+        Assert.assertEquals(context.getString(R.string.tracing_notifications_disabled),
+                statusPreference.getTitle());
+
+        mMockNotificationManager.setNotificationsEnabled(true);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
index a185ea48..638901c4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
@@ -324,7 +324,7 @@
             }
 
             @Override
-            public void createNative() {}
+            public void createNative(WebContents webContents) {}
 
             @Override
             public void destroyNative() {}
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index 881d107d..f8731f3 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/components/quick_launch/public",
   "+chrome/browser",
   "+chrome/child",
   "+chrome/chrome_watcher",
@@ -28,10 +29,10 @@
   "+content/public/app",
   "+content/public/browser/browser_main_runner.h",
   "+extensions/common/constants.h",
-  "+headless/public",  # For headless mode.
+  "+headless/public",
   "+mash/common",
-  "+ash/components/quick_launch/public",
   "+native_client/src/trusted/service_runtime/osx",
+  "+pdf/pdf_ppapi.h",
   "+remoting/client/plugin",
   "+sandbox",
   "+services/ws/public",
diff --git a/chrome/app/md_extensions_strings.grdp b/chrome/app/md_extensions_strings.grdp
index 220e568..55f3b335 100644
--- a/chrome/app/md_extensions_strings.grdp
+++ b/chrome/app/md_extensions_strings.grdp
@@ -7,7 +7,7 @@
   <message name="IDS_EXTENSIONS_ALLOW_ON_ALL_URLS" desc="The checkbox for allowing an extension to run scripts on all websites without explicit permission.">
     Allow on all websites
   </message>
-  <message name="IDS_EXTENSIONS_VIEW_ACTIVITY_LOG" desc="The label of the button to click to view recent extension activity.">
+  <message name="IDS_EXTENSIONS_ACTIVITY_LOG" desc="The label of the button to click to view recent extension activity.">
     View Activity Log
   </message>
   <message name="IDS_EXTENSIONS_BACKGROUND_PAGE" desc="Display name for an autogenerated background page.">
@@ -121,9 +121,6 @@
   <message name="IDS_MD_EXTENSIONS_ACCESSIBILITY_ERROR_MULTI_LINE" desc="Accessibility message to indicate the lines that an error spans. The lines of code will be read to the user. The place holders will be numbers.">
     Error from line <ph name="ERROR_LINE_START">$1</ph> to <ph name="ERROR_LINE_END">$2</ph>
   </message>
-  <message name="IDS_MD_EXTENSIONS_ACTIVITY_LOG_PAGE_HEADING" desc="The heading of the page displaying an extension's activity log.">
-    Activity Log
-  </message>
   <message name="IDS_MD_EXTENSIONS_ITEM_ID" desc="The text for the label next to the extension id.">
     &lt;span&gt;ID: &lt;/span&gt;<ph name="EXTENSION_ID">$1<ex>cfhdojbkjhnklbpkdaibdccddilifddb</ex></ph>
   </message>
@@ -226,9 +223,6 @@
   <message name="IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text on the button to retry loading an unpacked extension after a failed load.">
     Retry
   </message>
-  <message name="IDS_MD_EXTENSIONS_NO_ACTIVITIES" desc="The message shown to the user when an extension has no recent activities.">
-    No recent activities
-  </message>
   <message name="IDS_MD_EXTENSIONS_NO_INSTALLED_ITEMS" desc="The message shown to the user on the Extensions settings page when there are no extensions or apps installed.">
     Find extensions and themes in the <ph name="BEGIN_LINK">&lt;a target="_blank" href="https://chrome.google.com/webstore/category/extensions"&gt;</ph>Chrome Web Store<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2458d0c0..38158e06 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2561,6 +2561,7 @@
       "//rlz:rlz_utils",
       "//sandbox",
       "//sandbox:sandbox_buildflags",
+      "//services/identity/public/cpp",
       "//services/proxy_resolver:lib",
       "//third_party/android_opengl/etc1",
       "//third_party/android_tools:cpu_features",
@@ -4294,6 +4295,7 @@
     deps += [
       "//components/pdf/browser",
       "//media:media_buildflags",
+      "//pdf:features",
       "//ppapi/buildflags",
       "//ppapi/proxy:ipc",
       "//services/device/public/mojom",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0d71ab4b..4360f405 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -177,6 +177,10 @@
 #include "extensions/common/switches.h"
 #endif  // ENABLE_EXTENSIONS
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "pdf/pdf_features.h"
+#endif
+
 #if defined(USE_OZONE)
 #include "ui/ozone/public/ozone_switches.h"
 #endif  // USE_OZONE
@@ -3846,9 +3850,15 @@
      flag_descriptions::kVizHitTestDrawQuadDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kEnableVizHitTestDrawQuad)},
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+    {"pdf-form-save", flag_descriptions::kPdfFormSaveName,
+     flag_descriptions::kPdfFormSaveDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(chrome_pdf::features::kSaveEditedPDFForm)},
+
     {"pdf-isolation", flag_descriptions::kPdfIsolationName,
      flag_descriptions::kPdfIsolationDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kPdfIsolation)},
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 #if BUILDFLAG(ENABLE_PRINTING)
     {"use-pdf-compositor-service-for-print",
@@ -4683,10 +4693,10 @@
 }
 
 void RecordUMAStatistics(flags_ui::FlagsStorage* flags_storage) {
-  const std::set<std::string> switches =
-      FlagsStateSingleton::GetFlagsState()->GetSwitchesFromFlags(flags_storage);
-  const std::set<std::string> features =
-      FlagsStateSingleton::GetFlagsState()->GetFeaturesFromFlags(flags_storage);
+  std::set<std::string> switches;
+  std::set<std::string> features;
+  FlagsStateSingleton::GetFlagsState()->GetSwitchesAndFeaturesFromFlags(
+      flags_storage, &switches, &features);
   ReportAboutFlagsHistogram("Launch.FlagsAtStartup", switches, features);
 }
 
diff --git a/chrome/browser/about_flags_unittest.cc b/chrome/browser/about_flags_unittest.cc
index 0e9da08e..0375d730 100644
--- a/chrome/browser/about_flags_unittest.cc
+++ b/chrome/browser/about_flags_unittest.cc
@@ -10,9 +10,13 @@
 #include <set>
 #include <string>
 
+#include "base/base_paths.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/format_macros.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_enum_reader.h"
 #include "base/values.h"
@@ -64,6 +68,39 @@
   return result;
 }
 
+struct FlagMetadataEntry {
+  std::vector<std::string> owners;
+  int expire_milestone;
+};
+
+using FlagMetadataMap = std::map<std::string, FlagMetadataEntry>;
+
+FlagMetadataMap LoadFlagMetadata() {
+  FlagMetadataMap metadata;
+  base::FilePath metadata_path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &metadata_path);
+  JSONFileValueDeserializer deserializer(
+      metadata_path.AppendASCII("chrome").AppendASCII("browser").AppendASCII(
+          "flag-metadata.json"));
+  int error_code;
+  std::string error_message;
+  std::unique_ptr<base::Value> metadata_json =
+      deserializer.Deserialize(&error_code, &error_message);
+  DCHECK(metadata_json) << "Failed to load flag metadata: " << error_code << " "
+                        << error_message;
+
+  for (const auto& entry : metadata_json->GetList()) {
+    std::string name = entry.FindKey("name")->GetString();
+    std::vector<std::string> owners;
+    for (const auto& owner : entry.FindKey("owners")->GetList())
+      owners.push_back(owner.GetString());
+    int expire_milestone = entry.FindKey("expire_milestone")->GetInt();
+    metadata[name] = FlagMetadataEntry{owners, expire_milestone};
+  }
+
+  return metadata;
+}
+
 }  // anonymous namespace
 
 // Makes sure there are no separators in any of the entry names.
@@ -77,6 +114,24 @@
   }
 }
 
+// Makes sure that every flag has an owner and an expiry entry in
+// flag-metadata.json.
+TEST(AboutFlagsTest, DISABLED_EveryFlagHasMetadata) {
+  size_t count;
+  const flags_ui::FeatureEntry* entries = testing::GetFeatureEntries(&count);
+  FlagMetadataMap metadata = LoadFlagMetadata();
+
+  std::vector<std::string> missing_flags;
+
+  for (size_t i = 0; i < count; ++i) {
+    if (metadata.count(entries[i].internal_name) == 0)
+      missing_flags.push_back(entries[i].internal_name);
+  }
+
+  EXPECT_EQ(0u, missing_flags.size())
+      << "Missing flags: " << base::JoinString(missing_flags, "\n  ");
+}
+
 class AboutFlagsHistogramTest : public ::testing::Test {
  protected:
   // This is a helper function to check that all IDs in enum LoginCustomFlags in
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index d7ef478..1e7c6ba 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -67,7 +67,9 @@
     jobject jcaller,
     const JavaParamRef<jobject>& webContents,
     const JavaParamRef<jobjectArray>& parameterNames,
-    const JavaParamRef<jobjectArray>& parameterValues)
+    const JavaParamRef<jobjectArray>& parameterValues,
+    const JavaParamRef<jstring>& jlocale,
+    const JavaParamRef<jstring>& jcountryCode)
     : ui_delegate_(nullptr) {
   java_autofill_assistant_ui_controller_.Reset(env, jcaller);
 
@@ -75,9 +77,16 @@
       content::WebContents::FromJavaWebContents(webContents);
   DCHECK(web_contents);
   browser_context_ = web_contents->GetBrowserContext();
+
+  auto locale = base::android::ConvertJavaStringToUTF8(jlocale);
+  std::string country_code;
+  if (jcountryCode)
+    country_code = base::android::ConvertJavaStringToUTF8(jcountryCode);
+
   Controller::CreateForWebContents(
       web_contents, base::WrapUnique(this),
-      BuildParametersFromJava(env, parameterNames, parameterValues));
+      BuildParametersFromJava(env, parameterNames, parameterValues), locale,
+      country_code);
   DCHECK(ui_delegate_);
 }
 
@@ -395,20 +404,17 @@
   ui_delegate_->OnDestroy();
 }
 
-void UiControllerAndroid::GiveUp(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
-  ui_delegate_->OnGiveUp();
-}
-
 static jlong JNI_AutofillAssistantUiController_Init(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jobject>& webContents,
     const JavaParamRef<jobjectArray>& parameterNames,
-    const JavaParamRef<jobjectArray>& parameterValues) {
+    const JavaParamRef<jobjectArray>& parameterValues,
+    const JavaParamRef<jstring>& jlocale,
+    const JavaParamRef<jstring>& jcountryCode) {
   auto* ui_controller_android = new autofill_assistant::UiControllerAndroid(
-      env, jcaller, webContents, parameterNames, parameterValues);
+      env, jcaller, webContents, parameterNames, parameterValues, jlocale,
+      jcountryCode);
   return reinterpret_cast<intptr_t>(ui_controller_android);
 }
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index c719d6f..9f0108d2 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -30,7 +30,9 @@
       jobject jcaller,
       const base::android::JavaParamRef<jobject>& webContents,
       const base::android::JavaParamRef<jobjectArray>& parameterNames,
-      const base::android::JavaParamRef<jobjectArray>& parameterValues);
+      const base::android::JavaParamRef<jobjectArray>& parameterValues,
+      const base::android::JavaParamRef<jstring>& locale,
+      const base::android::JavaParamRef<jstring>& countryCode);
   ~UiControllerAndroid() override;
 
   // Overrides UiController:
@@ -75,7 +77,6 @@
              const base::android::JavaParamRef<jobject>& jcaller,
              const base::android::JavaParamRef<jstring>& initialUrlString);
   void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
-  void GiveUp(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void OnScriptSelected(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc
index 573fb7e..35df6eb 100644
--- a/chrome/browser/android/signin/signin_manager_android.cc
+++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/policy/cloud/user_policy_signin_service_mobile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/oauth2_token_service_delegate_android.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
@@ -40,7 +41,6 @@
 #include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "content/public/browser/browsing_data_filter_builder.h"
@@ -141,7 +141,7 @@
   java_signin_manager_.Reset(env, obj);
   profile_ = ProfileManager::GetActiveUserProfile();
   DCHECK(profile_);
-  SigninManagerFactory::GetForProfile(profile_)->AddObserver(this);
+  IdentityManagerFactory::GetForProfile(profile_)->AddObserver(this);
   pref_change_registrar_.Init(profile_->GetPrefs());
   pref_change_registrar_.Add(
       prefs::kSigninAllowed,
@@ -149,7 +149,9 @@
                  base::Unretained(this)));
 }
 
-SigninManagerAndroid::~SigninManagerAndroid() {}
+SigninManagerAndroid::~SigninManagerAndroid() {
+  IdentityManagerFactory::GetForProfile(profile_)->RemoveObserver(this);
+}
 
 void SigninManagerAndroid::CheckPolicyBeforeSignIn(
     JNIEnv* env,
@@ -207,6 +209,9 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& username) {
   DVLOG(1) << "SigninManagerAndroid::OnSignInCompleted";
+
+  // TODO(crbug.com/889902): Migrate to IdentityManager once there's an
+  // API mapping for SigninManager::OnExternalSigninCompleted().
   SigninManagerFactory::GetForProfile(profile_)->OnExternalSigninCompleted(
       base::android::ConvertJavaStringToUTF8(env, username));
 }
@@ -214,7 +219,8 @@
 void SigninManagerAndroid::SignOut(JNIEnv* env,
                                    const JavaParamRef<jobject>& obj,
                                    jint signoutReason) {
-  SigninManagerFactory::GetForProfile(profile_)->SignOut(
+  IdentityManagerFactory::GetForProfile(profile_)->ClearPrimaryAccount(
+      identity::IdentityManager::ClearAccountTokensAction::kDefault,
       static_cast<signin_metrics::ProfileSignout>(signoutReason),
       // Always use IGNORE_METRIC for the profile deletion argument. Chrome
       // Android has just a single-profile which is never deleted upon
@@ -294,13 +300,13 @@
 
 void SigninManagerAndroid::LogInSignedInUser(JNIEnv* env,
                                              const JavaParamRef<jobject>& obj) {
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(profile_);
+  identity::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile_);
   // With the account consistency enabled let the account Reconcilor handles
   // everything.
   ProfileOAuth2TokenService* token_service =
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
-  const std::string& primary_acct = signin_manager->GetAuthenticatedAccountId();
+  const std::string& primary_acct = identity_manager->GetPrimaryAccountId();
 
   static_cast<OAuth2TokenServiceDelegateAndroid*>(token_service->GetDelegate())
       ->ValidateAccounts(primary_acct, true);
@@ -309,7 +315,7 @@
 jboolean SigninManagerAndroid::IsSigninAllowedByPolicy(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  return SigninManagerFactory::GetForProfile(profile_)->IsSigninAllowed();
+  return profile_->GetPrefs()->GetBoolean(prefs::kSigninAllowed);
 }
 
 jboolean SigninManagerAndroid::IsForceSigninEnabled(
@@ -323,17 +329,11 @@
 jboolean SigninManagerAndroid::IsSignedInOnNative(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  return SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated();
+  return IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount();
 }
 
-void SigninManagerAndroid::GoogleSigninFailed(
-    const GoogleServiceAuthError& error) {}
-
-void SigninManagerAndroid::GoogleSigninSucceeded(const std::string& account_id,
-                                                 const std::string& username) {}
-
-void SigninManagerAndroid::GoogleSignedOut(const std::string& account_id,
-                                           const std::string& username) {
+void SigninManagerAndroid::OnPrimaryAccountCleared(
+    const AccountInfo& previous_primary_account_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
   Java_SigninManager_onNativeSignOut(base::android::AttachCurrentThread(),
                                      java_signin_manager_);
@@ -342,7 +342,7 @@
 void SigninManagerAndroid::OnSigninAllowedPrefChanged() {
   Java_SigninManager_onSigninAllowedByPolicyChanged(
       base::android::AttachCurrentThread(), java_signin_manager_,
-      SigninManagerFactory::GetForProfile(profile_)->IsSigninAllowed());
+      profile_->GetPrefs()->GetBoolean(prefs::kSigninAllowed));
 }
 
 // static
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h
index 4d711455..5512aee 100644
--- a/chrome/browser/android/signin/signin_manager_android.h
+++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -13,19 +13,19 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "components/signin/core/browser/signin_manager_base.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 class Profile;
 
-// Android wrapper of the SigninManager which provides access from the Java
-// layer. Note that on Android, there's only a single profile, and therefore
-// a single instance of this wrapper. The name of the Java class is
-// SigninManager.
-// This class should only be accessed from the UI thread.
+// Android wrapper of Chrome's C++ identity management code which provides
+// access from the Java layer. Note that on Android, there's only a single
+// profile, and therefore a single instance of this wrapper. The name of the
+// Java class is SigninManager. This class should only be accessed from the UI
+// thread.
 //
 // This class implements parts of the sign-in flow, to make sure that policy
 // is available before sign-in completes.
-class SigninManagerAndroid : public SigninManagerBase::Observer {
+class SigninManagerAndroid : public identity::IdentityManager::Observer {
  public:
   SigninManagerAndroid(JNIEnv* env, jobject obj);
 
@@ -80,12 +80,9 @@
   jboolean IsSignedInOnNative(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj);
 
-  // SigninManagerBase::Observer implementation.
-  void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
-  void GoogleSigninSucceeded(const std::string& account_id,
-                             const std::string& username) override;
-  void GoogleSignedOut(const std::string& account_id,
-                       const std::string& username) override;
+  // identity::IdentityManager::Observer implementation.
+  void OnPrimaryAccountCleared(
+      const AccountInfo& previous_primary_account_info) override;
 
  private:
   friend class SigninManagerAndroidTest;
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
index 8218c58..6fae86a 100644
--- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc
+++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -612,7 +612,7 @@
       // that was inserted after Submit may not be complete yet when the next
       // Submit finishes.
       browser_gpu_trace =
-          std::make_unique<ScopedGpuTrace>("gpu", "Vr.PostSubmitDrawOnGpu");
+          std::make_unique<ScopedGpuTrace>("Vr.PostSubmitDrawOnGpu");
     }
     graphics_->SubmitToGvr(head_pose);
 
diff --git a/chrome/browser/android/vr/scoped_gpu_trace.cc b/chrome/browser/android/vr/scoped_gpu_trace.cc
index 653e8d29..adb6e0c 100644
--- a/chrome/browser/android/vr/scoped_gpu_trace.cc
+++ b/chrome/browser/android/vr/scoped_gpu_trace.cc
@@ -10,12 +10,15 @@
 
 namespace vr {
 
+namespace {
+constexpr const char kGpuTracingCategory[] = "gpu";
+}  // namespace
+
 uint32_t ScopedGpuTrace::s_trace_id_ = 0;
 
-ScopedGpuTrace::ScopedGpuTrace(const char* categoray, const char* name)
+ScopedGpuTrace::ScopedGpuTrace(const char* name)
     : start_time_(base::TimeTicks::Now()),
       fence_(gl::GLFenceAndroidNativeFenceSync::CreateForGpuFence()),
-      categoray_(categoray),
       name_(name),
       trace_id_(s_trace_id_++) {}
 
@@ -30,11 +33,11 @@
     return;
 
   TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
-      categoray_, name_, trace_id_, base::PlatformThread::CurrentId(),
+      kGpuTracingCategory, name_, trace_id_, base::PlatformThread::CurrentId(),
       start_time_);
-  TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(categoray_, name_, trace_id_,
-                                             base::PlatformThread::CurrentId(),
-                                             end_time);
+  TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
+      kGpuTracingCategory, name_, trace_id_, base::PlatformThread::CurrentId(),
+      end_time);
 }
 
 }  // namespace vr
diff --git a/chrome/browser/android/vr/scoped_gpu_trace.h b/chrome/browser/android/vr/scoped_gpu_trace.h
index c6aab2aa..6dcf239b 100644
--- a/chrome/browser/android/vr/scoped_gpu_trace.h
+++ b/chrome/browser/android/vr/scoped_gpu_trace.h
@@ -19,14 +19,15 @@
 
 // Helper class to trace GPU work.
 // It creates a fence in the constructor and extracts the time when the fence
-// completed in the destructor. The duration is reported to trace. It assumes
-// that fence has completed when destructor is triggered. If for some reason it
-// failed to extract fence completion time, no trace event will be recorded.
-// NB: This class is not thread safe due to static id generation. If you need to
-// use it on different threads, consider a thread safe id generator.
+// completed in the destructor. The duration is reported to trace in the "gpu"
+// category. It assumes that fence has completed when destructor is triggered.
+// If for some reason it failed to extract fence completion time, no trace event
+// will be recorded. NB: This class is not thread safe due to static id
+// generation. If you need to use it on different threads, consider a thread
+// safe id generator.
 class ScopedGpuTrace {
  public:
-  ScopedGpuTrace(const char* categoray, const char* name);
+  explicit ScopedGpuTrace(const char* name);
 
   virtual ~ScopedGpuTrace();
 
@@ -37,7 +38,6 @@
   static uint32_t s_trace_id_;
   base::TimeTicks start_time_;
   std::unique_ptr<gl::GLFenceAndroidNativeFenceSync> fence_;
-  const char* const categoray_;
   const char* const name_;
   uint32_t trace_id_;
 
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 97bdfd8..0aabc156 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -1358,9 +1358,13 @@
   if (focused) {
     return true;
   } else {
-    ADD_FAILURE() << "Failed to place focus on the element: " << element_xpath
-                  << "!";
-    return false;
+    // Failing focusing on an element through script, use the less preferred
+    // method of left mouse clicking the element.
+    int x, y;
+    if (!GetCenterCoordinateOfTargetElement(frame, element_xpath, x, y))
+      return false;
+
+    return SimulateLeftMouseClickAt(frame, gfx::Point(x, y));
   }
 }
 
diff --git a/chrome/browser/background_fetch/background_fetch_browsertest.cc b/chrome/browser/background_fetch/background_fetch_browsertest.cc
index acacb461..8b6e4a7c 100644
--- a/chrome/browser/background_fetch/background_fetch_browsertest.cc
+++ b/chrome/browser/background_fetch/background_fetch_browsertest.cc
@@ -256,12 +256,11 @@
 
     SetUpBrowser(browser());
 
-    BackgroundFetchDelegateImpl* delegate =
-        static_cast<BackgroundFetchDelegateImpl*>(
-            active_browser_->profile()->GetBackgroundFetchDelegate());
-    DCHECK(delegate);
+    delegate_ = static_cast<BackgroundFetchDelegateImpl*>(
+        active_browser_->profile()->GetBackgroundFetchDelegate());
+    DCHECK(delegate_);
 
-    offline_content_provider_observer_->set_delegate(delegate);
+    offline_content_provider_observer_->set_delegate(delegate_);
   }
 
   void SetUpBrowser(Browser* browser) {
@@ -323,11 +322,8 @@
       const ContentId& offline_item_id,
       std::unique_ptr<OfflineItemVisuals>* out_visuals) {
     base::RunLoop run_loop;
-    BackgroundFetchDelegateImpl* delegate =
-        static_cast<BackgroundFetchDelegateImpl*>(
-            active_browser_->profile()->GetBackgroundFetchDelegate());
-    DCHECK(delegate);
-    delegate->GetVisualsForItem(
+
+    delegate_->GetVisualsForItem(
         offline_item_id, base::Bind(&BackgroundFetchBrowserTest::DidGetVisuals,
                                     base::Unretained(this),
                                     run_loop.QuitClosure(), out_visuals));
@@ -427,7 +423,8 @@
   }
 
  protected:
-  download::DownloadService* download_service_{nullptr};
+  BackgroundFetchDelegateImpl* delegate_ = nullptr;
+  download::DownloadService* download_service_ = nullptr;
 
   std::unique_ptr<WaitableDownloadLoggerObserver> download_observer_;
   std::unique_ptr<OfflineContentProviderObserver>
@@ -685,6 +682,9 @@
   EXPECT_TRUE(
       base::StartsWith(offline_content_provider_observer_->latest_item().title,
                        "New Fetched Title!", base::CompareCase::SENSITIVE));
+
+  // Make sure the delegate cleans up after the fetch is complete.
+  EXPECT_TRUE(delegate_->job_details_map_.empty());
 }
 
 IN_PROC_BROWSER_TEST_F(BackgroundFetchBrowserTest,
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
index aff81ad3..368b8c6 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
@@ -314,7 +314,11 @@
     download_job_unique_id_map_.erase(download_guid);
   }
   UpdateOfflineItemAndUpdateObservers(&job_details);
-  job_details_map_.erase(job_details_iter);
+}
+
+void BackgroundFetchDelegateImpl::MarkJobComplete(
+    const std::string& job_unique_id) {
+  job_details_map_.erase(job_unique_id);
 }
 
 void BackgroundFetchDelegateImpl::UpdateUI(
@@ -340,10 +344,6 @@
   }
 
   UpdateOfflineItemAndUpdateObservers(&job_details);
-
-  // UpdateUI() can only be called once, and only when the background fetch
-  // has succeeded or failed, so we can delete |job_details| now.
-  job_details_map_.erase(job_details_iter);
 }
 
 void BackgroundFetchDelegateImpl::OnDownloadStarted(
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.h b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
index ab79b32..e355a1f8 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.h
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
@@ -65,6 +65,7 @@
                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
                    const net::HttpRequestHeaders& headers) override;
   void Abort(const std::string& job_unique_id) override;
+  void MarkJobComplete(const std::string& job_unique_id) override;
   void UpdateUI(const std::string& job_unique_id,
                 const base::Optional<std::string>& title,
                 const base::Optional<SkBitmap>& icon) override;
@@ -122,6 +123,9 @@
   }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(BackgroundFetchBrowserTest,
+                           FetchesRunToCompletionAndUpdateTitle_Fetched);
+
   struct JobDetails {
     // If a job is part of the |job_details_map_|, it will have one of these
     // states.
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2839fc4..82544f4 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3129,9 +3129,12 @@
   web_prefs->data_saver_holdback_web_api_enabled =
       base::GetFieldTrialParamByFeatureAsBool(features::kDataSaverHoldback,
                                               "holdback_web", false);
+  // Media holdback is enabled by default. Enabling this by default decouples
+  // the behavior of the media stack (e.g., autoplay or preloading of
+  // metadata) from the state of the data saver.
   web_prefs->data_saver_holdback_media_api_enabled =
       base::GetFieldTrialParamByFeatureAsBool(features::kDataSaverHoldback,
-                                              "holdback_media", false);
+                                              "holdback_media", true);
 
   content::WebContents* contents =
       content::WebContents::FromRenderViewHost(rvh);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 535ed4d00..8482334 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -986,7 +986,8 @@
   callback.Run(result, virtual_path);
   if (!file_picker_callbacks_.empty()) {
     base::OnceClosure callback = std::move(file_picker_callbacks_.front());
-    std::move(callback).Run();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback)));
     file_picker_callbacks_.pop_front();
   } else {
     is_file_picker_showing_ = false;
@@ -999,17 +1000,25 @@
     const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback) {
   DownloadItem* download = download_manager_->GetDownloadByGuid(guid);
   if (download) {
-    DownloadFilePicker::ShowFilePicker(
-        download, suggested_path,
-        base::BindRepeating(
-            &ChromeDownloadManagerDelegate::OnConfirmationCallbackComplete,
-            weak_ptr_factory_.GetWeakPtr(), callback));
+    ShowFilePickerForDownload(download, suggested_path, callback);
   } else {
     OnConfirmationCallbackComplete(
         callback, DownloadConfirmationResult::CANCELED, base::FilePath());
   }
 }
 
+void ChromeDownloadManagerDelegate::ShowFilePickerForDownload(
+    DownloadItem* download,
+    const base::FilePath& suggested_path,
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback) {
+  DCHECK(download);
+  DownloadFilePicker::ShowFilePicker(
+      download, suggested_path,
+      base::BindRepeating(
+          &ChromeDownloadManagerDelegate::OnConfirmationCallbackComplete,
+          weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
 #if defined(OS_ANDROID)
 void ChromeDownloadManagerDelegate::GenerateUniqueFileNameDone(
     gfx::NativeWindow native_window,
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index 7c6a757b..967206da 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -134,9 +134,9 @@
   virtual safe_browsing::DownloadProtectionService*
       GetDownloadProtectionService();
 
-  // Called to show a file picker
-  virtual void ShowFilePicker(
-      const std::string& guid,
+  // Show file picker for |download|.
+  virtual void ShowFilePickerForDownload(
+      download::DownloadItem* download,
       const base::FilePath& suggested_path,
       const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback);
 
@@ -186,6 +186,12 @@
 
   typedef std::vector<content::DownloadIdCallback> IdCallbackVector;
 
+  // Called to show a file picker for download with |guid|
+  void ShowFilePicker(
+      const std::string& guid,
+      const base::FilePath& suggested_path,
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback);
+
   // content::NotificationObserver implementation.
   void Observe(int type,
                const content::NotificationSource& source,
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 8531372..a7207be 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/guid.h"
 #include "base/location.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
@@ -213,6 +214,14 @@
     ChromeDownloadManagerDelegate::RequestConfirmation(download_item, path,
                                                        reason, callback);
   }
+
+  void ShowFilePickerForDownload(
+      DownloadItem* download,
+      const base::FilePath& path,
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback&) override {}
+
+ private:
+  friend class ChromeDownloadManagerDelegateTest;
 };
 
 class ChromeDownloadManagerDelegateTest
@@ -246,6 +255,11 @@
   // method.
   bool CheckForFileExistence(DownloadItem* download);
 
+  void OnConfirmationCallbackComplete(
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback,
+      DownloadConfirmationResult result,
+      const base::FilePath& virtual_path);
+
   base::FilePath GetDefaultDownloadPath() const;
   TestChromeDownloadManagerDelegate* delegate();
   content::MockDownloadManager* download_manager();
@@ -327,9 +341,13 @@
       .WillByDefault(Return(false));
   ON_CALL(*item, IsTemporary())
       .WillByDefault(Return(false));
+  std::string guid = base::GenerateGUID();
+  ON_CALL(*item, GetGuid()).WillByDefault(ReturnRefOfCopy(guid));
   content::DownloadItemUtils::AttachInfo(item.get(), profile(), web_contents());
   EXPECT_CALL(*download_manager_, GetDownload(id))
       .WillRepeatedly(Return(item.get()));
+  EXPECT_CALL(*download_manager_, GetDownloadByGuid(guid))
+      .WillRepeatedly(Return(item.get()));
   return item;
 }
 
@@ -391,6 +409,13 @@
   return path;
 }
 
+void ChromeDownloadManagerDelegateTest::OnConfirmationCallbackComplete(
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback,
+    DownloadConfirmationResult result,
+    const base::FilePath& virtual_path) {
+  delegate_->OnConfirmationCallbackComplete(callback, result, virtual_path);
+}
+
 TestChromeDownloadManagerDelegate*
     ChromeDownloadManagerDelegateTest::delegate() {
   return delegate_.get();
@@ -711,6 +736,76 @@
   EXPECT_EQ(expected_ids, download_ids());
 }
 
+#if !defined(OS_ANDROID)
+namespace {
+// Verify the file picker confirmation result matches |expected_result|. Run
+// |completion_closure| on completion.
+void VerifyFilePickerConfirmation(DownloadConfirmationResult expected_result,
+                                  base::RepeatingClosure completion_closure,
+                                  DownloadConfirmationResult result,
+                                  const base::FilePath& virtual_path) {
+  ASSERT_EQ(result, expected_result);
+  base::ResetAndReturn(&completion_closure).Run();
+}
+}  // namespace
+
+// Test that it is fine to remove a download before its file picker is being
+// shown.
+TEST_F(ChromeDownloadManagerDelegateTest,
+       RemovingDownloadBeforeShowingFilePicker) {
+  GURL download_url("http://example.com/foo.txt");
+
+  std::unique_ptr<download::MockDownloadItem> download1 =
+      CreateActiveDownloadItem(0);
+  EXPECT_CALL(*download1, GetURL())
+      .Times(AnyNumber())
+      .WillRepeatedly(ReturnRef(download_url));
+  EXPECT_CALL(*download1, GetTargetDisposition())
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
+
+  std::unique_ptr<download::MockDownloadItem> download2 =
+      CreateActiveDownloadItem(1);
+  EXPECT_CALL(*download2, GetURL())
+      .Times(AnyNumber())
+      .WillRepeatedly(ReturnRef(download_url));
+  EXPECT_CALL(*download2, GetTargetDisposition())
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
+
+  EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _))
+      .WillRepeatedly(Invoke(
+          delegate(),
+          &TestChromeDownloadManagerDelegate::RequestConfirmationConcrete));
+
+  base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt"));
+  delegate()->RequestConfirmation(download1.get(), expected_prompt_path,
+                                  DownloadConfirmationReason::NAME_TOO_LONG,
+                                  base::DoNothing());
+  base::RunLoop run_loop;
+  // Verify that the second download's file picker will be canceled, because
+  // it will be removed from the DownloadManager.
+  delegate()->RequestConfirmation(
+      download2.get(), expected_prompt_path,
+      DownloadConfirmationReason::NAME_TOO_LONG,
+      base::BindRepeating(&VerifyFilePickerConfirmation,
+                          DownloadConfirmationResult::CANCELED,
+                          run_loop.QuitClosure()));
+  // Make the manager no longer return the 2nd download as if the latter is
+  // removed.
+  EXPECT_CALL(*download_manager(), GetDownloadByGuid(download2->GetGuid()))
+      .WillRepeatedly(Return(nullptr));
+  // Complete the first download, so the second download's file picker should
+  // be handled. And since the second download is removed from the manager,
+  // the file picker should be canceled.
+  OnConfirmationCallbackComplete(base::DoNothing(),
+                                 DownloadConfirmationResult::CONFIRMED,
+                                 expected_prompt_path);
+
+  run_loop.Run();
+}
+#endif  // OS_ANDROID
+
 #if defined(FULL_SAFE_BROWSING)
 namespace {
 
diff --git a/chrome/browser/download/download_test_file_activity_observer.cc b/chrome/browser/download/download_test_file_activity_observer.cc
index 5d408157..f79f22a3 100644
--- a/chrome/browser/download/download_test_file_activity_observer.cc
+++ b/chrome/browser/download/download_test_file_activity_observer.cc
@@ -52,8 +52,8 @@
   }
 
  protected:
-  void ShowFilePicker(
-      const std::string& guid,
+  void ShowFilePickerForDownload(
+      download::DownloadItem* download,
       const base::FilePath& suggested_path,
       const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback)
       override {
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
index 7be8126..dae8dff 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
@@ -28,6 +28,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/browsing_data/core/pref_names.h"
+#include "components/signin/core/browser/signin_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_remover.h"
 #include "extensions/common/error_utils.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
new file mode 100644
index 0000000..24213489
--- /dev/null
+++ b/chrome/browser/flag-metadata.json
@@ -0,0 +1,28 @@
+// This file lists metadata for chrome://flags entries. This metadata is not
+// ever used in the built browser or in any compiled code, but is used as part
+// of the review process and to clean up flags that have become obsolete or
+// unused.
+//
+// This file is a list of json objects; each object contains these keys:
+//
+//   name: the internal name of the flag, as present in chrome://flags. This is
+//   used as a primary key. The value is a string.
+//
+//   owners: the person(s) or team(s) responsible for this flag. The value is a
+//   list of strings, each of which is either an email address, or any other
+//   text, in which case it is assumed to be the username part of an
+//   @chromium.org email address.
+//
+//   expiry_milestone: the milestone after which this flag is obsolete.
+//   Specifically, after the milestone with the given number branches, this flag
+//   may freely be deleted and defaulted to either enabled or disabled where
+//   used. The special value -1 means "never expires", which should only be used
+//   in consultation with top-level OWNERS.
+
+[
+  {
+    "name": "ignore-gpu-blacklist",
+    "owners": [ "graphics-dev" ],
+    "expiry_milestone": -1
+  }
+]
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 218720f..e1cc3a3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1543,10 +1543,6 @@
     "Performs a one-off irreversible migration of passwords from the "
     "gnome-keyring or kwallet into the profile directory.";
 
-const char kPdfIsolationName[] = "PDF Isolation";
-const char kPdfIsolationDescription[] =
-    "Render PDF files from different origins in different plugin processes.";
-
 const char kPerMethodCanMakePaymentQuotaName[] =
     "Per-method canMakePayment() quota.";
 const char kPerMethodCanMakePaymentQuotaDescription[] =
@@ -3793,6 +3789,18 @@
 
 #endif  // BUILDFLAG(ENABLE_NACL)
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+
+const char kPdfFormSaveName[] = "Save PDF Forms";
+const char kPdfFormSaveDescription[] =
+    "Enable saving PDFs with filled form data.";
+
+const char kPdfIsolationName[] = "PDF Isolation";
+const char kPdfIsolationDescription[] =
+    "Render PDF files from different origins in different plugin processes.";
+
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
+
 #if defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
 const char kAutofillCreditCardUploadName[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 66cc4b9..b6b2b6a6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -13,6 +13,7 @@
 #include "components/nacl/common/buildflags.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
+#include "ppapi/buildflags/buildflags.h"
 
 // This file declares strings used in chrome://flags. These messages are not
 // translated, because instead of end-users they target Chromium developers and
@@ -938,9 +939,6 @@
 extern const char kPasswordsMigrateLinuxToLoginDBName[];
 extern const char kPasswordsMigrateLinuxToLoginDBDescription[];
 
-extern const char kPdfIsolationName[];
-extern const char kPdfIsolationDescription[];
-
 extern const char kPerMethodCanMakePaymentQuotaName[];
 extern const char kPerMethodCanMakePaymentQuotaDescription[];
 
@@ -2300,6 +2298,16 @@
 
 #endif  // BUILDFLAG(ENABLE_NACL)
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+
+extern const char kPdfFormSaveName[];
+extern const char kPdfFormSaveDescription[];
+
+extern const char kPdfIsolationName[];
+extern const char kPdfIsolationDescription[];
+
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
+
 #if defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
 extern const char kAutofillCreditCardUploadName[];
diff --git a/chrome/browser/offline_pages/offline_page_auto_fetcher.cc b/chrome/browser/offline_pages/offline_page_auto_fetcher.cc
index b58a9cb..521cdf8 100644
--- a/chrome/browser/offline_pages/offline_page_auto_fetcher.cc
+++ b/chrome/browser/offline_pages/offline_page_auto_fetcher.cc
@@ -6,14 +6,37 @@
 
 #include <utility>
 
+#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/offline_pages/offline_page_auto_fetcher_service.h"
 #include "chrome/browser/offline_pages/offline_page_auto_fetcher_service_factory.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
+namespace {
+
+TabAndroid* FindTab(content::RenderFrameHost* render_frame_host) {
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host);
+  if (!web_contents)
+    return nullptr;
+  TabModel* tab_model = TabModelList::GetTabModelForWebContents(web_contents);
+  if (!tab_model)
+    return nullptr;
+  // For this use-case, it's OK to fail if the active tab doesn't match
+  // web_contents.
+  if (tab_model->GetActiveWebContents() != web_contents)
+    return nullptr;
+
+  return tab_model->GetTabAt(tab_model->GetActiveIndex());
+}
+
+}  // namespace
 
 OfflinePageAutoFetcher::OfflinePageAutoFetcher(
     content::RenderFrameHost* render_frame_host)
@@ -23,9 +46,14 @@
 
 void OfflinePageAutoFetcher::TrySchedule(bool user_requested,
                                          TryScheduleCallback callback) {
+  TabAndroid* tab = FindTab(render_frame_host_);
+  if (!tab) {
+    std::move(callback).Run(OfflinePageAutoFetcherScheduleResult::kOtherError);
+    return;
+  }
   GetService()->TrySchedule(user_requested,
                             render_frame_host_->GetLastCommittedURL(),
-                            std::move(callback));
+                            tab->GetAndroidId(), std::move(callback));
 }
 
 void OfflinePageAutoFetcher::CancelSchedule() {
diff --git a/chrome/browser/offline_pages/offline_page_auto_fetcher_service.cc b/chrome/browser/offline_pages/offline_page_auto_fetcher_service.cc
index a82f0c67f7..74ab8f37 100644
--- a/chrome/browser/offline_pages/offline_page_auto_fetcher_service.cc
+++ b/chrome/browser/offline_pages/offline_page_auto_fetcher_service.cc
@@ -4,10 +4,15 @@
 
 #include "chrome/browser/offline_pages/offline_page_auto_fetcher_service.h"
 
+#include <string>
 #include <utility>
 
+#include "base/optional.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/offline_pages/request_coordinator_factory.h"
+#include "components/offline_pages/core/auto_fetch.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/client_id.h"
@@ -21,17 +26,24 @@
 namespace {
 constexpr int kMaximumInFlight = 3;
 
-ClientId URLToClientId(const GURL& url) {
-  // By using namespace.URL as the client ID, we ensure that only a single
-  // request for a page can be in flight.
-
+bool URLMatches(const GURL& a, const GURL& b) {
   // We strip the fragment, because when loading an offline page, only the most
   // recent page saved with the URL (fragment stripped) can be loaded.
   GURL::Replacements remove_fragment;
   remove_fragment.ClearRef();
-  GURL url_to_match = url.ReplaceComponents(remove_fragment);
+  return a.ReplaceComponents(remove_fragment) ==
+         b.ReplaceComponents(remove_fragment);
+}
 
-  return ClientId(kAutoAsyncNamespace, url_to_match.spec());
+SavePageRequest* FindRequest(
+    const std::vector<std::unique_ptr<SavePageRequest>>& all_requests,
+    const GURL& url) {
+  for (const auto& request : all_requests) {
+    if (request->client_id().name_space == kAutoAsyncNamespace &&
+        URLMatches(request->url(), url))
+      return request.get();
+  }
+  return nullptr;
 }
 }  // namespace
 
@@ -64,10 +76,11 @@
 
 void OfflinePageAutoFetcherService::TrySchedule(bool user_requested,
                                                 const GURL& url,
+                                                int android_tab_id,
                                                 TryScheduleCallback callback) {
-  StartOrEnqueue(
-      base::BindOnce(&OfflinePageAutoFetcherService::TryScheduleStep1,
-                     GetWeakPtr(), user_requested, url, std::move(callback)));
+  StartOrEnqueue(base::BindOnce(
+      &OfflinePageAutoFetcherService::TryScheduleStep1, GetWeakPtr(),
+      user_requested, url, android_tab_id, std::move(callback)));
 }
 
 void OfflinePageAutoFetcherService::CancelSchedule(const GURL& url) {
@@ -78,6 +91,7 @@
 void OfflinePageAutoFetcherService::TryScheduleStep1(
     bool user_requested,
     const GURL& url,
+    int android_tab_id,
     TryScheduleCallback callback,
     TaskToken token) {
   // Return an early failure if the URL is not suitable.
@@ -89,30 +103,29 @@
 
   // We need to do some checks on in-flight requests before scheduling the
   // fetch. So first, get the list of all requests, and proceed to step 2.
-  request_coordinator_->GetAllRequests(base::BindOnce(
-      &OfflinePageAutoFetcherService::TryScheduleStep2, GetWeakPtr(),
-      std::move(token), user_requested, url, std::move(callback),
-      // Unretained is OK because coordinator is calling us back.
-      base::Unretained(request_coordinator_)));
+  request_coordinator_->GetAllRequests(
+      base::BindOnce(&OfflinePageAutoFetcherService::TryScheduleStep2,
+                     GetWeakPtr(), std::move(token), user_requested, url,
+                     android_tab_id, std::move(callback),
+                     // Unretained is OK because coordinator is calling us back.
+                     base::Unretained(request_coordinator_)));
 }
 
 void OfflinePageAutoFetcherService::TryScheduleStep2(
     TaskToken token,
     bool user_requested,
     const GURL& url,
+    int android_tab_id,
     TryScheduleCallback callback,
     RequestCoordinator* coordinator,
     std::vector<std::unique_ptr<SavePageRequest>> all_requests) {
   // If a request for this page is already scheduled, report scheduling as
   // successful without doing anything.
-  const ClientId url_client_id = URLToClientId(url);
-  for (const auto& request : all_requests) {
-    if (url_client_id == request->client_id()) {
-      std::move(callback).Run(
-          OfflinePageAutoFetcherScheduleResult::kAlreadyScheduled);
-      TaskComplete(std::move(token));
-      return;
-    }
+  if (FindRequest(all_requests, url)) {
+    std::move(callback).Run(
+        OfflinePageAutoFetcherScheduleResult::kAlreadyScheduled);
+    TaskComplete(std::move(token));
+    return;
   }
 
   // Respect kMaximumInFlight.
@@ -134,7 +147,8 @@
   // Finally, schedule a new request, and proceed to step 3.
   RequestCoordinator::SavePageLaterParams params;
   params.url = url;
-  params.client_id = url_client_id;
+  auto_fetch::ClientIdMetadata metadata(android_tab_id);
+  params.client_id = auto_fetch::MakeClientId(metadata);
   params.user_requested = false;
   params.availability =
       RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER;
@@ -170,15 +184,13 @@
     RequestCoordinator* coordinator,
     std::vector<std::unique_ptr<SavePageRequest>> requests) {
   // Cancel the request if it's found in the list of all requests.
-  const ClientId url_client_id = URLToClientId(url);
-  for (const auto& request : requests) {
-    if (url_client_id == request->client_id()) {
-      coordinator->RemoveRequests(
-          {request->request_id()},
-          base::BindOnce(&OfflinePageAutoFetcherService::CancelScheduleStep3,
-                         GetWeakPtr(), std::move(token)));
-      return;
-    }
+  SavePageRequest* matching_request = FindRequest(requests, url);
+  if (matching_request) {
+    coordinator->RemoveRequests(
+        {matching_request->request_id()},
+        base::BindOnce(&OfflinePageAutoFetcherService::CancelScheduleStep3,
+                       GetWeakPtr(), std::move(token)));
+    return;
   }
   TaskComplete(std::move(token));
 }
diff --git a/chrome/browser/offline_pages/offline_page_auto_fetcher_service.h b/chrome/browser/offline_pages/offline_page_auto_fetcher_service.h
index 0aed545..7474a22 100644
--- a/chrome/browser/offline_pages/offline_page_auto_fetcher_service.h
+++ b/chrome/browser/offline_pages/offline_page_auto_fetcher_service.h
@@ -59,6 +59,7 @@
 
   void TrySchedule(bool user_requested,
                    const GURL& url,
+                   int android_tab_id,
                    TryScheduleCallback callback);
   void CancelSchedule(const GURL& url);
 
@@ -90,12 +91,14 @@
 
   void TryScheduleStep1(bool user_requested,
                         const GURL& url,
+                        int android_tab_id,
                         TryScheduleCallback callback,
                         TaskToken token);
   void TryScheduleStep2(
       TaskToken token,
       bool user_requested,
       const GURL& url,
+      int android_tab_id,
       TryScheduleCallback callback,
       RequestCoordinator* coordinator,
       std::vector<std::unique_ptr<SavePageRequest>> all_requests);
diff --git a/chrome/browser/offline_pages/offline_page_auto_fetcher_service_unittest.cc b/chrome/browser/offline_pages/offline_page_auto_fetcher_service_unittest.cc
index d678802..203457e4 100644
--- a/chrome/browser/offline_pages/offline_page_auto_fetcher_service_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_auto_fetcher_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -20,6 +21,7 @@
 
 namespace offline_pages {
 namespace {
+const int kTabId = 1;
 using OfflinePageAutoFetcherScheduleResult =
     chrome::mojom::OfflinePageAutoFetcherScheduleResult;
 
@@ -71,7 +73,8 @@
       result_callback;
   EXPECT_CALL(result_callback,
               Run(OfflinePageAutoFetcherScheduleResult::kScheduled));
-  service_->TrySchedule(false, GURL("http://foo.com"), result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
+                        result_callback.Get());
   browser_thread_bundle_.RunUntilIdle();
   EXPECT_EQ(1ul, GetRequestsSync().size());
 }
@@ -82,7 +85,8 @@
       result_callback;
   EXPECT_CALL(result_callback,
               Run(OfflinePageAutoFetcherScheduleResult::kOtherError));
-  service_->TrySchedule(false, GURL("ftp://foo.com"), result_callback.Get());
+  service_->TrySchedule(false, GURL("ftp://foo.com"), kTabId,
+                        result_callback.Get());
   browser_thread_bundle_.RunUntilIdle();
   EXPECT_EQ(0ul, GetRequestsSync().size());
 }
@@ -98,8 +102,10 @@
               Run(OfflinePageAutoFetcherScheduleResult::kAlreadyScheduled))
       .Times(1);
   // The page should only be saved once, because the fragment is ignored.
-  service_->TrySchedule(false, GURL("http://foo.com#A"), result_callback.Get());
-  service_->TrySchedule(false, GURL("http://foo.com#Z"), result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com#A"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com#Z"), kTabId,
+                        result_callback.Get());
   browser_thread_bundle_.RunUntilIdle();
   EXPECT_EQ(1ul, GetRequestsSync().size());
 }
@@ -120,15 +126,20 @@
       .Times(1);
 
   // Three requests within quota.
-  service_->TrySchedule(false, GURL("http://foo.com/1"), result_callback.Get());
-  service_->TrySchedule(false, GURL("http://foo.com/2"), result_callback.Get());
-  service_->TrySchedule(false, GURL("http://foo.com/3"), result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com/1"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com/2"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com/3"), kTabId,
+                        result_callback.Get());
 
   // Quota is exhausted.
-  service_->TrySchedule(false, GURL("http://foo.com/4"), result_callback.Get());
+  service_->TrySchedule(false, GURL("http://foo.com/4"), kTabId,
+                        result_callback.Get());
 
   // User-requested, quota is not enforced.
-  service_->TrySchedule(true, GURL("http://foo.com/5"), result_callback.Get());
+  service_->TrySchedule(true, GURL("http://foo.com/5"), kTabId,
+                        result_callback.Get());
 
   browser_thread_bundle_.RunUntilIdle();
 }
@@ -141,15 +152,20 @@
   EXPECT_CALL(result_callback,
               Run(OfflinePageAutoFetcherScheduleResult::kScheduled))
       .Times(4);
-  service_->TrySchedule(true, GURL("http://foo.com/1"), result_callback.Get());
-  service_->TrySchedule(true, GURL("http://foo.com/2"), result_callback.Get());
-  service_->TrySchedule(true, GURL("http://foo.com/3"), result_callback.Get());
-  service_->TrySchedule(true, GURL("http://foo.com/4"), result_callback.Get());
+  service_->TrySchedule(true, GURL("http://foo.com/1"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(true, GURL("http://foo.com/2"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(true, GURL("http://foo.com/3"), kTabId,
+                        result_callback.Get());
+  service_->TrySchedule(true, GURL("http://foo.com/4"), kTabId,
+                        result_callback.Get());
   browser_thread_bundle_.RunUntilIdle();
 }
 
 TEST_F(OfflinePageAutoFetcherServiceTest, CancelSuccess) {
-  service_->TrySchedule(false, GURL("http://foo.com"), base::DoNothing());
+  service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
+                        base::DoNothing());
   browser_thread_bundle_.RunUntilIdle();
   service_->CancelSchedule(GURL("http://foo.com"));
   browser_thread_bundle_.RunUntilIdle();
@@ -157,7 +173,8 @@
 }
 
 TEST_F(OfflinePageAutoFetcherServiceTest, CancelNotExist) {
-  service_->TrySchedule(false, GURL("http://foo.com"), base::DoNothing());
+  service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
+                        base::DoNothing());
   browser_thread_bundle_.RunUntilIdle();
   service_->CancelSchedule(GURL("http://NOT-FOO.com"));
   browser_thread_bundle_.RunUntilIdle();
diff --git a/chrome/browser/payments/manifest_verifier_browsertest.cc b/chrome/browser/payments/manifest_verifier_browsertest.cc
index 3f1d2cf..2a1896212 100644
--- a/chrome/browser/payments/manifest_verifier_browsertest.cc
+++ b/chrome/browser/payments/manifest_verifier_browsertest.cc
@@ -51,7 +51,8 @@
         content::BrowserContext::GetDefaultStoragePartition(context)
             ->GetURLLoaderFactoryForBrowserProcess());
     downloader->AddTestServerURL("https://", https_server_->GetURL("/"));
-    auto parser = std::make_unique<payments::PaymentManifestParser>();
+    auto parser = std::make_unique<payments::PaymentManifestParser>(
+        std::make_unique<ErrorLogger>());
     auto cache = WebDataServiceFactory::GetPaymentManifestWebDataForProfile(
         Profile::FromBrowserContext(context),
         ServiceAccessType::EXPLICIT_ACCESS);
diff --git a/chrome/browser/payments/payment_manifest_parser_browsertest.cc b/chrome/browser/payments/payment_manifest_parser_browsertest.cc
index 15e64ee..4c6986a 100644
--- a/chrome/browser/payments/payment_manifest_parser_browsertest.cc
+++ b/chrome/browser/payments/payment_manifest_parser_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/json/json_writer.h"
 #include "base/run_loop.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/payments/core/error_logger.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace payments {
@@ -45,7 +46,9 @@
 // Test fixture for payment manifest parser.
 class PaymentManifestParserTest : public InProcessBrowserTest {
  public:
-  PaymentManifestParserTest() : all_origins_supported_(false) {}
+  PaymentManifestParserTest()
+      : parser_(std::make_unique<ErrorLogger>()),
+        all_origins_supported_(false) {}
   ~PaymentManifestParserTest() override {}
 
   // Sends the |content| to the utility process to parse as a web app manifest
diff --git a/chrome/browser/prerender/OWNERS b/chrome/browser/prerender/OWNERS
index 61337163..9a57b26b 100644
--- a/chrome/browser/prerender/OWNERS
+++ b/chrome/browser/prerender/OWNERS
@@ -1,4 +1,4 @@
-droger@chromium.org
+lizeb@chromium.org
 mattcary@chromium.org
 pasko@chromium.org
 
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 98a9029f..91d30e87 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -1691,84 +1691,6 @@
                    FINAL_STATUS_APP_TERMINATING, 0);
 }
 
-// Checks that we don't prerender in an infinite loop.
-// TODO(crbug.com/903695): This test is flaky.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, DISABLED_PrerenderInfiniteLoop) {
-  const char* const kHtmlFileA = "/prerender/prerender_infinite_a.html";
-  const char* const kHtmlFileB = "/prerender/prerender_infinite_b.html";
-
-  std::vector<FinalStatus> expected_final_status_queue;
-  expected_final_status_queue.push_back(FINAL_STATUS_USED);
-  expected_final_status_queue.push_back(FINAL_STATUS_APP_TERMINATING);
-
-  std::vector<std::unique_ptr<TestPrerender>> prerenders =
-      PrerenderTestURL(kHtmlFileA, expected_final_status_queue, 1);
-  ASSERT_TRUE(prerenders[0]->contents());
-  // Assert that the pending prerender is in there already. This relies on the
-  // fact that the renderer sends out the AddLinkRelPrerender IPC before sending
-  // the page load one.
-  EXPECT_EQ(2U, GetLinkPrerenderCount());
-  EXPECT_EQ(1U, GetRunningLinkPrerenderCount());
-
-  // Next url should be in pending list but not an active entry.
-  EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileB));
-
-  NavigateToDestURL();
-
-  // Make sure the PrerenderContents for the next url is now in the manager and
-  // not pending. This relies on pending prerenders being resolved in the same
-  // event loop iteration as OnPrerenderStop.
-  EXPECT_TRUE(UrlIsInPrerenderManager(kHtmlFileB));
-  EXPECT_EQ(1U, GetLinkPrerenderCount());
-  EXPECT_EQ(1U, GetRunningLinkPrerenderCount());
-}
-
-// Checks that we don't prerender in an infinite loop and multiple links are
-// handled correctly.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       DISABLED_PrerenderInfiniteLoopMultiple) {
-  const char* const kHtmlFileA =
-      "/prerender/prerender_infinite_a_multiple.html";
-  const char* const kHtmlFileB =
-      "/prerender/prerender_infinite_b_multiple.html";
-  const char* const kHtmlFileC =
-      "/prerender/prerender_infinite_c_multiple.html";
-
-  // This test is conceptually simplest if concurrency is at two, since we
-  // don't have to worry about which of kHtmlFileB or kHtmlFileC gets evicted.
-  GetPrerenderManager()->mutable_config().max_link_concurrency = 2;
-  GetPrerenderManager()->mutable_config().max_link_concurrency_per_launcher = 2;
-
-  std::vector<FinalStatus> expected_final_status_queue;
-  expected_final_status_queue.push_back(FINAL_STATUS_USED);
-  expected_final_status_queue.push_back(FINAL_STATUS_APP_TERMINATING);
-  expected_final_status_queue.push_back(FINAL_STATUS_APP_TERMINATING);
-
-  std::vector<std::unique_ptr<TestPrerender>> prerenders =
-      PrerenderTestURL(kHtmlFileA, expected_final_status_queue, 1);
-  ASSERT_TRUE(prerenders[0]->contents());
-
-  // Next url should be in pending list but not an active entry. This relies on
-  // the fact that the renderer sends out the AddLinkRelPrerender IPC before
-  // sending the page load one.
-  EXPECT_EQ(3U, GetLinkPrerenderCount());
-  EXPECT_EQ(1U, GetRunningLinkPrerenderCount());
-  EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileB));
-  EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileC));
-
-  NavigateToDestURL();
-
-  // Make sure the PrerenderContents for the next urls are now in the manager
-  // and not pending. One and only one of the URLs (the last seen) should be the
-  // active entry. This relies on pending prerenders being resolved in the same
-  // event loop iteration as OnPrerenderStop.
-  bool url_b_is_active_prerender = UrlIsInPrerenderManager(kHtmlFileB);
-  bool url_c_is_active_prerender = UrlIsInPrerenderManager(kHtmlFileC);
-  EXPECT_TRUE(url_b_is_active_prerender && url_c_is_active_prerender);
-  EXPECT_EQ(2U, GetLinkPrerenderCount());
-  EXPECT_EQ(2U, GetRunningLinkPrerenderCount());
-}
-
 // Checks that pending prerenders are aborted (and never launched) when launched
 // by a prerender that itself gets aborted.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderAbortPendingOnCancel) {
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index bc7e6e5..82d1174 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -72,12 +72,14 @@
 const char kPrefetchLoaderPath[] = "/prerender/prefetch_loader.html";
 const char kPrefetchLoopPage[] = "/prerender/prefetch_loop.html";
 const char kPrefetchMetaCSP[] = "/prerender/prefetch_meta_csp.html";
+const char kPrefetchNostorePage[] = "/prerender/prefetch_nostore_page.html";
 const char kPrefetchPage[] = "/prerender/prefetch_page.html";
 const char kPrefetchPage2[] = "/prerender/prefetch_page2.html";
 const char kPrefetchPageBigger[] = "/prerender/prefetch_page_bigger.html";
 const char kPrefetchPng[] = "/prerender/image.png";
 const char kPrefetchPng2[] = "/prerender/image2.png";
 const char kPrefetchPngRedirect[] = "/prerender/image-redirect.png";
+const char kPrefetchRecursePage[] = "/prerender/prefetch_recurse.html";
 const char kPrefetchResponseHeaderCSP[] =
     "/prerender/prefetch_response_csp.html";
 const char kPrefetchScript[] = "/prerender/prefetch.js";
@@ -481,6 +483,24 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
 }
 
+// Checks that a prefetch does not recursively prefetch.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, NoPrefetchRecursive) {
+  // A prefetch of a page with a prefetch of the image page should not load the
+  // image page.
+  PrefetchFromFile(kPrefetchRecursePage,
+                   FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchRecursePage), 1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchNostorePage), 0);
+
+  // When the first page is loaded, the image page should be prefetched. The
+  // test may finish before the prerender is torn down, so
+  // IgnorePrerenderContents() is called to skip the final status check.
+  prerender_contents_factory()->IgnorePrerenderContents();
+  ui_test_utils::NavigateToURL(current_browser(),
+                               src_server()->GetURL(kPrefetchRecursePage));
+  WaitForRequestCount(src_server()->GetURL(kPrefetchNostorePage), 1);
+}
+
 // Checks a prefetch to a nonexisting page.
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchNonexisting) {
   std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 89a968a..8bdccdc 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -277,7 +277,8 @@
     const GURL& url,
     const content::Referrer& referrer,
     Origin origin,
-    FinalStatus expected_final_status)
+    FinalStatus expected_final_status,
+    bool ignore_final_status)
     : PrerenderContents(prerender_manager, profile, url, referrer, origin),
       expected_final_status_(expected_final_status),
       observer_(this),
@@ -285,7 +286,7 @@
       was_hidden_(false),
       was_shown_(false),
       should_be_shown_(expected_final_status == FINAL_STATUS_USED),
-      skip_final_checks_(false) {}
+      skip_final_checks_(ignore_final_status) {}
 
 TestPrerenderContents::~TestPrerenderContents() {
   if (skip_final_checks_)
@@ -519,6 +520,10 @@
   return handle;
 }
 
+void TestPrerenderContentsFactory::IgnorePrerenderContents() {
+  expected_contents_queue_.push_back(ExpectedContents(true));
+}
+
 PrerenderContents* TestPrerenderContentsFactory::CreatePrerenderContents(
     PrerenderManager* prerender_manager,
     Profile* profile,
@@ -530,15 +535,15 @@
     expected = expected_contents_queue_.front();
     expected_contents_queue_.pop_front();
   }
-  TestPrerenderContents* contents = new TestPrerenderContents(
-      prerender_manager, profile, url, referrer, origin, expected.final_status);
+  TestPrerenderContents* contents =
+      new TestPrerenderContents(prerender_manager, profile, url, referrer,
+                                origin, expected.final_status, expected.ignore);
   if (expected.handle)
     expected.handle->OnPrerenderCreated(contents);
   return contents;
 }
 
-TestPrerenderContentsFactory::ExpectedContents::ExpectedContents()
-    : final_status(FINAL_STATUS_MAX) {}
+TestPrerenderContentsFactory::ExpectedContents::ExpectedContents() {}
 
 TestPrerenderContentsFactory::ExpectedContents::ExpectedContents(
     const ExpectedContents& other) = default;
@@ -548,6 +553,9 @@
     const base::WeakPtr<TestPrerender>& handle)
     : final_status(final_status), handle(handle) {}
 
+TestPrerenderContentsFactory::ExpectedContents::ExpectedContents(bool ignore)
+    : ignore(ignore) {}
+
 TestPrerenderContentsFactory::ExpectedContents::~ExpectedContents() {}
 
 PrerenderInProcessBrowserTest::PrerenderInProcessBrowserTest()
diff --git a/chrome/browser/prerender/prerender_test_utils.h b/chrome/browser/prerender/prerender_test_utils.h
index 49859a4..1985a02 100644
--- a/chrome/browser/prerender/prerender_test_utils.h
+++ b/chrome/browser/prerender/prerender_test_utils.h
@@ -109,7 +109,8 @@
                         const GURL& url,
                         const content::Referrer& referrer,
                         Origin origin,
-                        FinalStatus expected_final_status);
+                        FinalStatus expected_final_status,
+                        bool ignore_final_status);
 
   ~TestPrerenderContents() override;
 
@@ -280,6 +281,8 @@
   std::unique_ptr<TestPrerender> ExpectPrerenderContents(
       FinalStatus final_status);
 
+  void IgnorePrerenderContents();
+
   PrerenderContents* CreatePrerenderContents(
       PrerenderManager* prerender_manager,
       Profile* profile,
@@ -293,10 +296,12 @@
     ExpectedContents(const ExpectedContents& other);
     ExpectedContents(FinalStatus final_status,
                      const base::WeakPtr<TestPrerender>& handle);
+    explicit ExpectedContents(bool ignore);
     ~ExpectedContents();
 
-    FinalStatus final_status;
-    base::WeakPtr<TestPrerender> handle;
+    FinalStatus final_status = FINAL_STATUS_MAX;
+    bool ignore = false;
+    base::WeakPtr<TestPrerender> handle = nullptr;
   };
 
   base::circular_deque<ExpectedContents> expected_contents_queue_;
diff --git a/chrome/browser/printing/DEPS b/chrome/browser/printing/DEPS
new file mode 100644
index 0000000..f5a9247
--- /dev/null
+++ b/chrome/browser/printing/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+pdf/pdf.h",  # Test only.
+]
diff --git a/chrome/browser/profiles/profile_downloader_unittest.cc b/chrome/browser/profiles/profile_downloader_unittest.cc
index 33735e4a..45eb3a7 100644
--- a/chrome/browser/profiles/profile_downloader_unittest.cc
+++ b/chrome/browser/profiles/profile_downloader_unittest.cc
@@ -11,16 +11,15 @@
 #include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/fake_account_fetcher_service_builder.h"
-#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/signin/test_signin_client_builder.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/test_signin_client.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/url_request/test_url_fetcher_factory.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -36,9 +35,10 @@
 
 } // namespace
 
-class ProfileDownloaderTest : public testing::Test,
-                              public ProfileDownloaderDelegate,
-                              public OAuth2TokenService::DiagnosticsObserver {
+class ProfileDownloaderTest
+    : public testing::Test,
+      public ProfileDownloaderDelegate,
+      public identity::IdentityManager::DiagnosticsObserver {
  protected:
   ProfileDownloaderTest()
     : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
@@ -47,24 +47,30 @@
   void SetUp() override {
     TestingProfile::Builder builder;
     builder.AddTestingFactory(
-        ProfileOAuth2TokenServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildFakeProfileOAuth2TokenService));
-    builder.AddTestingFactory(
         AccountFetcherServiceFactory::GetInstance(),
         base::BindRepeating(&FakeAccountFetcherServiceBuilder::BuildForTests));
-    profile_ = builder.Build();
+
+    profile_ = IdentityTestEnvironmentProfileAdaptor::
+        CreateProfileForIdentityTestEnvironment(builder);
+
     account_tracker_service_ =
         AccountTrackerServiceFactory::GetForProfile(profile_.get());
     account_fetcher_service_ = static_cast<FakeAccountFetcherService*>(
         AccountFetcherServiceFactory::GetForProfile(profile_.get()));
-    token_service_ = static_cast<FakeProfileOAuth2TokenService*>(
-        ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get()));
     profile_downloader_.reset(new ProfileDownloader(this));
 
-    token_service_->AddDiagnosticsObserver(this);
+    identity_test_env_profile_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
+    identity_test_env_ =
+        identity_test_env_profile_adaptor_->identity_test_env();
+    DCHECK(identity_test_env_);
+
+    identity_test_env_->identity_manager()->AddDiagnosticsObserver(this);
   }
 
-  void TearDown() override { token_service_->RemoveDiagnosticsObserver(this); }
+  void TearDown() override {
+    identity_test_env_->identity_manager()->RemoveDiagnosticsObserver(this);
+  }
 
   bool NeedsProfilePicture() const override { return true; };
   int GetDesiredImageSideLength() const override { return 128; };
@@ -86,11 +92,10 @@
         kTestLocale, picture_url);
   }
 
-  // OAuth2TokenService::DiagnosticsObserver:
-  void OnAccessTokenRequested(
-      const std::string& account_id,
-      const std::string& consumer_id,
-      const OAuth2TokenService::ScopeSet& scopes) override {
+  // IdentityManager::DiagnosticsObserver:
+  void OnAccessTokenRequested(const std::string& account_id,
+                              const std::string& consumer_id,
+                              const identity::ScopeSet& scopes) override {
     // This flow should be invoked only when a test has explicitly set up
     // preconditions so that ProfileDownloader will request access tokens.
     DCHECK(!on_access_token_request_callback_.is_null());
@@ -106,9 +111,11 @@
 
   AccountTrackerService* account_tracker_service_;
   FakeAccountFetcherService* account_fetcher_service_;
-  FakeProfileOAuth2TokenService* token_service_;
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<Profile> profile_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_profile_adaptor_;
+  identity::IdentityTestEnvironment* identity_test_env_;
   base::OnceClosure on_access_token_request_callback_;
   std::string account_id_for_access_token_request_;
   std::unique_ptr<ProfileDownloader> profile_downloader_;
@@ -117,7 +124,7 @@
 TEST_F(ProfileDownloaderTest, FetchAccessToken) {
   std::string account_id =
       account_tracker_service_->SeedAccountInfo(kTestGaia, kTestEmail);
-  token_service_->UpdateCredentials(account_id, "refresh_token");
+  identity_test_env_->SetRefreshTokenForAccount(account_id);
 
   base::RunLoop run_loop;
   set_on_access_token_requested_callback(run_loop.QuitClosure());
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index 2cba9ad..97f38461 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -394,6 +394,7 @@
 
 .md-tile-container.reorder .md-tile {
   background-color: white;
+  border-radius: 4px;
   box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3),
       0 4px 8px 3px rgba(60, 64, 67, 0.15);
   transition-duration: 200ms;
@@ -408,7 +409,7 @@
   padding: var(--md-tile-padding-vertical) var(--md-tile-padding-horizontal);
 }
 
-body.using-theme .md-tile {
+body.using-theme .md-tile-container .md-tile {
   /* The theme title pill adds 2*4px of vertical padding. */
   padding-bottom: calc(var(--md-tile-padding-vertical) - 2*4px);
 }
@@ -519,7 +520,7 @@
 }
 
 /* Apply when a custom background is set */
-body.dark-theme .md-title {
+body.dark-theme .md-tile-container:not(.reorder) .md-title {
   color: rgb(248, 249, 250);
   text-shadow: 0 0 16px rgba(0, 0, 0, 0.3);
 }
@@ -531,7 +532,7 @@
   padding: 4px;
 }
 
-body.using-theme .md-title {
+body.using-theme .md-tile-container:not(.reorder) .md-title {
   color: rgba(33, 32, 36, 0.86);
   text-shadow: unset;
 }
@@ -549,6 +550,11 @@
   width: var(--md-menu-size);
 }
 
+html[dir=rtl] .md-menu {
+  left: 0;
+  right: auto;
+}
+
 body:not(.reordering) .md-menu:active,
 body:not(.reordering) .md-menu:focus:not(.mouse-navigation) {
   opacity: 1;
@@ -584,21 +590,18 @@
   width: var(--md-edit-menu-size);
 }
 
-body.dark-theme .md-menu::after {
-  background-color: white;
-}
-
-html[dir=rtl] .md-menu {
-  left: 0;
-  right: auto;
-}
-
 body:not(.reordering) .md-menu:hover::after,
 body:not(.reordering) .md-menu:focus::after {
   background-color: rgba(33, 32, 36, 0.71);
 }
 
+body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
+body.dark-theme:not(.reordering) .md-menu:focus:not(.mouse-navigation)::after {
+  background-color: white;
+}
+
+body.dark-theme:not(.reordering) .md-menu:active::after,
 body.dark-theme:not(.reordering) .md-menu:hover::after,
-body.dark-theme:not(.reordering) .md-menu:focus::after {
+body.dark-theme:not(.reordering) .md-menu.mouse-navigation:focus::after {
   background-color: rgb(218, 220, 224);
 }
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 795c48b..e8148b12 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -643,22 +643,29 @@
   // Starts the reorder flow after the user has held the mouse button down for
   // |REORDER_TIMEOUT_DELAY|.
   tile.addEventListener('mousedown', (event) => {
-    // Do not reorder if the edit menu was clicked.
-    if (event.button == 0 /* LEFT CLICK */ &&
+    // Do not reorder if the edit menu was clicked or if ctrl/shift/alt/meta is
+    // also held down.
+    if (event.button == 0 /* LEFT CLICK */ && !event.ctrlKey &&
+        !event.shiftKey && !event.altKey && !event.metaKey &&
         !event.target.classList.contains(CLASSES.MD_MENU)) {
       let timeout = -1;
 
       // Cancel the timeout if the user drags the mouse off the tile and
-      // releases.
+      // releases or if the mouse if released.
       let dragend = document.addEventListener('dragend', () => {
         window.clearTimeout(timeout);
       }, {once: true});
+      let mouseup = document.addEventListener('mouseup', () => {
+        if (event.button == 0 /* LEFT CLICK */)
+          window.clearTimeout(timeout);
+      }, {once: true});
 
       // Wait for |REORDER_TIMEOUT_DELAY| before starting the reorder flow.
       timeout = window.setTimeout(() => {
         if (!reordering)
           startReorder(tile);
         document.removeEventListener('dragend', dragend);
+        document.removeEventListener('mouseup', mouseup);
       }, REORDER_TIMEOUT_DELAY);
     }
   });
diff --git a/chrome/browser/resources/md_extensions/BUILD.gn b/chrome/browser/resources/md_extensions/BUILD.gn
index 4cd2d50c..416dfc3 100644
--- a/chrome/browser/resources/md_extensions/BUILD.gn
+++ b/chrome/browser/resources/md_extensions/BUILD.gn
@@ -51,7 +51,6 @@
 js_type_check("closure_compile") {
   deps = [
     ":activity_log",
-    ":activity_log_item",
     ":code_section",
     ":detail_view",
     ":drag_and_drop_handler",
@@ -91,22 +90,10 @@
   externs_list = [ "$externs_path/developer_private.js" ]
 }
 
-js_library("activity_log_item") {
-  deps = [
-    "//ui/webui/resources/js:cr",
-  ]
-  externs_list = [ "$externs_path/activity_log_private.js" ]
-}
-
 js_library("activity_log") {
   deps = [
-    ":activity_log_item",
     "//ui/webui/resources/js:cr",
   ]
-  externs_list = [
-    "$externs_path/activity_log_private.js",
-    "$externs_path/developer_private.js",
-  ]
 }
 
 js_library("detail_view") {
@@ -257,7 +244,6 @@
 js_library("manager") {
   deps = [
     ":activity_log",
-    ":activity_log_item",
     ":detail_view",
     ":item",
     ":item_list",
@@ -341,7 +327,6 @@
     "//ui/webui/resources/js:load_time_data",
   ]
   externs_list = [
-    "$externs_path/activity_log_private.js",
     "$externs_path/developer_private.js",
     "$externs_path/management.js",
     "$externs_path/metrics_private.js",
diff --git a/chrome/browser/resources/md_extensions/activity_log.html b/chrome/browser/resources/md_extensions/activity_log.html
index cd26d88..d552690 100644
--- a/chrome/browser/resources/md_extensions/activity_log.html
+++ b/chrome/browser/resources/md_extensions/activity_log.html
@@ -1,44 +1,26 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
-<link rel="import" href="activity_log_item.html">
-<link rel="import" href="navigation_helper.html">
-<link rel="import" href="shared_style.html">
 
 <dom-module id="extensions-activity-log">
   <template>
-    <style include="iron-flex cr-shared-style shared-style">
-      .empty-activity-message {
-        color: #6e6e6e;
-        font-size: 123%;  /* Should be 16px when 100% is 13px. */
-        font-weight: 500;
-        margin-top: 80px;
-        text-align: center;
+    <style include="iron-flex cr-shared-style">
+      #container {
+        height: 100%;
+        overflow: overlay;
+      }
+
+      #main {
+        background-color: white;
+        margin: auto;
+        min-height: 100%;
+        padding-bottom: 64px;
+        width: var(--cr-toolbar-field-width);
       }
     </style>
-    <div class="page-container" id="container">
-      <div class="page-content">
-        <div class="page-header">
-          <paper-icon-button-light class="icon-arrow-back no-overlap">
-            <button id="close-button" aria-label="$i18n{back}"
-                on-click="onCloseButtonTap_"></button>
-          </paper-icon-button-light>
-          <span>$i18n{activityLogPageHeading}</span>
-        </div>
-        <div id="no-activities" class="empty-activity-message"
-            hidden$="[[!shouldShowEmptyActivityLogMessage_(
-                activityData_)]]">
-            <span>$i18n{noActivities}</span>
-        </div>
-        <div hidden$="[[!activityData_]]">
-          <template is="dom-repeat" items="[[activityData_.activities]]">
-            <activity-log-item id="[[item.activityId]]" data="[[item]]">
-            </activity-log-item>
-          </template>
-        </div>
+    <div id="container">
+      <div id="main">
+        Welcome to the new activity log
       </div>
     </div>
   </template>
diff --git a/chrome/browser/resources/md_extensions/activity_log.js b/chrome/browser/resources/md_extensions/activity_log.js
index 7bbb8612..7f04685 100644
--- a/chrome/browser/resources/md_extensions/activity_log.js
+++ b/chrome/browser/resources/md_extensions/activity_log.js
@@ -5,86 +5,11 @@
 cr.define('extensions', function() {
   'use strict';
 
-  /** @interface */
-  class ActivityLogDelegate {
-    /**
-     * @param {string} extensionId
-     * @return {!Promise<!chrome.activityLogPrivate.ActivityResultSet>}
-     */
-    getExtensionActivityLog(extensionId) {}
-  }
-
   const ActivityLog = Polymer({
     is: 'extensions-activity-log',
 
-    behaviors: [
-      CrContainerShadowBehavior,
-    ],
-
-    properties: {
-      /** @type {!string} */
-      extensionId: String,
-
-      /** @type {!extensions.ActivityLogDelegate} */
-      delegate: Object,
-
-      /**
-       * @private
-       * @type {!chrome.activityLogPrivate.ActivityResultSet|undefined}
-       */
-      activityData_: Object,
-    },
-
-    navigationListener_: null,
-
-    /** @override */
-    attached: function() {
-      // Fetch the activity log for the extension when this page is attached.
-      // This is necesary as the listener below is not fired if the user
-      // navigates directly to the activity log page using url.
-      this.getActivityLog_();
-
-      // Add a listener here so we fetch the activity log whenever a user
-      // navigates to the activity log from another page.
-      // This is needed since this component already exists in the background
-      // if a user navigates away from this page so attached may not be called
-      // when a user navigates back.
-      this.navigationListener_ = extensions.navigation.addListener(newPage => {
-        if (newPage.page === Page.ACTIVITY_LOG)
-          this.getActivityLog_();
-      });
-    },
-
-    /** @override */
-    detached: function() {
-      assert(extensions.navigation.removeListener(this.navigationListener_));
-      this.navigationListener_ = null;
-    },
-
-    /**
-     * @private
-     * @return {boolean}
-     */
-    shouldShowEmptyActivityLogMessage_: function() {
-      return !this.activityData_ || this.activityData_.activities.length === 0;
-    },
-
-    /** @private */
-    onCloseButtonTap_: function() {
-      extensions.navigation.navigateTo(
-          {page: Page.DETAILS, extensionId: this.extensionId});
-    },
-
-    /** @private */
-    getActivityLog_: function() {
-      this.delegate.getExtensionActivityLog(this.extensionId).then(result => {
-        this.activityData_ = result;
-      });
-    },
+    properties: {},
   });
 
-  return {
-    ActivityLog: ActivityLog,
-    ActivityLogDelegate: ActivityLogDelegate,
-  };
+  return {ActivityLog: ActivityLog};
 });
diff --git a/chrome/browser/resources/md_extensions/activity_log_item.html b/chrome/browser/resources/md_extensions/activity_log_item.html
deleted file mode 100644
index 237218ee..0000000
--- a/chrome/browser/resources/md_extensions/activity_log_item.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/html/cr.html">
-
-<dom-module id="activity-log-item">
-  <template>
-    <style include="iron-flex cr-shared-style">
-      #main {
-        min-height: 0;
-        padding: 16px 20px;
-      }
-
-      #activity-type {
-        min-width: 85px;
-      }
-
-      #api-call {
-        flex-grow: 1;
-        margin-left: 10px;
-      }
-
-      #page-url-link {
-        margin-left: 16px;
-      }
-    </style>
-    <div id="main">
-      <div id="activity-call-and-count" class="layout horizontal">
-        <span id="activity-type">[[data.activityType]]</span>
-        <span id="api-call">[[data.apiCall]]</span>
-        <span id="activity-count">[[data.count]]</span>
-      </div>
-      <div id="page-url" hidden$="[[!data.pageUrl]]">
-        <a id="page-url-link" href="[[data.pageUrl]]">[[data.pageUrl]]</a>
-      </div>
-    </div>
-  </template>
-  <script src="activity_log_item.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/md_extensions/activity_log_item.js b/chrome/browser/resources/md_extensions/activity_log_item.js
deleted file mode 100644
index 7945763..0000000
--- a/chrome/browser/resources/md_extensions/activity_log_item.js
+++ /dev/null
@@ -1,22 +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.
-
-cr.define('extensions', function() {
-  'use strict';
-
-  const ActivityLogItem = Polymer({
-    is: 'activity-log-item',
-
-    properties: {
-      /**
-       * The underlying ExtensionActivity that provides data for the
-       * ActivityLogItem displayed.
-       * @type {chrome.activityLogPrivate.ExtensionActivity}
-       */
-      data: Object,
-    },
-  });
-
-  return {ActivityLogItem: ActivityLogItem};
-});
diff --git a/chrome/browser/resources/md_extensions/detail_view.html b/chrome/browser/resources/md_extensions/detail_view.html
index 0fafd78b..137ce6d 100644
--- a/chrome/browser/resources/md_extensions/detail_view.html
+++ b/chrome/browser/resources/md_extensions/detail_view.html
@@ -23,13 +23,12 @@
 <link rel="import" href="host_permissions_toggle_list.html">
 <link rel="import" href="navigation_helper.html">
 <link rel="import" href="runtime_host_permissions.html">
-<link rel="import" href="shared_style.html">
 <link rel="import" href="strings.html">
 <link rel="import" href="toggle_row.html">
 
 <dom-module id="extensions-detail-view">
   <template>
-    <style include="iron-flex cr-shared-style cr-icons action-link paper-button-style shared-style">
+    <style include="iron-flex cr-shared-style cr-icons action-link paper-button-style">
       :host {
         --iron-icon-fill-color: var(--google-grey-refresh-700);
         display: block;
@@ -53,6 +52,29 @@
         color: var(--google-blue-500);
       }
 
+      #container {
+        height: 100%;
+        overflow: overlay;
+      }
+
+      #main {
+        @apply --cr-card-elevation;
+        background-color: white;
+        box-sizing: border-box;
+        margin: auto;
+        min-height: 100%;
+        padding-bottom: 64px;
+        width: var(--cr-toolbar-field-width);
+      }
+
+      #top-bar {
+        align-items: center;
+        display: flex;
+        height: 40px;
+        margin-bottom: 12px;
+        padding: 8px 12px 0;
+      }
+
       #icon {
         height: 24px;
         margin-inline-end: 12px;
@@ -169,9 +191,9 @@
         @apply --cr-icon-height-width;
       }
     </style>
-    <div class="page-container" id="container">
-      <div class="page-content">
-        <div class="page-header">
+    <div id="container">
+      <div id="main">
+        <div id="top-bar">
           <paper-icon-button-light class="icon-arrow-back no-overlap">
             <button id="closeButton" aria-label="$i18n{back}"
                 on-click="onCloseButtonTap_"></button>
diff --git a/chrome/browser/resources/md_extensions/error_page.html b/chrome/browser/resources/md_extensions/error_page.html
index 62cb02cb..851a7bd 100644
--- a/chrome/browser/resources/md_extensions/error_page.html
+++ b/chrome/browser/resources/md_extensions/error_page.html
@@ -17,16 +17,20 @@
 <link rel="import" href="code_section.html">
 <link rel="import" href="item_util.html">
 <link rel="import" href="navigation_helper.html">
-<link rel="import" href="shared_style.html">
 
 <dom-module id="extensions-error-page">
   <template>
-    <style include="paper-button-style cr-icons cr-shared-style shared-style">
+    <style include="paper-button-style cr-icons cr-shared-style">
       :host {
         display: block;
         height: 100%;
       }
 
+      #container {
+        height: 100%;
+        overflow: overlay;
+      }
+
       iron-icon {
         --iron-icon-fill-color: var(--google-grey-refresh-700);
         @apply --cr-icon-height-width;
@@ -41,6 +45,17 @@
         --iron-icon-fill-color: var(--paper-red-700);
       }
 
+      /* TODO(dpapad): There is a lot of duplicated styling between
+       * detail_view.html and error_page.html. Refactor such that no duplication
+       * happens.*/
+      #main {
+        @apply --cr-card-elevation;
+        background-color: white;
+        margin: auto;
+        min-height: 100%;
+        width: 640px;
+      }
+
       .section {
         padding: 0 var(--cr-section-padding);
       }
@@ -154,8 +169,8 @@
         outline: none;
       }
     </style>
-    <div class="page-container" id="container">
-      <div class="page-content">
+    <div id="container">
+      <div id="main">
         <div id="heading">
           <paper-icon-button-light class="icon-arrow-back no-overlap">
             <button id="closeButton" aria-label="$i18n{back}"
diff --git a/chrome/browser/resources/md_extensions/extensions_resources.grd b/chrome/browser/resources/md_extensions/extensions_resources.grd
index d1b672a..df243d2d 100644
--- a/chrome/browser/resources/md_extensions/extensions_resources.grd
+++ b/chrome/browser/resources/md_extensions/extensions_resources.grd
@@ -35,12 +35,6 @@
       <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_JS"
                  file="activity_log.js"
                  type="chrome_html" />
-      <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ITEM_HTML"
-                 file="activity_log_item.html"
-                 type="chrome_html" />
-      <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ITEM_JS"
-                 file="activity_log_item.js"
-                 type="chrome_html" />
       <structure name="IDR_MD_EXTENSIONS_DETAIL_VIEW_HTML"
                  file="detail_view.html"
                  type="chrome_html" />
@@ -177,9 +171,6 @@
       <structure name="IDR_MD_EXTENSIONS_SERVICE_JS"
                  file="service.js"
                  type="chrome_html" />
-      <structure name="IDR_MD_EXTENSIONS_SHARED_STYLE_HTML"
-                 file="shared_style.html"
-                 type="chrome_html" />
       <structure name="IDR_MD_EXTENSIONS_SHORTCUT_INPUT_HTML"
                  file="shortcut_input.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index a8d153e..639c08f 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -98,8 +98,7 @@
       </cr-lazy-render>
       <cr-lazy-render id="activity-log">
         <template>
-          <extensions-activity-log delegate="[[delegate]]" slot="view"
-              extension-id="[[activityLogItemId_]]">
+          <extensions-activity-log slot="view">
           </extensions-activity-log>
         </template>
       </cr-lazy-render>
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js
index f4622d2..63ca220 100644
--- a/chrome/browser/resources/md_extensions/manager.js
+++ b/chrome/browser/resources/md_extensions/manager.js
@@ -94,13 +94,6 @@
        */
       detailViewItem_: Object,
 
-      /**
-       * The id of the item for the activity log view subpage.
-       * See also errorPageItem_.
-       * @private {!string|undefined}
-       */
-      activityLogItemId_: String,
-
       /** @private {!Array<!chrome.developerPrivate.ExtensionInfo>} */
       extensions_: Array,
 
@@ -392,8 +385,6 @@
           this.errorPageItem_ && this.errorPageItem_.id == item.id &&
           this.currentPage_.page == Page.ERRORS) {
         this.errorPageItem_ = item;
-      } else if (this.currentPage_.page == Page.ACTIVITY_LOG) {
-        this.activityLogItemId_ = item.id;
       }
     },
 
@@ -476,8 +467,6 @@
               {page: Page.DETAILS, extensionId: newPage.extensionId});
           return;
         }
-
-        this.activityLogItemId_ = assert(data.id);
       }
 
       if (fromPage != toPage) {
diff --git a/chrome/browser/resources/md_extensions/service.js b/chrome/browser/resources/md_extensions/service.js
index d8f6417..0e1646c3 100644
--- a/chrome/browser/resources/md_extensions/service.js
+++ b/chrome/browser/resources/md_extensions/service.js
@@ -6,7 +6,6 @@
   'use strict';
 
   /**
-   * @implements {extensions.ActivityLogDelegate}
    * @implements {extensions.ErrorPageDelegate}
    * @implements {extensions.ItemDelegate}
    * @implements {extensions.KeyboardShortcutDelegate}
@@ -339,21 +338,6 @@
     showInFolder(id) {
       chrome.developerPrivate.showPath(id);
     }
-
-    /** @override */
-    getExtensionActivityLog(extensionId) {
-      return new Promise(function(resolve, reject) {
-        chrome.activityLogPrivate.getExtensionActivities(
-            {
-              activityType:
-                  chrome.activityLogPrivate.ExtensionActivityFilter.ANY,
-              extensionId: extensionId
-            },
-            function(result) {
-              resolve(result);
-            });
-      });
-    }
   }
 
   cr.addSingletonGetter(Service);
diff --git a/chrome/browser/resources/md_extensions/shared_style.html b/chrome/browser/resources/md_extensions/shared_style.html
deleted file mode 100644
index c2a074713..0000000
--- a/chrome/browser/resources/md_extensions/shared_style.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-
-<dom-module id="shared-style">
-  <template>
-    <style include="cr-shared-style">
-      .page-container {
-        height: 100%;
-        overflow: overlay;
-      }
-
-      .page-content {
-        @apply --cr-card-elevation;
-        background-color: white;
-        margin: auto;
-        min-height: 100%;
-        padding-bottom: 64px;
-        width: var(--cr-toolbar-field-width);
-      }
-
-      .page-header {
-        align-items: center;
-        display: flex;
-        height: 40px;
-        margin-bottom: 12px;
-        padding: 8px 12px 0;
-      }
-    </style>
-  </template>
-</dom-module>
diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc
index 2d6f9c9f..977660a 100644
--- a/chrome/browser/signin/account_reconcilor_factory.cc
+++ b/chrome/browser/signin/account_reconcilor_factory.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_reconcilor.h"
@@ -40,8 +40,8 @@
     : public signin::MirrorAccountReconcilorDelegate {
  public:
   explicit ChromeOSChildAccountReconcilorDelegate(
-      SigninManagerBase* signin_manager)
-      : signin::MirrorAccountReconcilorDelegate(signin_manager) {}
+      identity::IdentityManager* identity_manager)
+      : signin::MirrorAccountReconcilorDelegate(identity_manager) {}
 
   base::TimeDelta GetReconcileTimeout() const override {
     return base::TimeDelta::FromSeconds(10);
@@ -78,8 +78,8 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(ChromeSigninClientFactory::GetInstance());
   DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
-  DependsOn(SigninManagerFactory::GetInstance());
 }
 
 AccountReconcilorFactory::~AccountReconcilorFactory() {}
@@ -100,7 +100,7 @@
   Profile* profile = Profile::FromBrowserContext(context);
   AccountReconcilor* reconcilor = new AccountReconcilor(
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-      SigninManagerFactory::GetForProfile(profile),
+      IdentityManagerFactory::GetForProfile(profile),
       ChromeSigninClientFactory::GetForProfile(profile),
       GaiaCookieManagerServiceFactory::GetForProfile(profile),
       CreateAccountReconcilorDelegate(profile));
@@ -120,11 +120,11 @@
       // delegate.
       if (profile->IsChild()) {
         return std::make_unique<ChromeOSChildAccountReconcilorDelegate>(
-            SigninManagerFactory::GetForProfile(profile));
+            IdentityManagerFactory::GetForProfile(profile));
       }
 #endif
       return std::make_unique<signin::MirrorAccountReconcilorDelegate>(
-          SigninManagerFactory::GetForProfile(profile));
+          IdentityManagerFactory::GetForProfile(profile));
 
     case signin::AccountConsistencyMethod::kDisabled:
     case signin::AccountConsistencyMethod::kDiceFixAuthErrors:
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc
index 87c4cdfb..0488e11 100644
--- a/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -154,8 +154,8 @@
         std::make_unique<signin::DiceAccountReconcilorDelegate>(
             &signin_client_, signin::AccountConsistencyMethod::kDisabled);
     account_reconcilor_ = std::make_unique<AccountReconcilor>(
-        &token_service_, &signin_manager_, &signin_client_, nullptr,
-        std::move(account_reconcilor_delegate));
+        &token_service_, identity_test_env_.identity_manager(), &signin_client_,
+        nullptr, std::move(account_reconcilor_delegate));
     about_signin_internals_.Initialize(&signin_client_);
     account_tracker_service_.Initialize(&pref_service_, base::FilePath());
     account_reconcilor_->AddObserver(this);
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
index c474463..b020c27 100644
--- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -360,16 +360,14 @@
   std::string account_id_2 = "account_id_2";
   std::string refresh_token_2 = "refresh_token_2";
 
-  // TODO(fgorski): Enable below when implemented:
-  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
-  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_2));
+  EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_1));
+  EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_2));
   oauth2_service_delegate_->UpdateCredentials(account_id_1, refresh_token_1);
   oauth2_service_delegate_->UpdateCredentials(account_id_2, refresh_token_2);
   EXPECT_EQ(2, start_batch_changes_);
   EXPECT_EQ(2, end_batch_changes_);
 
-  // TODO(fgorski): Enable below when implemented:
-  // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
+  EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_1));
   EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_2));
 
   ResetObserverCounts();
@@ -378,8 +376,7 @@
   EXPECT_EQ(1, end_batch_changes_);
   ExpectOneTokenRevokedNotification();
 
-  // TODO(fgorski): Enable below when implemented:
-  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
+  EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_1));
   EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_2));
 
   oauth2_service_delegate_->RevokeAllCredentials();
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 1a41841..056c8e41f 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -423,7 +423,8 @@
   InitWithDefaultFeatures();
 
   GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001"),
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
        CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
   ASSERT_TRUE(SetupSync());
@@ -442,7 +443,8 @@
 
   // Put some completely new data in the sync server.
   GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"new-card", /*last_four=*/"0002"),
+      {CreateSyncWalletCard(/*name=*/"new-card", /*last_four=*/"0002",
+                            kDefaultBillingAddressID),
        CreateSyncWalletAddress(/*name=*/"new-address", /*company=*/"Company-2"),
        CreateSyncPaymentsCustomerData(/*customer_id=*/"newid")});
 
@@ -471,7 +473,8 @@
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTest, EmptyUpdatesAreIgnored) {
   InitWithDefaultFeatures();
   GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001"),
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
        CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
   ASSERT_TRUE(SetupSync());
diff --git a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
index a508434c..34c28e18 100644
--- a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/sync/test/integration/wallet_helper.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/common/autofill_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -18,11 +19,20 @@
 using wallet_helper::CreateSyncWalletAddress;
 using wallet_helper::CreateSyncWalletCard;
 using wallet_helper::GetCreditCard;
+using wallet_helper::GetLocalProfiles;
 using wallet_helper::GetServerCreditCards;
 using wallet_helper::GetServerProfiles;
+using wallet_helper::kDefaultBillingAddressID;
 using wallet_helper::UpdateServerAddressMetadata;
 using wallet_helper::UpdateServerCardMetadata;
 
+const char kDifferentBillingAddressId[] = "another address entity ID";
+constexpr char kLocalBillingAddressId[] =
+    "local billing address ID has size 36";
+static_assert(sizeof(kLocalBillingAddressId) == autofill::kLocalGuidSize + 1,
+              "|kLocalBillingAddressId| has to have the right length to be "
+              "considered a local guid");
+
 class TwoClientWalletSyncTest : public UssWalletSwitchToggler, public SyncTest {
  public:
   TwoClientWalletSyncTest() : SyncTest(TWO_CLIENT) {}
@@ -47,7 +57,8 @@
   InitWithDefaultFeatures();
 
   GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001"),
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
        CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
   ASSERT_TRUE(SetupSync());
@@ -79,6 +90,199 @@
   EXPECT_EQ(new_use_date, credit_cards[0]->use_date());
 }
 
+IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest, UpdateServerAddressMetadata) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
+       CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Grab the current address on the first client.
+  std::vector<AutofillProfile*> server_addresses = GetServerProfiles(0);
+  ASSERT_EQ(1u, server_addresses.size());
+  AutofillProfile address = *server_addresses[0];
+
+  // Simulate using it -- increase both its use count and use date.
+  ASSERT_EQ(1u, address.use_count());
+  address.set_use_count(2);
+  base::Time new_use_date = base::Time::Now();
+  ASSERT_NE(new_use_date, address.use_date());
+  address.set_use_date(new_use_date);
+  UpdateServerAddressMetadata(0, address);
+
+  // Wait for the change to propagate.
+  EXPECT_TRUE(AutofillWalletChecker(0, 1).Wait());
+
+  server_addresses = GetServerProfiles(1);
+  EXPECT_EQ(1U, server_addresses.size());
+  EXPECT_EQ(2u, server_addresses[0]->use_count());
+  EXPECT_EQ(new_use_date, server_addresses[0]->use_date());
+
+  server_addresses = GetServerProfiles(0);
+  EXPECT_EQ(1U, server_addresses.size());
+  EXPECT_EQ(2u, server_addresses[0]->use_count());
+  EXPECT_EQ(new_use_date, server_addresses[0]->use_date());
+}
+
+IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest,
+                       UpdateCreditCardMetadataWithNewBillingAddressId) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            /*billing_address_id=*/""),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Grab the current card on the first client.
+  std::vector<CreditCard*> credit_cards = GetServerCreditCards(0);
+  ASSERT_EQ(1U, credit_cards.size());
+  CreditCard card = *credit_cards[0];
+  ASSERT_TRUE(card.billing_address_id().empty());
+
+  // Update the billing address.
+  card.set_billing_address_id(kDefaultBillingAddressID);
+  UpdateServerCardMetadata(0, card);
+  EXPECT_TRUE(AutofillWalletChecker(0, 1).Wait());
+
+  // Make sure both clients have the updated billing_address_id.
+  credit_cards = GetServerCreditCards(1);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kDefaultBillingAddressID, credit_cards[0]->billing_address_id());
+
+  credit_cards = GetServerCreditCards(0);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kDefaultBillingAddressID, credit_cards[0]->billing_address_id());
+}
+
+IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest,
+                       UpdateCreditCardMetadataWithChangedBillingAddressId) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Grab the current card on the first client.
+  std::vector<CreditCard*> credit_cards = GetServerCreditCards(0);
+  ASSERT_EQ(1U, credit_cards.size());
+  CreditCard card = *credit_cards[0];
+
+  // Update the billing address.
+  ASSERT_EQ(kDefaultBillingAddressID, card.billing_address_id());
+  card.set_billing_address_id(kDifferentBillingAddressId);
+  UpdateServerCardMetadata(0, card);
+  EXPECT_TRUE(AutofillWalletChecker(0, 1).Wait());
+
+  // Make sure both clients have the updated billing_address_id.
+  credit_cards = GetServerCreditCards(1);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kDifferentBillingAddressId, credit_cards[0]->billing_address_id());
+
+  credit_cards = GetServerCreditCards(0);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kDifferentBillingAddressId, credit_cards[0]->billing_address_id());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    TwoClientWalletSyncTest,
+    UpdateCreditCardMetadataWithChangedBillingAddressId_RemoteToLocal) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Grab the current card on the first client.
+  std::vector<CreditCard*> credit_cards = GetServerCreditCards(0);
+  ASSERT_EQ(1U, credit_cards.size());
+  CreditCard card = *credit_cards[0];
+  ASSERT_EQ(kDefaultBillingAddressID, card.billing_address_id());
+
+  // Update the billing address (replace a remote profile by a local profile).
+  card.set_billing_address_id(kLocalBillingAddressId);
+  UpdateServerCardMetadata(0, card);
+  EXPECT_TRUE(AutofillWalletChecker(0, 1).Wait());
+
+  // Make sure both clients have the updated billing_address_id (local profile
+  // wins).
+  credit_cards = GetServerCreditCards(1);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kLocalBillingAddressId, credit_cards[0]->billing_address_id());
+
+  credit_cards = GetServerCreditCards(0);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kLocalBillingAddressId, credit_cards[0]->billing_address_id());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    TwoClientWalletSyncTest,
+    UpdateCreditCardMetadataWithChangedBillingAddressId_LocalToRemote) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kLocalBillingAddressId),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Grab the current card on the first client.
+  std::vector<CreditCard*> credit_cards = GetServerCreditCards(0);
+  ASSERT_EQ(1U, credit_cards.size());
+  CreditCard card = *credit_cards[0];
+
+  // Update the billing address (replace a local profile by a remote profile).
+  ASSERT_EQ(kLocalBillingAddressId, card.billing_address_id());
+  card.set_billing_address_id(kDifferentBillingAddressId);
+  UpdateServerCardMetadata(0, card);
+  EXPECT_TRUE(AutofillWalletChecker(0, 1).Wait());
+
+  // Make sure both clients have the original billing_address_id (local profile
+  // wins).
+  credit_cards = GetServerCreditCards(1);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kLocalBillingAddressId, credit_cards[0]->billing_address_id());
+
+  credit_cards = GetServerCreditCards(0);
+  EXPECT_EQ(1U, credit_cards.size());
+  EXPECT_EQ(kLocalBillingAddressId, credit_cards[0]->billing_address_id());
+}
+
+IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest,
+                       ServerAddressConvertsToSameLocalAddress) {
+  InitWithDefaultFeatures();
+
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  // Make sure both have has_converted true.
+  std::vector<AutofillProfile*> server_addresses = GetServerProfiles(0);
+  EXPECT_EQ(1u, server_addresses.size());
+  EXPECT_TRUE(server_addresses[0]->has_converted());
+
+  server_addresses = GetServerProfiles(1);
+  EXPECT_EQ(1U, server_addresses.size());
+  EXPECT_TRUE(server_addresses[0]->has_converted());
+
+  // Make sure they have the same local profile.
+  std::vector<AutofillProfile*> local_addresses = GetLocalProfiles(0);
+  EXPECT_EQ(1u, local_addresses.size());
+  const std::string& guid = local_addresses[0]->guid();
+
+  local_addresses = GetLocalProfiles(1);
+  EXPECT_EQ(1u, local_addresses.size());
+  EXPECT_EQ(guid, local_addresses[0]->guid());
+}
+
 INSTANTIATE_TEST_CASE_P(USS,
                         TwoClientWalletSyncTest,
                         ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index ef64a43..b7bdb0d 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -238,11 +238,14 @@
 }
 
 sync_pb::SyncEntity CreateDefaultSyncWalletCard() {
-  return CreateSyncWalletCard(kDefaultCardID, kDefaultCardLastFour);
+  return CreateSyncWalletCard(kDefaultCardID, kDefaultCardLastFour,
+                              kDefaultBillingAddressID);
 }
 
-sync_pb::SyncEntity CreateSyncWalletCard(const std::string& name,
-                                         const std::string& last_four) {
+sync_pb::SyncEntity CreateSyncWalletCard(
+    const std::string& name,
+    const std::string& last_four,
+    const std::string& billing_address_id) {
   sync_pb::SyncEntity entity;
   entity.set_name(name);
   entity.set_id_string(name);
@@ -263,7 +266,9 @@
   credit_card->set_name_on_card(kDefaultCardName);
   credit_card->set_status(sync_pb::WalletMaskedCreditCard::VALID);
   credit_card->set_type(kDefaultCardType);
-  credit_card->set_billing_address_id(kDefaultBillingAddressID);
+  if (!billing_address_id.empty()) {
+    credit_card->set_billing_address_id(billing_address_id);
+  }
   return entity;
 }
 
@@ -397,6 +402,12 @@
   return pdm->GetServerProfiles();
 }
 
+std::vector<AutofillProfile*> GetLocalProfiles(int profile) {
+  WaitForPDMToRefresh(profile);
+  PersonalDataManager* pdm = GetPersonalDataManager(profile);
+  return pdm->GetProfiles();
+}
+
 std::vector<CreditCard*> GetServerCreditCards(int profile) {
   WaitForPDMToRefresh(profile);
   PersonalDataManager* pdm = GetPersonalDataManager(profile);
diff --git a/chrome/browser/sync/test/integration/wallet_helper.h b/chrome/browser/sync/test/integration/wallet_helper.h
index d5263bf2..a566ff3 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.h
+++ b/chrome/browser/sync/test/integration/wallet_helper.h
@@ -65,7 +65,8 @@
 sync_pb::SyncEntity CreateDefaultSyncWalletCard();
 
 sync_pb::SyncEntity CreateSyncWalletCard(const std::string& name,
-                                         const std::string& last_four);
+                                         const std::string& last_four,
+                                         const std::string& billing_address_id);
 
 sync_pb::SyncEntity CreateSyncPaymentsCustomerData(
     const std::string& customer_id);
@@ -92,6 +93,7 @@
 
 // Load current data from the database of profile |profile|.
 std::vector<autofill::AutofillProfile*> GetServerProfiles(int profile);
+std::vector<autofill::AutofillProfile*> GetLocalProfiles(int profile);
 std::vector<autofill::CreditCard*> GetServerCreditCards(int profile);
 
 }  // namespace wallet_helper
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 617ed0e..3faf0698 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1389,8 +1389,6 @@
       "ash/multi_user/multi_user_window_manager_chromeos.h",
       "ash/multi_user/multi_user_window_manager_stub.cc",
       "ash/multi_user/multi_user_window_manager_stub.h",
-      "ash/multi_user/user_switch_animator_chromeos.cc",
-      "ash/multi_user/user_switch_animator_chromeos.h",
       "ash/network/data_promo_notification.cc",
       "ash/network/data_promo_notification.h",
       "ash/network/enrollment_dialog_view.cc",
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/launcher/DEPS
index 192ab8b..b870e12 100644
--- a/chrome/browser/ui/ash/launcher/DEPS
+++ b/chrome/browser/ui/ash/launcher/DEPS
@@ -7,6 +7,10 @@
   "arc_app_window_launcher_controller\.cc": [
     "+ash/shell.h",
   ],
+  # https://crbug.com/875111
+  "chrome_launcher_controller_unittest\.cc": [
+    "+ash/multi_user/multi_user_window_manager.h",
+  ],
   # https://crbug.com/887156
   "crostini_app_window_shelf_controller\.cc": [
     "+ash/shell.h",
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index ed06101..efe84d3 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "ash/display/display_configuration_controller.h"
+#include "ash/multi_user/multi_user_window_manager.h"
 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
@@ -1253,7 +1254,9 @@
     const std::string email_string = user_name + "@example.com";
     const AccountId account_id(AccountId::FromUserEmail(email_string));
     // Add a user to the fake user manager.
-    GetFakeUserManager()->AddUser(account_id);
+    auto* user = GetFakeUserManager()->AddUser(account_id);
+    ash_test_helper()->test_session_controller_client()->AddUserSession(
+        user->GetDisplayEmail());
 
     GetFakeUserManager()->LoginUser(account_id);
 
@@ -1273,12 +1276,9 @@
   // Switch to another user.
   void SwitchActiveUser(const AccountId& account_id) {
     GetFakeUserManager()->SwitchActiveUser(account_id);
-    MultiUserWindowManagerChromeOS* manager =
-        static_cast<MultiUserWindowManagerChromeOS*>(
-            MultiUserWindowManager::GetInstance());
-    manager->SetAnimationSpeedForTest(
-        MultiUserWindowManagerChromeOS::ANIMATION_SPEED_DISABLED);
-    manager->ActiveUserChanged(GetFakeUserManager()->FindUser(account_id));
+    ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+        ash::MultiUserWindowManager::ANIMATION_SPEED_DISABLED);
+    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(account_id);
     launcher_controller_->browser_status_monitor_for_test()->ActiveUserChanged(
         account_id.GetUserEmail());
 
diff --git a/chrome/browser/ui/ash/multi_user/DEPS b/chrome/browser/ui/ash/multi_user/DEPS
index 6829ed24..1a1ad789 100644
--- a/chrome/browser/ui/ash/multi_user/DEPS
+++ b/chrome/browser/ui/ash/multi_user/DEPS
@@ -1,14 +1,12 @@
 specific_include_rules = {
-  # mash-ok. For Shell::tablet_mode_controller() (!mash)
-  "multi_user_window_manager_chromeos\.cc": [
-    "+ash/shell.h",
-    "+ash/wm/tablet_mode/tablet_mode_controller.h",
-  ],
   # https://crbug.com/756085
-  "user_switch_animator_chromeos\.cc": [
-    "+ash/app_list/app_list_controller_impl.h",
-    "+ash/shell.h",
-    "+ash/wm/mru_window_tracker.h",
-    "+ash/wm/window_positioner.h",
+  "multi_user_window_manager_chromeos\.*": [
+    "+ash/multi_user/multi_user_window_manager.h",
+    "+ash/multi_user/multi_user_window_manager_delegate.h",
+  ],
+  # https://crbug.com/875111
+  "multi_user_window_manager_chromeos_unittest\.cc": [
+    "+ash/multi_user/user_switch_animator.h",
+    "+ash/session/session_controller.h",
   ],
 }
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc
index 1e0e655..5d44e7b1 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc
@@ -76,14 +76,14 @@
   multi_user_window_manager_ =
       new MultiUserWindowManagerChromeOS(AccountId::FromUserEmail("A"));
   multi_user_window_manager_->Init();
-  MultiUserWindowManager::SetInstanceForTest(multi_user_window_manager_);
+  ::MultiUserWindowManager::SetInstanceForTest(multi_user_window_manager_);
   EXPECT_TRUE(multi_user_window_manager_);
 }
 
 void MultiUserContextMenuChromeOSTest::TearDown() {
   delete window_;
 
-  MultiUserWindowManager::DeleteInstance();
+  ::MultiUserWindowManager::DeleteInstance();
   AshTestBase::TearDown();
 }
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager.h
index ff84e8d8..cc97703 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager.h
@@ -34,6 +34,14 @@
 //   clean automatically all references of that window upon destruction.
 // - User changes will be tracked via observer. No need to call.
 // - All child windows will be owned by the same owner as its parent.
+//
+// WARNING: in mash this code ends up referencing windows with an aura Env
+// of MUS *and* windows with an aura Env of LOCAL. This is because Arc/Crostini
+// windows are created by Ash, and browser/app windows are created by the
+// browser. Long term this code needs to be refactored out of chrome, at which
+// time *all* windows should be created by Ash. https://crbug.com/875111
+//
+// TODO(sky): rename this (and related classes).
 class MultiUserWindowManager {
  public:
   // Observer to notify of any window owner changes.
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
index f2c4cc33..7184725 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -7,21 +7,15 @@
 #include <set>
 #include <vector>
 
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/interfaces/ash_window_manager.mojom.h"
-#include "ash/shell.h"                                  // mash-ok
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"  // mash-ok
-#include "base/auto_reset.h"
-#include "base/macros.h"
+#include "ash/multi_user/multi_user_window_manager.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/media_client.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
-#include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
 #include "chrome/browser/ui/ash/session_controller_client.h"
 #include "chrome/browser/ui/ash/session_util.h"
 #include "chrome/browser/ui/browser.h"
@@ -32,32 +26,11 @@
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/aura/mus/window_mus.h"
-#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_types.h"
-#include "ui/events/event.h"
-#include "ui/views/mus/mus_client.h"
-#include "ui/wm/core/transient_window_manager.h"
-#include "ui/wm/core/window_animations.h"
-#include "ui/wm/core/window_util.h"
 
 namespace {
 
-// The animation time in milliseconds for a single window which is fading
-// in / out.
-const int kAnimationTimeMS = 100;
-
-// The animation time in milliseconds for the fade in and / or out when
-// switching users.
-const int kUserFadeTimeMS = 110;
-
-// The animation time in ms for a window which get teleported to another screen.
-const int kTeleportAnimationTimeMS = 300;
-
 // Used for UMA metrics. Do not reorder.
 enum TeleportWindowType {
   TELEPORT_WINDOW_BROWSER = 0,
@@ -107,58 +80,8 @@
                             NUM_TELEPORT_WINDOW_TYPES);
 }
 
-bool HasSystemModalTransientChildWindow(aura::Window* window) {
-  if (window == nullptr)
-    return false;
-
-  aura::Window* system_modal_container = window->GetRootWindow()->GetChildById(
-      ash::kShellWindowId_SystemModalContainer);
-  if (window->parent() == system_modal_container)
-    return true;
-
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it) {
-    if (HasSystemModalTransientChildWindow(*it))
-      return true;
-  }
-  return false;
-}
-
 }  // namespace
 
-// A class to temporarily change the animation properties for a window.
-class AnimationSetter {
- public:
-  AnimationSetter(aura::Window* window, int animation_time_in_ms)
-      : window_(window),
-        previous_animation_type_(wm::GetWindowVisibilityAnimationType(window_)),
-        previous_animation_time_(
-            wm::GetWindowVisibilityAnimationDuration(*window_)) {
-    wm::SetWindowVisibilityAnimationType(
-        window_, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-    wm::SetWindowVisibilityAnimationDuration(
-        window_, base::TimeDelta::FromMilliseconds(animation_time_in_ms));
-  }
-
-  ~AnimationSetter() {
-    wm::SetWindowVisibilityAnimationType(window_, previous_animation_type_);
-    wm::SetWindowVisibilityAnimationDuration(window_, previous_animation_time_);
-  }
-
- private:
-  // The window which gets used.
-  aura::Window* window_;
-
-  // Previous animation type.
-  const int previous_animation_type_;
-
-  // Previous animation time.
-  const base::TimeDelta previous_animation_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
-};
-
 // This class keeps track of all applications which were started for a user.
 // When an app gets created, the window will be tagged for that user. Note
 // that the destruction does not need to be tracked here since the universal
@@ -185,25 +108,13 @@
 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
     const AccountId& current_account_id)
     : current_account_id_(current_account_id),
-      suppress_visibility_changes_(false),
-      animation_speed_(ANIMATION_SPEED_NORMAL) {
-  if (features::IsUsingWindowService()) {
-    ash_window_manager_ =
-        views::MusClient::Get()
-            ->window_tree_client()
-            ->BindWindowManagerInterface<ash::mojom::AshWindowManager>();
-  }
-
-  if (TabletModeClient::Get())
-    tablet_mode_observer_.Add(TabletModeClient::Get());
-}
+      ash_multi_user_window_manager_(
+          std::make_unique<ash::MultiUserWindowManager>(this,
+                                                        current_account_id)) {}
 
 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
-  // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
-  // As such we should not try to finalize any outstanding user animations.
-  // Note that the destruction of the object can be done later.
-  if (animation_.get())
-    animation_->CancelAnimation();
+  // This may trigger callbacks to us, delete it early on.
+  ash_multi_user_window_manager_.reset();
 
   // Remove all window observers.
   WindowToEntryMap::iterator window = window_to_entry_.begin();
@@ -215,9 +126,6 @@
     window = window_to_entry_.begin();
   }
 
-  if (user_manager::UserManager::IsInitialized())
-    user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
-
   // Remove all app observers.
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   // might be nullptr in unit tests.
@@ -244,10 +152,6 @@
   DCHECK(account_id_to_app_observer_.find(current_account_id_) ==
          account_id_to_app_observer_.end());
 
-  // Add a session state observer to be able to monitor session changes.
-  if (user_manager::UserManager::IsInitialized())
-    user_manager::UserManager::Get()->AddSessionStateObserver(this);
-
   // The BrowserListObserver would have been better to use then the old
   // notification system, but that observer fires before the window got created.
   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
@@ -268,31 +172,23 @@
   DCHECK(account_id.is_valid());
   if (GetWindowOwner(window) == account_id)
     return;
+
   DCHECK(GetWindowOwner(window).empty());
   window_to_entry_[window] = new WindowEntry(account_id);
 
-  // Remember the initial visibility of the window.
-  window_to_entry_[window]->set_show(window->IsVisible());
+  ash_multi_user_window_manager_->SetWindowOwner(window, account_id);
 
   // Add observers to track state changes.
   window->AddObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
 
   // Check if this window was created due to a user interaction. If it was,
   // transfer it to the current user.
   if (window->GetProperty(aura::client::kCreatedByUserGesture))
     window_to_entry_[window]->set_show_for_user(current_account_id_);
 
-  // Add all transient children to our set of windows. Note that the function
-  // will add the children but not the owner to the transient children map.
-  AddTransientOwnerRecursive(window, window);
-
   // Notify entry adding.
   for (Observer& observer : observers_)
     observer.OnOwnerEntryAdded(window);
-
-  if (!IsWindowOnDesktopOfUser(window, current_account_id_))
-    SetWindowVisibility(window, false, 0);
 }
 
 const AccountId& MultiUserWindowManagerChromeOS::GetWindowOwner(
@@ -304,17 +200,7 @@
 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
     aura::Window* window,
     const AccountId& account_id) {
-  const AccountId previous_owner(GetUserPresentingWindow(window));
-  if (!ShowWindowForUserIntern(window, account_id))
-    return;
-  // The window switched to a new desktop and we have to switch to that desktop,
-  // but only when it was on the visible desktop and the the target is not the
-  // visible desktop.
-  if (account_id == current_account_id_ ||
-      previous_owner != current_account_id_)
-    return;
-
-  SessionControllerClient::DoSwitchActiveUser(account_id);
+  ash_multi_user_window_manager_->ShowWindowForUser(window, account_id);
 }
 
 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const {
@@ -391,108 +277,12 @@
   observers_.RemoveObserver(observer);
 }
 
-void MultiUserWindowManagerChromeOS::ActiveUserChanged(
-    const user_manager::User* active_user) {
-  // This needs to be set before the animation starts.
-  current_account_id_ = active_user->GetAccountId();
-
-  // Here to avoid a very nasty race condition, we must destruct any previously
-  // created animation before creating a new one. Otherwise, the newly
-  // constructed will hide all windows of the old user in the first step of the
-  // animation only to be reshown again by the destructor of the old animation.
-  animation_.reset();
-  animation_.reset(new UserSwitchAnimatorChromeOS(
-      this, current_account_id_,
-      GetAdjustedAnimationTimeInMS(kUserFadeTimeMS)));
-
-  // Call RequestCaptureState here instead of having MediaClient observe
-  // ActiveUserChanged because it must happen after
-  // MultiUserWindowManagerChromeOS is notified. The MediaClient may be null in
-  // tests.
-  if (MediaClient::Get())
-    MediaClient::Get()->RequestCaptureState();
-}
-
 void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) {
-  if (GetWindowOwner(window).empty()) {
-    // This must be a window in the transient chain - remove it and its
-    // children from the owner.
-    RemoveTransientOwnerRecursive(window);
-    return;
-  }
-  wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
   // Remove the window from the owners list.
   delete window_to_entry_[window];
   window_to_entry_.erase(window);
 }
 
-void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging(
-    aura::Window* window,
-    bool visible) {
-  // This command gets called first and immediately when show or hide gets
-  // called. We remember here the desired state for restoration IF we were
-  // not ourselves issuing the call.
-  // Note also that using the OnWindowVisibilityChanged callback cannot be
-  // used for this.
-  if (suppress_visibility_changes_)
-    return;
-
-  WindowToEntryMap::iterator it = window_to_entry_.find(window);
-  // If the window is not owned by anyone it is shown on all desktops.
-  if (it != window_to_entry_.end()) {
-    // Remember what was asked for so that we can restore this when the user's
-    // desktop gets restored.
-    it->second->set_show(visible);
-  } else {
-    TransientWindowToVisibility::iterator it =
-        transient_window_to_visibility_.find(window);
-    if (it != transient_window_to_visibility_.end())
-      it->second = visible;
-  }
-}
-
-void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged(
-    aura::Window* window,
-    bool visible) {
-  if (suppress_visibility_changes_)
-    return;
-
-  // Don't allow to make the window visible if it shouldn't be.
-  if (visible && !IsWindowOnDesktopOfUser(window, current_account_id_)) {
-    SetWindowVisibility(window, false, 0);
-    return;
-  }
-  aura::Window* owned_parent = GetOwningWindowInTransientChain(window);
-  if (owned_parent && owned_parent != window && visible &&
-      !IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
-    SetWindowVisibility(window, false, 0);
-}
-
-void MultiUserWindowManagerChromeOS::OnTransientChildAdded(
-    aura::Window* window,
-    aura::Window* transient_window) {
-  if (!GetWindowOwner(window).empty()) {
-    AddTransientOwnerRecursive(transient_window, window);
-    return;
-  }
-  aura::Window* owned_parent =
-      GetOwningWindowInTransientChain(transient_window);
-  if (!owned_parent)
-    return;
-
-  AddTransientOwnerRecursive(transient_window, owned_parent);
-}
-
-void MultiUserWindowManagerChromeOS::OnTransientChildRemoved(
-    aura::Window* window,
-    aura::Window* transient_window) {
-  // Remove the transient child if the window itself is owned, or one of the
-  // windows in its transient parents chain.
-  if (!GetWindowOwner(window).empty() ||
-      GetOwningWindowInTransientChain(window))
-    RemoveTransientOwnerRecursive(transient_window);
-}
-
 void MultiUserWindowManagerChromeOS::Observe(
     int type,
     const content::NotificationSource& source,
@@ -501,131 +291,63 @@
   AddBrowserWindow(content::Source<Browser>(source).ptr());
 }
 
-void MultiUserWindowManagerChromeOS::OnTabletModeToggled(bool enabled) {
-  if (!enabled)
+void MultiUserWindowManagerChromeOS::OnOwnerEntryChanged(
+    aura::Window* window,
+    const AccountId& account_id,
+    bool was_minimized,
+    bool teleported) {
+  WindowToEntryMap::iterator it = window_to_entry_.find(window);
+  if (it == window_to_entry_.end())
     return;
 
-  for (auto entry : window_to_entry_) {
-    aura::Window* window = entry.first;
-    if (ash_window_manager_) {
-      ash_window_manager_->AddWindowToTabletMode(
-          aura::WindowMus::Get(window)->server_id());
+  if (was_minimized)
+    RecordUMAForTransferredWindowType(window);
+
+  it->second->set_show_for_user(account_id);
+
+  const AccountId& owner = GetWindowOwner(window);
+  if (owner.is_valid()) {
+    const user_manager::User* const window_owner =
+        user_manager::UserManager::IsInitialized()
+            ? user_manager::UserManager::Get()->FindUser(owner)
+            : nullptr;
+    if (window_owner && teleported) {
+      window->SetProperty(
+          aura::client::kAvatarIconKey,
+          new gfx::ImageSkia(GetAvatarImageForUser(window_owner)));
     } else {
-      ash::Shell::Get()->tablet_mode_controller()->AddWindow(window);
+      window->ClearProperty(aura::client::kAvatarIconKey);
     }
   }
+
+  for (Observer& observer : observers_)
+    observer.OnOwnerEntryChanged(window);
+}
+
+void MultiUserWindowManagerChromeOS::OnWillSwitchActiveAccount(
+    const AccountId& account_id) {
+  current_account_id_ = account_id;
+}
+
+void MultiUserWindowManagerChromeOS::OnTransitionUserShelfToNewAccount() {
+  ChromeLauncherController* chrome_launcher_controller =
+      ChromeLauncherController::instance();
+  // Some unit tests have no ChromeLauncherController.
+  if (chrome_launcher_controller) {
+    chrome_launcher_controller->ActiveUserChanged(
+        current_account_id_.GetUserEmail());
+  }
 }
 
-void MultiUserWindowManagerChromeOS::SetAnimationSpeedForTest(
-    MultiUserWindowManagerChromeOS::AnimationSpeed speed) {
-  animation_speed_ = speed;
-}
-
-bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() {
-  return animation_ && !animation_->IsAnimationFinished();
+void MultiUserWindowManagerChromeOS::OnDidSwitchActiveAccount() {
+  for (Observer& observer : observers_)
+    observer.OnUserSwitchAnimationFinished();
 }
 
 const AccountId& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() const {
   return current_account_id_;
 }
 
-bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern(
-    aura::Window* window,
-    const AccountId& account_id) {
-  // If there is either no owner, or the owner is the current user, no action
-  // is required.
-  const AccountId& owner = GetWindowOwner(window);
-  if ((!owner.is_valid()) ||
-      (owner == account_id && IsWindowOnDesktopOfUser(window, account_id)))
-    return false;
-
-  bool minimized = wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED);
-  // Check that we are not trying to transfer ownership of a minimized window.
-  if (account_id != owner && minimized)
-    return false;
-
-  if (!minimized) {
-    // If the window was transferred without getting minimized, we should record
-    // the window type. Otherwise it falls back to the original desktop.
-    RecordUMAForTransferredWindowType(window);
-  }
-
-  WindowToEntryMap::iterator it = window_to_entry_.find(window);
-  it->second->set_show_for_user(account_id);
-
-  // Tests could either not have a UserManager or the UserManager does not
-  // know the window owner.
-  const user_manager::User* const window_owner =
-      user_manager::UserManager::IsInitialized()
-          ? user_manager::UserManager::Get()->FindUser(owner)
-          : nullptr;
-
-  const bool teleported = !IsWindowOnDesktopOfUser(window, owner);
-  if (window_owner && teleported) {
-    window->SetProperty(
-        aura::client::kAvatarIconKey,
-        new gfx::ImageSkia(GetAvatarImageForUser(window_owner)));
-  } else {
-    window->ClearProperty(aura::client::kAvatarIconKey);
-  }
-
-  // Show the window if the added user is the current one.
-  if (account_id == current_account_id_) {
-    // Only show the window if it should be shown according to its state.
-    if (it->second->show())
-      SetWindowVisibility(window, true, kTeleportAnimationTimeMS);
-  } else {
-    SetWindowVisibility(window, false, kTeleportAnimationTimeMS);
-  }
-
-  // Notify entry change.
-  for (Observer& observer : observers_)
-    observer.OnOwnerEntryChanged(window);
-  return true;
-}
-
-void MultiUserWindowManagerChromeOS::SetWindowVisibility(
-    aura::Window* window,
-    bool visible,
-    int animation_time_in_ms) {
-  if (window->IsVisible() == visible)
-    return;
-
-  // Hiding a system modal dialog should not be allowed. Instead we switch to
-  // the user which is showing the system modal window.
-  // Note that in some cases (e.g. unit test) windows might not have a root
-  // window.
-  if (!visible && window->GetRootWindow()) {
-    if (HasSystemModalTransientChildWindow(window)) {
-      // The window is system modal and we need to find the parent which owns
-      // it so that we can switch to the desktop accordingly.
-      AccountId account_id = GetUserPresentingWindow(window);
-      if (!account_id.is_valid()) {
-        aura::Window* owning_window = GetOwningWindowInTransientChain(window);
-        DCHECK(owning_window);
-        account_id = GetUserPresentingWindow(owning_window);
-        DCHECK(account_id.is_valid());
-      }
-      SessionControllerClient::DoSwitchActiveUser(account_id);
-      return;
-    }
-  }
-
-  // To avoid that these commands are recorded as any other commands, we are
-  // suppressing any window entry changes while this is going on.
-  base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
-
-  if (visible)
-    ShowWithTransientChildrenRecursive(window, animation_time_in_ms);
-  else
-    SetWindowVisible(window, false, animation_time_in_ms);
-}
-
-void MultiUserWindowManagerChromeOS::NotifyAfterUserSwitchAnimationFinished() {
-  for (Observer& observer : observers_)
-    observer.OnUserSwitchAnimationFinished();
-}
-
 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) {
   // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
   // come here with no valid window.
@@ -634,127 +356,3 @@
   SetWindowOwner(browser->window()->GetNativeWindow(),
                  multi_user_util::GetAccountIdFromProfile(browser->profile()));
 }
-
-void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
-    aura::Window* window,
-    int animation_time_in_ms) {
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    ShowWithTransientChildrenRecursive(*it, animation_time_in_ms);
-
-  // We show all children which were not explicitly hidden.
-  TransientWindowToVisibility::iterator it2 =
-      transient_window_to_visibility_.find(window);
-  if (it2 == transient_window_to_visibility_.end() || it2->second)
-    SetWindowVisible(window, true, animation_time_in_ms);
-}
-
-aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
-    aura::Window* window) const {
-  if (!GetWindowOwner(window).empty())
-    return NULL;
-  aura::Window* parent = wm::GetTransientParent(window);
-  while (parent) {
-    if (!GetWindowOwner(parent).empty())
-      return parent;
-    parent = wm::GetTransientParent(parent);
-  }
-  return NULL;
-}
-
-void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
-    aura::Window* window,
-    aura::Window* owned_parent) {
-  // First add all child windows.
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    AddTransientOwnerRecursive(*it, owned_parent);
-
-  // If this window is the owned window, we do not have to handle it again.
-  if (window == owned_parent)
-    return;
-
-  // Remember the current visibility.
-  DCHECK(transient_window_to_visibility_.find(window) ==
-         transient_window_to_visibility_.end());
-  transient_window_to_visibility_[window] = window->IsVisible();
-
-  // Add observers to track state changes.
-  window->AddObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->AddObserver(this);
-
-  // Hide the window if it should not be shown. Note that this hide operation
-  // will hide recursively this and all children - but we have already collected
-  // their initial view state.
-  if (!IsWindowOnDesktopOfUser(owned_parent, current_account_id_))
-    SetWindowVisibility(window, false, kAnimationTimeMS);
-}
-
-void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
-    aura::Window* window) {
-  // First remove all child windows.
-  aura::Window::Windows::const_iterator it =
-      wm::GetTransientChildren(window).begin();
-  for (; it != wm::GetTransientChildren(window).end(); ++it)
-    RemoveTransientOwnerRecursive(*it);
-
-  // Find from transient window storage the visibility for the given window,
-  // set the visibility accordingly and delete the window from the map.
-  TransientWindowToVisibility::iterator visibility_item =
-      transient_window_to_visibility_.find(window);
-  DCHECK(visibility_item != transient_window_to_visibility_.end());
-
-  window->RemoveObserver(this);
-  wm::TransientWindowManager::GetOrCreate(window)->RemoveObserver(this);
-
-  bool unowned_view_state = visibility_item->second;
-  transient_window_to_visibility_.erase(visibility_item);
-  if (unowned_view_state && !window->IsVisible()) {
-    // To prevent these commands from being recorded as any other commands, we
-    // are suppressing any window entry changes while this is going on.
-    // Instead of calling SetWindowVisible, only show gets called here since all
-    // dependents have been shown previously already.
-    base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
-    window->Show();
-  }
-}
-
-void MultiUserWindowManagerChromeOS::SetWindowVisible(
-    aura::Window* window,
-    bool visible,
-    int animation_time_in_ms) {
-  // The TabletModeWindowManager will not handle invisible windows since they
-  // are not user activatable. Since invisible windows are not being tracked,
-  // we tell it to maximize / track this window now before it gets shown, to
-  // reduce animation jank from multiple resizes.
-  if (visible) {
-    if (ash_window_manager_) {
-      ash_window_manager_->AddWindowToTabletMode(
-          aura::WindowMus::Get(window)->server_id());
-    } else {
-      ash::Shell::Get()->tablet_mode_controller()->AddWindow(window);
-    }
-  }
-
-  // Under mash we apply visibility changes to the root so both the window frame
-  // and contents hide together.
-  if (features::IsUsingWindowService())
-    window = window->GetRootWindow();
-
-  AnimationSetter animation_setter(
-      window, GetAdjustedAnimationTimeInMS(animation_time_in_ms));
-
-  if (visible)
-    window->Show();
-  else
-    window->Hide();
-}
-
-int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS(
-    int default_time_in_ms) const {
-  return animation_speed_ == ANIMATION_SPEED_NORMAL
-             ? default_time_in_ms
-             : (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0);
-}
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
index 4cb93c39..1db57996 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
@@ -9,23 +9,23 @@
 #include <memory>
 #include <string>
 
-#include "ash/public/interfaces/ash_window_manager.mojom.h"
-#include "base/compiler_specific.h"
+#include "ash/multi_user/multi_user_window_manager_delegate.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "base/scoped_observer.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
-#include "chrome/browser/ui/ash/tablet_mode_client.h"
-#include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
 #include "components/account_id/account_id.h"
-#include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "ui/aura/window_observer.h"
-#include "ui/wm/core/transient_window_observer.h"
 
+class AppObserver;
 class Browser;
 
+namespace ash {
+class MultiUserWindowManager;
+class MultiUserWindowManagerChromeOSTest;
+}  // namespace ash
+
 namespace content {
 class BrowserContext;
 }
@@ -34,41 +34,20 @@
 class Window;
 }  // namespace aura
 
-namespace ash {
-class MultiUserWindowManagerChromeOSTest;
-}  // namespace ash
-
-class AppObserver;
-class UserSwitchAnimatorChromeOS;
-
-// This ChromeOS implementation of the MultiUserWindowManager interface is
-// detecting app and browser creations, tagging their windows automatically and
-// using (currently) show and hide to make the owned windows visible - or not.
-// If it becomes necessary, the function |SetWindowVisibility| can be
-// overwritten to match new ways of doing this.
-// Note:
-// - aura::Window::Hide() is currently hiding the window and all owned transient
-//   children. However aura::Window::Show() is only showing the window itself.
-//   To address that, all transient children (and their children) are remembered
-//   in |transient_window_to_visibility_| and monitored to keep track of the
-//   visibility changes from the owning user. This way the visibility can be
-//   changed back to its requested state upon showing by us - or when the window
-//   gets detached from its current owning parent.
+// This class is responsible for notifying Ash's MultiUserWindowManager of
+// which aura::Windows are associated with which accounts. This class caches
+// similar information to that of Ash's MultiUserWindowManager so that it can
+// synchronously respond to requests for state (such as
+// IsWindowOnDesktopOfUser()).
+//
+// TODO: don't use ash::MultiUserWindowManager directly, instead use it over
+// a mojom. https://crbug.com/875111.
 class MultiUserWindowManagerChromeOS
     : public MultiUserWindowManager,
-      public user_manager::UserManager::UserSessionStateObserver,
+      public ash::MultiUserWindowManagerDelegate,
       public aura::WindowObserver,
-      public content::NotificationObserver,
-      public wm::TransientWindowObserver,
-      public TabletModeClientObserver {
+      public content::NotificationObserver {
  public:
-  // The speed which should be used to perform animations.
-  enum AnimationSpeed {
-    ANIMATION_SPEED_NORMAL,   // The normal animation speed.
-    ANIMATION_SPEED_FAST,     // Unit test speed which test animations.
-    ANIMATION_SPEED_DISABLED  // Unit tests which do not require animations.
-  };
-
   // Create the manager and use |active_account_id| as the active user.
   explicit MultiUserWindowManagerChromeOS(const AccountId& active_account_id);
   ~MultiUserWindowManagerChromeOS() override;
@@ -92,33 +71,22 @@
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
 
-  // user_manager::UserManager::UserSessionStateObserver overrides:
-  void ActiveUserChanged(const user_manager::User* active_user) override;
-
   // WindowObserver overrides:
   void OnWindowDestroyed(aura::Window* window) override;
-  void OnWindowVisibilityChanging(aura::Window* window, bool visible) override;
-  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
-
-  // TransientWindowObserver overrides:
-  void OnTransientChildAdded(aura::Window* window,
-                             aura::Window* transient) override;
-  void OnTransientChildRemoved(aura::Window* window,
-                               aura::Window* transient) override;
 
   // content::NotificationObserver overrides:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // TabletModeClientObserver:
-  void OnTabletModeToggled(bool enabled) override;
-
-  // Disable any animations for unit tests.
-  void SetAnimationSpeedForTest(AnimationSpeed speed);
-
-  // Returns true when a user switch animation is running. For unit tests.
-  bool IsAnimationRunningForTest();
+  // ash::MultiUserWindowManagerDelegate overrides:
+  void OnOwnerEntryChanged(aura::Window* window,
+                           const AccountId& account_id,
+                           bool was_minimized,
+                           bool teleported) override;
+  void OnWillSwitchActiveAccount(const AccountId& account_id) override;
+  void OnTransitionUserShelfToNewAccount() override;
+  void OnDidSwitchActiveAccount() override;
 
   // Returns the current user for unit tests.
   const AccountId& GetCurrentUserForTest() const;
@@ -129,8 +97,8 @@
   class WindowEntry {
    public:
     explicit WindowEntry(const AccountId& account_id)
-        : owner_(account_id), show_for_user_(account_id), show_(true) {}
-    virtual ~WindowEntry() {}
+        : owner_(account_id), show_for_user_(account_id) {}
+    ~WindowEntry() {}
 
     // Returns the owner of this window. This cannot be changed.
     const AccountId& owner() const { return owner_; }
@@ -138,18 +106,12 @@
     // Returns the user for which this should be shown.
     const AccountId& show_for_user() const { return show_for_user_; }
 
-    // Returns if the window should be shown for the "show user" or not.
-    bool show() const { return show_; }
-
     // Set the user which will display the window on the owned desktop. If
     // an empty user id gets passed the owner will be used.
     void set_show_for_user(const AccountId& account_id) {
       show_for_user_ = account_id.is_valid() ? account_id : owner_;
     }
 
-    // Sets if the window gets shown for the active user or not.
-    void set_show(bool show) { show_ = show; }
-
    private:
     // The user id of the owner of this window.
     const AccountId owner_;
@@ -157,32 +119,11 @@
     // The user id of the user on which desktop the window gets shown.
     AccountId show_for_user_;
 
-    // True if the window should be visible for the user which shows the window.
-    bool show_;
-
     DISALLOW_COPY_AND_ASSIGN(WindowEntry);
   };
 
-  typedef std::map<aura::Window*, WindowEntry*> WindowToEntryMap;
-
-  // Show a window for a user without switching the user.
-  // Returns true when the window moved to a new desktop.
-  bool ShowWindowForUserIntern(aura::Window* window,
-                               const AccountId& account_id);
-
-  // Show / hide the given window. Note: By not doing this within the functions,
-  // this allows to either switching to different ways to show/hide and / or to
-  // distinguish state changes performed by this class vs. state changes
-  // performed by the others. Note furthermore that system modal dialogs will
-  // not get hidden. We will switch instead to the owners desktop.
-  // The |animation_time_in_ms| is the time the animation should take. Set to 0
-  // if it should get set instantly.
-  void SetWindowVisibility(aura::Window* window,
-                           bool visible,
-                           int animation_time_in_ms);
-
-  // Notify the observers after the user switching animation is finished.
-  void NotifyAfterUserSwitchAnimationFinished();
+  // TODO(sky): make this map to unique_ptr<WindowEntry>.
+  using WindowToEntryMap = std::map<aura::Window*, WindowEntry*>;
 
   const WindowToEntryMap& window_to_entry() { return window_to_entry_; }
 
@@ -190,40 +131,10 @@
   friend class ash::MultiUserWindowManagerChromeOSTest;
 
   typedef std::map<AccountId, AppObserver*> AccountIdToAppWindowObserver;
-  typedef std::map<aura::Window*, bool> TransientWindowToVisibility;
 
   // Add a browser window to the system so that the owner can be remembered.
   void AddBrowserWindow(Browser* browser);
 
-  // Show the window and its transient children. However - if a transient child
-  // was turned invisible by some other operation, it will stay invisible.
-  // Use the given |animation_time_in_ms| for transitioning.
-  void ShowWithTransientChildrenRecursive(aura::Window* window,
-                                          int animation_time_in_ms);
-
-  // Find the first owned window in the chain.
-  // Returns NULL when the window itself is owned.
-  aura::Window* GetOwningWindowInTransientChain(aura::Window* window) const;
-
-  // A |window| and its children were attached as transient children to an
-  // |owning_parent| and need to be registered. Note that the |owning_parent|
-  // itself will not be registered, but its children will.
-  void AddTransientOwnerRecursive(aura::Window* window,
-                                  aura::Window* owning_parent);
-
-  // A window and its children were removed from its parent and can be
-  // unregistered.
-  void RemoveTransientOwnerRecursive(aura::Window* window);
-
-  // Animate a |window| to be |visible| in |animation_time_in_ms|.
-  void SetWindowVisible(aura::Window* window,
-                        bool visible,
-                        int aimation_time_in_ms);
-
-  // Get the animation time in milliseconds dependent on the |AnimationSpeed|
-  // from the passed |default_time_in_ms|.
-  int GetAdjustedAnimationTimeInMS(int default_time_in_ms) const;
-
   // A lookup to see to which user the given window belongs to, where and if it
   // should get shown.
   WindowToEntryMap window_to_entry_;
@@ -234,9 +145,6 @@
   // An observer list to be notified upon window owner changes.
   base::ObserverList<Observer>::Unchecked observers_;
 
-  // A map which remembers for owned transient windows their own visibility.
-  TransientWindowToVisibility transient_window_to_visibility_;
-
   // The currently selected active user. It is used to find the proper
   // visibility state in various cases. The state is stored here instead of
   // being read from the user manager to be in sync while a switch occurs.
@@ -245,20 +153,7 @@
   // The notification registrar to track the creation of browser windows.
   content::NotificationRegistrar registrar_;
 
-  // Suppress changes to the visibility flag while we are changing it ourselves.
-  bool suppress_visibility_changes_;
-
-  // The speed which is used to perform any animations.
-  AnimationSpeed animation_speed_;
-
-  // The animation between users.
-  std::unique_ptr<UserSwitchAnimatorChromeOS> animation_;
-
-  // Only used in mash.
-  ash::mojom::AshWindowManagerAssociatedPtr ash_window_manager_;
-
-  ScopedObserver<TabletModeClient, TabletModeClientObserver>
-      tablet_mode_observer_{this};
+  std::unique_ptr<ash::MultiUserWindowManager> ash_multi_user_window_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerChromeOS);
 };
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
index f73d0a0..1ff34c2 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
@@ -9,8 +9,11 @@
 
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
+#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
+#include "ash/session/session_controller.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -29,6 +32,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -39,7 +43,6 @@
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
-#include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
 #include "chrome/browser/ui/ash/session_controller_client.h"
 #include "chrome/browser/ui/ash/session_util.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
@@ -56,6 +59,7 @@
 #include "components/user_manager/user_info.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/env_test_helper.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/ui_base_types.h"
@@ -120,18 +124,25 @@
         fake_user_manager_(new chromeos::FakeChromeUserManager),
         user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {}
 
+  // AshTestBase:
   void SetUp() override;
   void TearDown() override;
 
  protected:
+  void SwitchActiveUser(const AccountId& id) {
+    fake_user_manager_->SwitchActiveUser(id);
+    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(id);
+  }
+
   // Set up the test environment for this many windows.
   void SetUpForThisManyWindows(int windows);
 
   // Switch the user and wait until the animation is finished.
   void SwitchUserAndWaitForAnimation(const AccountId& account_id) {
-    multi_user_window_manager_->ActiveUserChanged(EnsureTestUser(account_id));
+    EnsureTestUser(account_id);
+    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(account_id);
     base::TimeTicks now = base::TimeTicks::Now();
-    while (multi_user_window_manager_->IsAnimationRunningForTest()) {
+    while (ash::MultiUserWindowManager::Get()->IsAnimationRunningForTest()) {
       // This should never take longer then a second.
       ASSERT_GE(1000, (base::TimeTicks::Now() - now).InMilliseconds());
       base::RunLoop().RunUntilIdle();
@@ -162,8 +173,12 @@
   // Ensures that a user with the given |account_id| exists.
   const user_manager::User* EnsureTestUser(const AccountId& account_id) {
     const user_manager::User* user = fake_user_manager_->FindUser(account_id);
-    if (!user)
-      user = fake_user_manager_->AddUser(account_id);
+    if (user)
+      return user;
+
+    user = fake_user_manager_->AddUser(account_id);
+    ash_test_helper()->test_session_controller_client()->AddUserSession(
+        user->GetDisplayEmail());
     return user;
   }
 
@@ -197,51 +212,48 @@
 
   void ShowWindowForUserNoUserTransition(aura::Window* window,
                                          const AccountId& account_id) {
-    multi_user_window_manager_->ShowWindowForUserIntern(window, account_id);
+    ash::MultiUserWindowManager::Get()->ShowWindowForUserIntern(window,
+                                                                account_id);
   }
 
   // The FakeChromeUserManager does not automatically call the window
   // manager. This function gets the current user from it and also sets it to
   // the multi user window manager.
   AccountId GetAndValidateCurrentUserFromSessionStateObserver() {
-    const AccountId account_id =
-        user_manager()->GetActiveUser()->GetAccountId();
-    if (account_id != multi_user_window_manager_->GetCurrentUserForTest()) {
-      multi_user_window_manager()->ActiveUserChanged(
-          fake_user_manager_->FindUser(account_id));
-    }
-
-    return account_id;
+    SessionController* session_controller = Shell::Get()->session_controller();
+    session_controller->FlushMojoForTest();
+    return session_controller->GetUserSessions()[0]->user_info->account_id;
   }
 
   // Initiate a user transition.
   void StartUserTransitionAnimation(const AccountId& account_id) {
-    // Ensures a user exists for the ActiveUserChanged call but do not make the
-    // user as logged in. The tests that call StartUserTransitionAnimation do
-    // not need a logged in user. Otherwise, profile switch also needs to
-    // be simulated so that CanShowWindowForUser works correctly.
-    multi_user_window_manager_->ActiveUserChanged(EnsureTestUser(account_id));
+    EnsureTestUser(account_id);
+    ash_test_helper()->test_session_controller_client()->SwitchActiveUser(
+        account_id);
   }
 
   // Call next animation step.
   void AdvanceUserTransitionAnimation() {
-    multi_user_window_manager_->animation_->AdvanceUserTransitionAnimation();
+    ash::MultiUserWindowManager::Get()
+        ->animation_->AdvanceUserTransitionAnimation();
   }
 
   // Return the user id of the wallpaper which is currently set.
   const std::string& GetWallpaperUserIdForTest() {
-    return multi_user_window_manager_->animation_->wallpaper_user_id_for_test();
+    return ash::MultiUserWindowManager::Get()
+        ->animation_->wallpaper_user_id_for_test();
   }
 
   // Returns true if the given window covers the screen.
   bool CoversScreen(aura::Window* window) {
-    return UserSwitchAnimatorChromeOS::CoversScreen(window);
+    return ash::UserSwitchAnimator::CoversScreen(window);
   }
 
  private:
   chromeos::ScopedStubInstallAttributes test_install_attributes_;
 
   // These get created for each session.
+  // TODO: convert to vector<std::unique_ptr<aura::Window>>.
   aura::Window::Windows windows_;
 
   // The instance of the MultiUserWindowManager.
@@ -270,6 +282,10 @@
       TestingBrowserProcess::GetGlobal()->local_state());
   ash_test_helper()->set_test_shell_delegate(new TestShellDelegateChromeOS);
   AshTestBase::SetUp();
+  // This test has mixed case user ids.
+  ash_test_helper()
+      ->test_session_controller_client()
+      ->set_use_lower_case_user_id(false);
   profile_manager_.reset(
       new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
   ASSERT_TRUE(profile_manager_.get()->SetUp());
@@ -287,9 +303,9 @@
   multi_user_window_manager_ =
       new MultiUserWindowManagerChromeOS(AccountId::FromUserEmail("A"));
   multi_user_window_manager_->Init();
-  multi_user_window_manager_->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_DISABLED);
-  MultiUserWindowManager::SetInstanceForTest(multi_user_window_manager_);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_DISABLED);
+  ::MultiUserWindowManager::SetInstanceForTest(multi_user_window_manager_);
   EXPECT_TRUE(multi_user_window_manager_);
   wallpaper_controller_client_ = std::make_unique<WallpaperControllerClient>();
   wallpaper_controller_client_->InitForTesting(
@@ -304,7 +320,7 @@
     windows_.erase(windows_.begin());
   }
 
-  MultiUserWindowManager::DeleteInstance();
+  ::MultiUserWindowManager::DeleteInstance();
   AshTestBase::TearDown();
   wallpaper_controller_client_.reset();
   profile_manager_.reset();
@@ -353,7 +369,8 @@
   // Check the basic assumptions: All windows are visible and there is no owner.
   EXPECT_EQ("S[], S[], S[]", GetStatus());
   EXPECT_TRUE(multi_user_window_manager());
-  EXPECT_EQ(multi_user_window_manager(), MultiUserWindowManager::GetInstance());
+  EXPECT_EQ(multi_user_window_manager(),
+            ::MultiUserWindowManager::GetInstance());
   EXPECT_FALSE(multi_user_window_manager()->AreWindowsSharedAmongUsers());
 
   const AccountId account_id_A(AccountId::FromUserEmail("A"));
@@ -830,7 +847,6 @@
   EXPECT_FALSE(wm::GetWindowState(window(1))->IsMaximized());
 
   Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
-  multi_user_window_manager()->OnTabletModeToggled(true);
 
   EXPECT_TRUE(wm::GetWindowState(window(0))->IsMaximized());
   EXPECT_TRUE(wm::GetWindowState(window(1))->IsMaximized());
@@ -838,7 +854,6 @@
   // Tests that on exiting tablet mode, the window states return to not
   // maximized.
   Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false);
-  multi_user_window_manager()->OnTabletModeToggled(false);
   EXPECT_FALSE(wm::GetWindowState(window(0))->IsMaximized());
   EXPECT_FALSE(wm::GetWindowState(window(1))->IsMaximized());
 }
@@ -851,15 +866,15 @@
   const AccountId account_id_a(AccountId::FromUserEmail("a"));
   const AccountId account_id_b(AccountId::FromUserEmail("b"));
 
-  SessionControllerClient::DoSwitchActiveUser(account_id_a);
+  StartUserTransitionAnimation(account_id_a);
 
   // Making the window system modal should not change anything.
   MakeWindowSystemModal(window(0));
-  EXPECT_EQ(account_id_a, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver());
 
   // Making the window owned by user B should switch users.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_b);
-  EXPECT_EQ(account_id_b, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver());
 }
 
 // Test that a system modal dialog will not switch desktop if active user has
@@ -870,15 +885,15 @@
   const AccountId account_id_a(AccountId::FromUserEmail("a"));
   const AccountId account_id_b(AccountId::FromUserEmail("b"));
 
-  SessionControllerClient::DoSwitchActiveUser(account_id_a);
+  StartUserTransitionAnimation(account_id_a);
 
   // Making the window system modal should not change anything.
   MakeWindowSystemModal(window(0));
-  EXPECT_EQ(account_id_a, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver());
 
   // Making the window owned by user a should not switch users.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_a);
-  EXPECT_EQ(account_id_a, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver());
 }
 
 // Test that a system modal dialog will not switch if shown on correct desktop
@@ -890,7 +905,7 @@
   const AccountId account_id_a(AccountId::FromUserEmail("a"));
   const AccountId account_id_b(AccountId::FromUserEmail("b"));
 
-  SessionControllerClient::DoSwitchActiveUser(account_id_a);
+  StartUserTransitionAnimation(account_id_a);
 
   window(0)->Hide();
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_b);
@@ -898,7 +913,7 @@
   MakeWindowSystemModal(window(0));
   // Showing the window should trigger no user switch.
   window(0)->Show();
-  EXPECT_EQ(account_id_a, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver());
 }
 
 // Test that a system modal dialog will switch if shown on incorrect desktop but
@@ -910,7 +925,7 @@
   const AccountId account_id_a(AccountId::FromUserEmail("a"));
   const AccountId account_id_b(AccountId::FromUserEmail("b"));
 
-  SessionControllerClient::DoSwitchActiveUser(account_id_a);
+  StartUserTransitionAnimation(account_id_a);
 
   window(0)->Hide();
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_a);
@@ -918,7 +933,7 @@
   MakeWindowSystemModal(window(0));
   // Showing the window should trigger a user switch.
   window(0)->Show();
-  EXPECT_EQ(account_id_b, user_manager()->GetActiveUser()->GetAccountId());
+  EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver());
 }
 
 // Test that using the full user switch animations are working as expected.
@@ -930,8 +945,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager()->SetWindowOwner(window(1), account_id_B);
@@ -968,8 +983,8 @@
   const AccountId account_id_B(AccountId::FromUserEmail("B"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager()->SetWindowOwner(window(1), account_id_B);
@@ -989,8 +1004,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager()->SetWindowOwner(window(1), account_id_B);
@@ -1047,8 +1062,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   wm::GetWindowState(window(0))->Maximize();
@@ -1092,8 +1107,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager()->SetWindowOwner(window(1), account_id_B);
@@ -1138,8 +1153,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  multi_user_window_manager()->SetAnimationSpeedForTest(
-      MultiUserWindowManagerChromeOS::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   wm::GetWindowState(window(0))->Maximize();
@@ -1205,7 +1220,6 @@
   const AccountId account_id_c(AccountId::FromUserEmail("c"));
 
   StartUserTransitionAnimation(account_id_a);
-  SessionControllerClient::DoSwitchActiveUser(account_id_a);
 
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_a);
@@ -1330,18 +1344,14 @@
   wm::GetWindowState(window(2))->Minimize();
 
   // Windows belonging to user2 (window #2 and #3) can't be activated by user1.
-  user_manager()->SwitchActiveUser(user1);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user1));
+  SwitchActiveUser(user1);
   EXPECT_TRUE(::wm::CanActivateWindow(window(0)));
   EXPECT_TRUE(::wm::CanActivateWindow(window(1)));
   EXPECT_FALSE(::wm::CanActivateWindow(window(2)));
   EXPECT_FALSE(::wm::CanActivateWindow(window(3)));
 
   // Windows belonging to user1 (window #0 and #1) can't be activated by user2.
-  user_manager()->SwitchActiveUser(user2);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user2));
+  SwitchActiveUser(user2);
   EXPECT_FALSE(::wm::CanActivateWindow(window(0)));
   EXPECT_FALSE(::wm::CanActivateWindow(window(1)));
   EXPECT_TRUE(::wm::CanActivateWindow(window(2)));
@@ -1360,9 +1370,7 @@
   multi_user_window_manager()->SetWindowOwner(window(0), user1);
   multi_user_window_manager()->SetWindowOwner(window(1), user2);
 
-  user_manager()->SwitchActiveUser(user1);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user1));
+  SwitchActiveUser(user1);
   EXPECT_TRUE(::wm::CanActivateWindow(window(0)));
   EXPECT_FALSE(::wm::CanActivateWindow(window(1)));
 
@@ -1372,9 +1380,7 @@
   EXPECT_FALSE(::wm::CanActivateWindow(window(0)));
 
   // Test that window #0 can be activated by user2.
-  user_manager()->SwitchActiveUser(user2);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user2));
+  SwitchActiveUser(user2);
   EXPECT_TRUE(::wm::CanActivateWindow(window(0)));
   EXPECT_TRUE(::wm::CanActivateWindow(window(1)));
 }
@@ -1390,9 +1396,7 @@
 
   multi_user_window_manager()->SetWindowOwner(window(0), user1);
 
-  user_manager()->SwitchActiveUser(user1);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user1));
+  SwitchActiveUser(user1);
 
   // Window #0 has no kAvatarIconKey property before teloporting.
   EXPECT_FALSE(window(0)->GetProperty(aura::client::kAvatarIconKey));
@@ -1417,9 +1421,7 @@
   const AccountId account_id_B(AccountId::FromUserEmail("B"));
   AddTestUser(account_id_A);
   AddTestUser(account_id_B);
-  user_manager()->SwitchActiveUser(account_id_A);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(account_id_A));
+  SwitchActiveUser(account_id_A);
 
   // Set the windows owner.
   ::wm::ActivationClient* activation_client =
@@ -1441,15 +1443,11 @@
   EXPECT_EQ(mru_list[1], window(1));
   EXPECT_EQ(mru_list[2], window(2));
 
-  user_manager()->SwitchActiveUser(account_id_B);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(account_id_B));
+  SwitchActiveUser(account_id_B);
   EXPECT_EQ("H[A], H[A], H[A]", GetStatus());
   EXPECT_EQ(activation_client->GetActiveWindow(), nullptr);
 
-  user_manager()->SwitchActiveUser(account_id_A);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(account_id_A));
+  SwitchActiveUser(account_id_A);
   EXPECT_EQ("S[A], S[A], S[A]", GetStatus());
   EXPECT_EQ(activation_client->GetActiveWindow(), window(0));
 
@@ -1469,9 +1467,7 @@
   const AccountId account_id_B(AccountId::FromUserEmail("B"));
   AddTestUser(account_id_A);
   AddTestUser(account_id_B);
-  user_manager()->SwitchActiveUser(account_id_A);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(account_id_A));
+  SwitchActiveUser(account_id_A);
 
   multi_user_window_manager()->SetWindowOwner(window(0), account_id_A);
   Profile* profile = multi_user_util::GetProfileFromAccountId(account_id_A);
@@ -1486,9 +1482,7 @@
   EXPECT_EQ(browser.get(), chrome::FindBrowserWithActiveWindow());
 
   // Switch to another user's desktop with no active window.
-  user_manager()->SwitchActiveUser(account_id_B);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(account_id_B));
+  SwitchActiveUser(account_id_B);
   EXPECT_EQ(browser.get(), BrowserList::GetInstance()->GetLastActive());
   EXPECT_FALSE(browser->window()->IsActive());
   EXPECT_EQ(nullptr, chrome::FindBrowserWithActiveWindow());
@@ -1508,19 +1502,15 @@
   const AccountId user2(AccountId::FromUserEmail("B"));
   AddTestUser(user1);
   AddTestUser(user2);
-  user_manager()->SwitchActiveUser(user1);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user1));
+  SwitchActiveUser(user1);
   multi_user_window_manager()->SetWindowOwner(window(0), user1);
   multi_user_window_manager()->SetWindowOwner(window(1), user2);
   const gfx::Rect bounds(20, 20, 360, 100);
   window(0)->SetBounds(bounds);
   window(1)->SetBounds(bounds);
 
-  // Enter tablet mode. Manually call OnTabletModeToggled because
-  // TabletModeClient is null during tests.
+  // Enter tablet mode.
   Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
-  multi_user_window_manager()->OnTabletModeToggled(true);
   // Tests that bounds of both windows are maximized.
   const gfx::Rect maximized_bounds(0, 0, 400,
                                    200 - ShelfConstants::shelf_size());
@@ -1535,13 +1525,10 @@
   test_api.SetDisplayRotation(display::Display::ROTATE_0,
                               display::Display::RotationSource::ACTIVE);
   Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false);
-  multi_user_window_manager()->OnTabletModeToggled(false);
 
   // Tests that both windows have the same bounds as when they entered tablet
   // mode.
-  user_manager()->SwitchActiveUser(user2);
-  multi_user_window_manager()->ActiveUserChanged(
-      user_manager()->FindUser(user2));
+  SwitchActiveUser(user2);
   EXPECT_EQ(bounds, window(0)->bounds());
   EXPECT_EQ(bounds, window(1)->bounds());
 }
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index 21362076..b72d66d 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
@@ -21,12 +22,14 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/browser/url_and_title.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
@@ -42,6 +45,25 @@
 namespace {
 const char kPersistBookmarkURL[] = "http://www.cnn.com/";
 const char kPersistBookmarkTitle[] = "CNN";
+
+bool AreCommittedInterstitialsEnabled() {
+  return base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials);
+}
+
+bool IsShowingInterstitial(content::WebContents* tab) {
+  if (AreCommittedInterstitialsEnabled()) {
+    security_interstitials::SecurityInterstitialTabHelper* helper =
+        security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+            tab);
+    if (!helper) {
+      return false;
+    }
+    return helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting() !=
+           nullptr;
+  }
+  return tab->GetInterstitialPage() != nullptr;
+}
+
 }  // namespace
 
 class TestBookmarkTabHelperObserver : public BookmarkTabHelperObserver {
@@ -187,16 +209,16 @@
 
   // Go to a bookmarked url. Bookmark star should show.
   ui_test_utils::NavigateToURL(browser(), bookmark_url);
-  EXPECT_FALSE(web_contents->ShowingInterstitialPage());
+  EXPECT_FALSE(IsShowingInterstitial(web_contents));
   EXPECT_TRUE(bookmark_observer.is_starred());
-
   // Now go to a non-bookmarked url which triggers an SSL warning. Bookmark
   // star should disappear.
   GURL error_url = https_server.GetURL("/");
   ui_test_utils::NavigateToURL(browser(), error_url);
   web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-  content::WaitForInterstitialAttach(web_contents);
-  EXPECT_TRUE(web_contents->ShowingInterstitialPage());
+  if (!AreCommittedInterstitialsEnabled())
+    content::WaitForInterstitialAttach(web_contents);
+  EXPECT_TRUE(IsShowingInterstitial(web_contents));
   EXPECT_FALSE(bookmark_observer.is_starred());
 
   tab_helper->RemoveObserver(&bookmark_observer);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index eff848b..1ad6e17 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1429,10 +1429,6 @@
   UpdateWindowForLoadingStateChanged(source, to_different_document);
 }
 
-void Browser::LoadProgressChanged(WebContents* source, double progress) {
-  ScheduleUIUpdate(source, content::INVALIDATE_TYPE_LOAD);
-}
-
 void Browser::CloseContents(WebContents* source) {
   bool can_close_contents;
   if (IsFastTabUnloadEnabled())
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 0d173244..30324c9 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -595,8 +595,6 @@
   void ActivateContents(content::WebContents* contents) override;
   void LoadingStateChanged(content::WebContents* source,
                            bool to_different_document) override;
-  void LoadProgressChanged(content::WebContents* source,
-                           double progress) override;
   void CloseContents(content::WebContents* source) override;
   void SetContentsBounds(content::WebContents* source,
                          const gfx::Rect& bounds) override;
diff --git a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
index 55c123e6..fb212c9 100644
--- a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h"
@@ -52,29 +53,22 @@
   HasVisibleLocationBarForBrowser(browser());
 }
 
-// Disabled. See https://crbug.com/845389 - this regressed somewhere between
-// r545258 and r559030 (suspect: r549698), but it may be obsolete soon.
 IN_PROC_BROWSER_TEST_F(PermissionBubbleBrowserTest,
-                       DISABLED_TabFullscreenHasLocationBar) {
-  ui::test::ScopedFakeNSWindowFullscreen faker;
-
-  // TODO(tapted): This should use ShowBubble(). However, on 10.9 it triggers a
-  // DCHECK failure in cr_setPatternPhase:forView:. See http://crbug.com/802107.
-  auto prompt =
-      std::make_unique<PermissionPromptImpl>(browser(), test_delegate());
+                       TabFullscreenHasLocationBar) {
+  FullscreenNotificationObserver fullscreen_observer;
+  ShowBubble(browser());
   EXPECT_TRUE(HasVisibleLocationBarForBrowser(browser()));
 
   FullscreenController* controller =
       browser()->exclusive_access_manager()->fullscreen_controller();
   controller->EnterFullscreenModeForTab(
       browser()->tab_strip_model()->GetActiveWebContents(), GURL());
-  faker.FinishTransition();
-
+  fullscreen_observer.Wait();
   EXPECT_FALSE(HasVisibleLocationBarForBrowser(browser()));
+
   controller->ExitFullscreenModeForTab(
       browser()->tab_strip_model()->GetActiveWebContents());
-  faker.FinishTransition();
-
+  fullscreen_observer.Wait();
   EXPECT_TRUE(HasVisibleLocationBarForBrowser(browser()));
 }
 
diff --git a/chrome/browser/ui/omnibox/lookalike_url_navigation_observer.cc b/chrome/browser/ui/omnibox/lookalike_url_navigation_observer.cc
index cb12fa79..5749d29 100644
--- a/chrome/browser/ui/omnibox/lookalike_url_navigation_observer.cc
+++ b/chrome/browser/ui/omnibox/lookalike_url_navigation_observer.cc
@@ -73,7 +73,7 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   SiteEngagementService* service = SiteEngagementService::Get(profile);
-  if (service->IsEngagementAtLeast(url, blink::mojom::EngagementLevel::LOW))
+  if (service->IsEngagementAtLeast(url, blink::mojom::EngagementLevel::MEDIUM))
     return;
 
   const base::StringPiece host = url.host_piece();
@@ -154,7 +154,7 @@
   for (const auto& detail : engagement_details) {
     // Ignore sites with an engagement score lower than LOW.
     if (!service->IsEngagementAtLeast(detail.origin,
-                                      blink::mojom::EngagementLevel::LOW))
+                                      blink::mojom::EngagementLevel::MEDIUM))
       continue;
 
     // If the user has engaged with eTLD+1 of this site, don't show any
diff --git a/chrome/browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc b/chrome/browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc
index 62ead614..790ec49 100644
--- a/chrome/browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc
+++ b/chrome/browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc
@@ -33,6 +33,12 @@
 
 enum class FeatureTestState { kDisabled, kEnabled };
 
+// An engagement score above MEDIUM.
+const int kHighEngagement = 20;
+
+// An engagement score below MEDIUM.
+const int kLowEngagement = 1;
+
 struct SiteEngagementTestCase {
   const char* const navigated;
   const char* const suggested;
@@ -201,7 +207,7 @@
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
                        TopDomainIdn_EngagedSite_NoInfobar) {
   const GURL url = embedded_test_server()->GetURL("googlé.com", "/title1.html");
-  SetSiteEngagementScore(url, 20);
+  SetSiteEngagementScore(url, kHighEngagement);
   TestInfobarNotShown(url);
   CheckNoUkm();
 }
@@ -217,6 +223,9 @@
       embedded_test_server()->GetURL("googlé.com", "/title1.html");
 
   if (GetParam() == FeatureTestState::kEnabled) {
+    // Even if the navigated site has a low engagement score, it should be
+    // considered for lookalike suggestions.
+    SetSiteEngagementScore(kNavigatedUrl, kLowEngagement);
     // If the feature is enabled, the UI will be displayed. Expect extra
     // histogram entries for kInfobarShown and kLinkClicked events.
     TestInfobarShown(kNavigatedUrl,
@@ -256,16 +265,19 @@
 // a feature param.
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
                        Idn_SiteEngagement_Match) {
-  SetSiteEngagementScore(GURL("http://site1.test"), 20);
-  SetSiteEngagementScore(GURL("http://www.site2.test"), 20);
-  SetSiteEngagementScore(GURL("http://sité3.test"), 20);
-  SetSiteEngagementScore(GURL("http://www.sité4.test"), 20);
+  SetSiteEngagementScore(GURL("http://site1.test"), kHighEngagement);
+  SetSiteEngagementScore(GURL("http://www.site2.test"), kHighEngagement);
+  SetSiteEngagementScore(GURL("http://sité3.test"), kHighEngagement);
+  SetSiteEngagementScore(GURL("http://www.sité4.test"), kHighEngagement);
 
   std::vector<GURL> ukm_urls;
   for (const auto& test_case : kSiteEngagementTestCases) {
     base::HistogramTester histograms;
     const GURL kNavigatedUrl =
         embedded_test_server()->GetURL(test_case.navigated, "/title1.html");
+    // Even if the navigated site has a low engagement score, it should be
+    // considered for lookalike suggestions.
+    SetSiteEngagementScore(kNavigatedUrl, kLowEngagement);
 
     if (GetParam() == FeatureTestState::kEnabled) {
       // If the feature is enabled, the UI will be displayed. Expect extra
@@ -306,6 +318,20 @@
            LookalikeUrlNavigationObserver::MatchType::kSiteEngagement);
 }
 
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
+                       Idn_SiteEngagement_Match_Ignored) {
+  // Test that navigations to a site with a high engagement score shouldn't
+  // record metrics or show infobar.
+  base::HistogramTester histograms;
+  SetSiteEngagementScore(GURL("http://site5.test"), kHighEngagement);
+  const GURL high_engagement_url =
+      embedded_test_server()->GetURL("síte5.test", "/title1.html");
+  SetSiteEngagementScore(high_engagement_url, kHighEngagement);
+  TestInfobarNotShown(high_engagement_url);
+  histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
+                              0);
+}
+
 // IDNs with a single label should be properly handled. There are two cases
 // where this might occur:
 // 1. The navigated URL is an IDN with a single label.
@@ -320,7 +346,7 @@
 
   // Case 2: An IDN with a single label with a site engagement score shouldn't
   // cause a crash.
-  SetSiteEngagementScore(GURL("http://tést"), 20);
+  SetSiteEngagementScore(GURL("http://tést"), kHighEngagement);
   TestInfobarNotShown(
       embedded_test_server()->GetURL("tést.com", "/title1.html"));
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
index 147dd52..9ea2ca97 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
@@ -188,6 +188,13 @@
     menu()->Cancel();
 }
 
+bool BookmarkMenuController::ShouldTryPositioningBesideAnchor() const {
+  // The bookmark menu appears from the bookmark bar, which has a set of buttons positioned next to
+  // each other; if the bookmark menu appears beside its anchor button, it will likely overlay the
+  // adjacent bookmark button, which prevents easy scrubbing through the bookmark bar's menus.
+  return false;
+}
+
 BookmarkMenuController::~BookmarkMenuController() {
   menu_delegate_->GetBookmarkModel()->RemoveObserver(this);
   if (observer_)
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
index d0f4d1b..701d547 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
@@ -114,6 +114,7 @@
                                       views::MenuButton** button) override;
   int GetMaxWidthForMenu(views::MenuItemView* view) override;
   void WillShowMenu(views::MenuItemView* menu) override;
+  bool ShouldTryPositioningBesideAnchor() const override;
 
   // bookmarks::BaseBookmarkModelObserver:
   void BookmarkModelChanged() override;
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index c60395a..976b24bc 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -299,6 +299,12 @@
   return list_views_[0];
 }
 
+int DesktopMediaPickerDialogView::GetDefaultDialogButton() const {
+  // To prevent permissions being accepted accidentally, permission prompts
+  // should not be accepted as the default action.
+  return ui::DIALOG_BUTTON_CANCEL;
+}
+
 base::string16 DesktopMediaPickerDialogView::GetDialogButtonLabel(
     ui::DialogButton button) const {
   return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
index 54d7ca56..63bb315 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
@@ -49,6 +49,7 @@
   base::string16 GetWindowTitle() const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   views::View* GetInitiallyFocusedView() override;
+  int GetDefaultDialogButton() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   View* CreateExtraView() override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc
index 445f2ae..b81b31e8 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc
@@ -15,6 +15,9 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "content/public/browser/desktop_media_id.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/window/dialog_client_view.h"
+#include "ui/views/window/dialog_delegate.h"
 
 class DesktopMediaPickerViewsBrowserTest : public DialogBrowserTest {
  public:
@@ -43,7 +46,7 @@
                   DesktopMediaPicker::DoneCallback());
   }
 
- private:
+ protected:
   std::unique_ptr<DesktopMediaPickerViews> picker_;
 
   DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViewsBrowserTest);
@@ -54,3 +57,12 @@
 IN_PROC_BROWSER_TEST_F(DesktopMediaPickerViewsBrowserTest, InvokeUi_default) {
   ShowAndVerifyUi();
 }
+
+IN_PROC_BROWSER_TEST_F(DesktopMediaPickerViewsBrowserTest,
+                       InitiallyFocusesCancel) {
+  ShowUi(std::string());
+  views::DialogDelegate* dialog =
+      picker_->GetDialogViewForTesting()->AsDialogDelegate();
+  EXPECT_EQ(dialog->GetDialogClientView()->cancel_button(),
+            dialog->DialogDelegate::GetInitiallyFocusedView());
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
index a345b83..2c17655 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -86,12 +86,8 @@
 }
 
 // Tests the frame color for a bookmark app when a theme is applied.
-//
-// Disabled because it hits a DCHECK in BrowserView.
-// TODO(mgiuca): Remove this DCHECK, since it seems legitimate.
-// https://crbug.com/879030.
 IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewBrowserTest,
-                       DISABLED_BookmarkAppFrameColorCustomTheme) {
+                       BookmarkAppFrameColorCustomTheme) {
   // The theme color should not affect the window, but the theme must not be the
   // default GTK theme for Linux so we install one anyway.
   InstallExtension(test_data_dir_.AppendASCII("theme"), 1);
@@ -104,12 +100,8 @@
 
 // Tests the frame color for a bookmark app when a theme is applied, with the
 // app itself having no theme color.
-//
-// Disabled because it hits a DCHECK in BrowserView.
-// TODO(mgiuca): Remove this DCHECK, since it seems legitimate.
-// https://crbug.com/879030.
 IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewBrowserTest,
-                       DISABLED_BookmarkAppFrameColorCustomThemeNoThemeColor) {
+                       BookmarkAppFrameColorCustomThemeNoThemeColor) {
   InstallExtension(test_data_dir_.AppendASCII("theme"), 1);
   app_theme_color_.reset();
   InstallAndLaunchBookmarkApp();
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index fc1a0378..249a763 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -1260,14 +1260,6 @@
           contents, entry->GetVirtualURL(), security_info);
   bubble->SetHighlightedButton(location_icon_view());
   bubble->GetWidget()->Show();
-
-  // When the user opens the page info bubble, we also expose the full URL,
-  // temporarily disabling Steady State Elisions and Query in Omnibox.
-  // We are currently gating this behavior on the Query in Omnibox flag, since
-  // it's still under active experimentation.
-  if (base::FeatureList::IsEnabled(omnibox::kQueryInOmnibox))
-    omnibox_view()->model()->Unelide(true /* exit_query_in_omnibox */);
-
   return true;
 }
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
index d7b4517..17aef41 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
@@ -35,6 +35,7 @@
   const UIMediaSink& sink() const { return sink_; }
 
  private:
+  friend class MediaRouterUiForTest;
   FRIEND_TEST_ALL_PREFIXES(CastDialogSinkButtonTest, OverrideStatusText);
   FRIEND_TEST_ALL_PREFIXES(CastDialogSinkButtonTest,
                            SetStatusLabelForActiveSink);
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index 1a96e06e..30989ef 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -75,10 +75,10 @@
 void CastDialogView::HideDialog() {
   if (IsShowing())
     instance_->GetWidget()->Close();
-  // We also set |instance_| to nullptr in WindowClosing() which is called
-  // asynchronously, because not all paths to close the dialog go through
-  // HideDialog(). We set it here because IsShowing() should be false after
-  // HideDialog() is called.
+  // We set |instance_| to null here because IsShowing() should be false after
+  // HideDialog() is called. Not all paths to close the dialog go through
+  // HideDialog(), so we also set it to null in WindowClosing(), which always
+  // gets called asynchronously.
   instance_ = nullptr;
 }
 
@@ -88,6 +88,11 @@
 }
 
 // static
+CastDialogView* CastDialogView::GetInstance() {
+  return instance_;
+}
+
+// static
 views::Widget* CastDialogView::GetCurrentDialogWidget() {
   return instance_ ? instance_->GetWidget() : nullptr;
 }
@@ -152,6 +157,8 @@
   MaybeSizeToContents();
   // Update the main action button.
   DialogModelChanged();
+  for (Observer& observer : observers_)
+    observer.OnDialogModelUpdated(this);
 }
 
 void CastDialogView::OnControllerInvalidated() {
@@ -210,6 +217,14 @@
   }
 }
 
+void CastDialogView::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void CastDialogView::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 // static
 void CastDialogView::ShowDialog(views::View* anchor_view,
                                 views::BubbleBorder::Arrow anchor_position,
@@ -259,6 +274,8 @@
 }
 
 void CastDialogView::WindowClosing() {
+  for (Observer& observer : observers_)
+    observer.OnDialogWillClose(this);
   if (instance_ == this)
     instance_ = nullptr;
   metrics_.OnCloseDialog(base::Time::Now());
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.h b/chrome/browser/ui/views/media_router/cast_dialog_view.h
index 146a3a3..4457547 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "chrome/browser/ui/media_router/cast_dialog_controller.h"
 #include "chrome/browser/ui/views/media_router/cast_dialog_metrics.h"
 #include "ui/base/models/simple_menu_model.h"
@@ -37,6 +38,14 @@
                        public CastDialogController::Observer,
                        public ui::SimpleMenuModel::Delegate {
  public:
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnDialogModelUpdated(CastDialogView* dialog_view) = 0;
+    virtual void OnDialogWillClose(CastDialogView* dialog_view) = 0;
+  };
+
+  enum SourceType { kTab, kDesktop, kLocalFile };
+
   // Shows the singleton dialog anchored to the Cast toolbar icon. Requires that
   // BrowserActionsContainer exists for |browser|.
   static void ShowDialogWithToolbarAction(CastDialogController* controller,
@@ -54,6 +63,8 @@
 
   static bool IsShowing();
 
+  static CastDialogView* GetInstance();
+
   // Returns nullptr if the dialog is currently not shown.
   static views::Widget* GetCurrentDialogWidget();
 
@@ -86,6 +97,9 @@
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Called by tests.
   const std::vector<CastDialogSinkButton*>& sink_buttons_for_test() const {
     return sink_buttons_;
@@ -108,8 +122,6 @@
   FRIEND_TEST_ALL_PREFIXES(CastDialogViewTest, ShowAndHideDialog);
   FRIEND_TEST_ALL_PREFIXES(CastDialogViewTest, ShowSourcesMenu);
 
-  enum SourceType { kTab, kDesktop, kLocalFile };
-
   // Instantiates and shows the singleton dialog. The dialog must not be
   // currently shown.
   static void ShowDialog(views::View* anchor_view,
@@ -212,6 +224,8 @@
   // This value is set if the user has chosen a local file to cast.
   base::Optional<base::string16> local_file_name_;
 
+  base::ObserverList<Observer> observers_;
+
   base::WeakPtrFactory<CastDialogView> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CastDialogView);
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index 20215e2..f21d8b5 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -130,10 +130,12 @@
     PaymentRequestSpec* spec,
     PaymentRequestState* state,
     PaymentRequestDialogView* dialog,
+    content::WebContents* log_destination,
     Profile* profile,
     GURL target,
     PaymentHandlerOpenWindowCallback first_navigation_complete_callback)
     : PaymentRequestSheetController(spec, state, dialog),
+      log_(log_destination),
       profile_(profile),
       target_(target),
       show_progress_bar_(false),
@@ -245,10 +247,9 @@
   // Allow localhost for test.
   if (!SslValidityChecker::IsSslCertificateValid(source) &&
       !net::IsLocalhost(source->GetLastCommittedURL())) {
-    WarnIfPossible(
-        "Opened payment handler window's visible security state changed for "
-        "url " +
-        source->GetVisibleURL().spec());
+    log_.Error("Aborting payment handler window \"" + target_.spec() +
+               "\" because of insecure certificate state on \"" +
+               source->GetVisibleURL().spec() + "\"");
     AbortPayment();
   }
 }
@@ -294,9 +295,9 @@
           navigation_handle->GetWebContents())) {
     // Allow localhost for test.
     if (!net::IsLocalhost(navigation_handle->GetURL())) {
-      WarnIfPossible(
-          "Opened payment handler window has an insecure navigation to url " +
-          navigation_handle->GetURL().spec());
+      log_.Error("Aborting payment handler window \"" + target_.spec() +
+                 "\" because of navigation to an insecure url \"" +
+                 navigation_handle->GetURL().spec() + "\"");
       AbortPayment();
       return;
     }
@@ -318,8 +319,9 @@
 }
 
 void PaymentHandlerWebFlowViewController::DidAttachInterstitialPage() {
-  WarnIfPossible(
-      "An interstitial page attached to opened payment handler window.");
+  log_.Error("Aborting payment handler window \"" + target_.spec() +
+             "\" because of navigation to a page with invalid certificate "
+             "state or malicious content.");
   AbortPayment();
 }
 
@@ -330,14 +332,4 @@
   dialog()->ShowErrorMessage();
 }
 
-void PaymentHandlerWebFlowViewController::WarnIfPossible(
-    const std::string& message) {
-  if (web_contents()) {
-    web_contents()->GetMainFrame()->AddMessageToConsole(
-        content::ConsoleMessageLevel::CONSOLE_MESSAGE_LEVEL_WARNING, message);
-  } else {
-    LOG(WARNING) << message;
-  }
-}
-
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
index 3b5aeed..b084d99 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_HANDLER_WEB_FLOW_VIEW_CONTROLLER_H_
 
 #include "chrome/browser/ui/views/payments/payment_request_sheet_controller.h"
+#include "components/payments/content/developer_console_logger.h"
 #include "components/payments/content/payment_request_display_manager.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -31,14 +32,17 @@
       public content::WebContentsObserver {
  public:
   // This ctor forwards its first 3 args to PaymentRequestSheetController's
-  // ctor. |profile| is the browser context used to create the WebContents
-  // object that will navigate to |target|. |first_navigation_complete_callback|
-  // is invoked once the WebContents finishes the initial navigation to
-  // |target|.
+  // ctor. |log_destination| is the page whose web developer console will print
+  // error messages. That should be the page that instantiated PaymentRequest
+  // for developer convinience. |profile| is the browser context used to create
+  // the new WebContents object that will navigate to |target|.
+  // |first_navigation_complete_callback| is invoked once the WebContents
+  // finishes the initial navigation to |target|.
   PaymentHandlerWebFlowViewController(
       PaymentRequestSpec* spec,
       PaymentRequestState* state,
       PaymentRequestDialogView* dialog,
+      content::WebContents* log_destination,
       Profile* profile,
       GURL target,
       PaymentHandlerOpenWindowCallback first_navigation_complete_callback);
@@ -75,8 +79,8 @@
   void DidAttachInterstitialPage() override;
 
   void AbortPayment();
-  void WarnIfPossible(const std::string& message);
 
+  DeveloperConsoleLogger log_;
   Profile* profile_;
   GURL target_;
   bool show_progress_bar_;
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
index 709a2a7..b666485e 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -178,12 +178,13 @@
 void PaymentRequestDialogView::ShowPaymentHandlerScreen(
     const GURL& url,
     PaymentHandlerOpenWindowCallback callback) {
-  view_stack_->Push(CreateViewAndInstallController(
-                        std::make_unique<PaymentHandlerWebFlowViewController>(
-                            request_->spec(), request_->state(), this,
-                            GetProfile(), url, std::move(callback)),
-                        &controller_map_),
-                    /* animate = */ true);
+  view_stack_->Push(
+      CreateViewAndInstallController(
+          std::make_unique<PaymentHandlerWebFlowViewController>(
+              request_->spec(), request_->state(), this,
+              request_->web_contents(), GetProfile(), url, std::move(callback)),
+          &controller_map_),
+      /* animate = */ true);
   HideProcessingSpinner();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc b/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
index 825a45b..9940e69 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
@@ -4,7 +4,6 @@
 
 #include <stddef.h>
 
-#include "ash/test/ash_test_base.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -19,6 +18,7 @@
 #include "components/account_id/account_id.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -37,7 +37,7 @@
 
 namespace chromeos {
 
-class SigninPrepareUserListTest : public ash::AshTestBase,
+class SigninPrepareUserListTest : public testing::Test,
                                   public MultiProfileUserControllerDelegate {
  public:
   SigninPrepareUserListTest()
@@ -46,8 +46,9 @@
 
   ~SigninPrepareUserListTest() override {}
 
+  // testing::Test:
   void SetUp() override {
-    ash::AshTestBase::SetUp();
+    testing::Test::SetUp();
     profile_manager_.reset(
         new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
     ASSERT_TRUE(profile_manager_->SetUp());
@@ -68,12 +69,16 @@
   void TearDown() override {
     controller_.reset();
     profile_manager_.reset();
-    ash::AshTestBase::TearDown();
+    testing::Test::TearDown();
   }
 
-  // MultiProfileUserControllerDelegate overrides:
+  // MultiProfileUserControllerDelegate:
   void OnUserNotAllowed(const std::string& user_email) override {}
 
+  FakeChromeUserManager* user_manager() { return fake_user_manager_; }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
   ScopedCrosSettingsTestHelper cros_settings_test_helper_;
   FakeChromeUserManager* fake_user_manager_;
   user_manager::ScopedUserManager user_manager_enabler_;
@@ -85,22 +90,20 @@
 };
 
 TEST_F(SigninPrepareUserListTest, AlwaysKeepOwnerInList) {
-  EXPECT_LT(kMaxUsers, fake_user_manager_->GetUsers().size());
+  EXPECT_LT(kMaxUsers, user_manager()->GetUsers().size());
   user_manager::UserList users_to_send =
       UserSelectionScreen::PrepareUserListForSending(
-          fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
-          true /* is signin to add */);
+          user_manager()->GetUsers(), AccountId::FromUserEmail(kOwner),
+          true /* is_signin_to_add */);
 
   EXPECT_EQ(kMaxUsers, users_to_send.size());
   EXPECT_EQ(kOwner, users_to_send.back()->GetAccountId().GetUserEmail());
 
-  fake_user_manager_->RemoveUserFromList(
-      AccountId::FromUserEmail("a16@gmail.com"));
-  fake_user_manager_->RemoveUserFromList(
-      AccountId::FromUserEmail("a17@gmail.com"));
+  user_manager()->RemoveUserFromList(AccountId::FromUserEmail("a16@gmail.com"));
+  user_manager()->RemoveUserFromList(AccountId::FromUserEmail("a17@gmail.com"));
   users_to_send = UserSelectionScreen::PrepareUserListForSending(
-      fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
-      true /* is signin to add */);
+      user_manager()->GetUsers(), AccountId::FromUserEmail(kOwner),
+      true /* is_signin_to_add */);
 
   EXPECT_EQ(kMaxUsers, users_to_send.size());
   EXPECT_EQ("a18@gmail.com",
@@ -112,16 +115,16 @@
 TEST_F(SigninPrepareUserListTest, PublicAccounts) {
   user_manager::UserList users_to_send =
       UserSelectionScreen::PrepareUserListForSending(
-          fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
-          true /* is signin to add */);
+          user_manager()->GetUsers(), AccountId::FromUserEmail(kOwner),
+          true /* is_signin_to_add */);
 
   EXPECT_EQ(kMaxUsers, users_to_send.size());
   EXPECT_EQ("a0@gmail.com",
             users_to_send.front()->GetAccountId().GetUserEmail());
 
   users_to_send = UserSelectionScreen::PrepareUserListForSending(
-      fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
-      false /* is signin to add */);
+      user_manager()->GetUsers(), AccountId::FromUserEmail(kOwner),
+      false /* is_signin_to_add */);
 
   EXPECT_EQ(kMaxUsers, users_to_send.size());
   EXPECT_EQ("public0@gmail.com",
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
index f283384..39caabc 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
@@ -18,7 +17,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_contents_sizer.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
@@ -217,51 +215,3 @@
     expect_has_listeners(false);
   }
 }
-
-class ExtensionsActivityLogTest : public ExtensionSettingsUIBrowserTest {
- protected:
-  // Enable command line flags for test.
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kEnableExtensionActivityLogging);
-  };
-};
-
-IN_PROC_BROWSER_TEST_F(ExtensionsActivityLogTest, TestActivityLogVisible) {
-  base::FilePath test_data_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
-  test_data_dir = test_data_dir.AppendASCII("extensions");
-  extensions::ChromeTestExtensionLoader loader(browser()->profile());
-  const extensions::Extension* extension =
-      loader
-          .LoadExtension(test_data_dir.AppendASCII("activity_log/simple_call"))
-          .get();
-  ASSERT_TRUE(extension);
-
-  GURL activity_log_url("chrome://extensions/?activity=" + extension->id());
-  ui_test_utils::NavigateToURL(browser(), activity_log_url);
-  content::WebContents* activity_log_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(activity_log_contents);
-  EXPECT_EQ(activity_log_url, activity_log_contents->GetLastCommittedURL());
-
-  // We are looking for the 'tabs.query' entry in the activity log as that is
-  // the only API call the simple_call.crx extension does.
-  // The querySelectors and shadowRoots are used here in order to penetrate
-  // multiple nested shadow DOMs created by Polymer components
-  // in the chrome://extensions page.
-  // See chrome/browser/resources/md_extensions for the Polymer code.
-  // This test only serves as an end to end test, and most of the functionality
-  // is covered in the JS unit tests.
-  bool has_api_call = false;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
-      activity_log_contents,
-      R"(let manager = document.querySelector('extensions-manager');
-         let activityLog =
-             manager.shadowRoot.querySelector('extensions-activity-log');
-         let item = activityLog.shadowRoot.querySelector('activity-log-item');
-         let apiCall = item.shadowRoot.getElementById('api-call');
-         window.domAutomationController.send(
-             apiCall.innerText === 'tabs.query');)",
-      &has_api_call));
-  EXPECT_TRUE(has_api_call);
-}
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 1ee8310..486a82fd 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -192,7 +192,6 @@
     {"accessibilityErrorLine", IDS_MD_EXTENSIONS_ACCESSIBILITY_ERROR_LINE},
     {"accessibilityErrorMultiLine",
      IDS_MD_EXTENSIONS_ACCESSIBILITY_ERROR_MULTI_LINE},
-    {"activityLogPageHeading", IDS_MD_EXTENSIONS_ACTIVITY_LOG_PAGE_HEADING},
     {"appIcon", IDS_MD_EXTENSIONS_APP_ICON},
     {"extensionIcon", IDS_MD_EXTENSIONS_EXTENSION_ICON},
     {"extensionA11yAssociation", IDS_MD_EXTENSIONS_EXTENSION_A11Y_ASSOCIATION},
@@ -227,7 +226,6 @@
     {"loadErrorFileLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_FILE_LABEL},
     {"loadErrorErrorLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_ERROR_LABEL},
     {"loadErrorRetry", IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY},
-    {"noActivities", IDS_MD_EXTENSIONS_NO_ACTIVITIES},
     {"noErrorsToShow", IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE},
     {"runtimeHostsDialogInputError",
      IDS_MD_EXTENSIONS_RUNTIME_HOSTS_DIALOG_INPUT_ERROR},
@@ -261,7 +259,7 @@
     {"toolbarUpdatingToast", IDS_MD_EXTENSIONS_TOOLBAR_UPDATING_TOAST},
     {"updateRequiredByPolicy",
      IDS_MD_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY},
-    {"viewActivityLog", IDS_EXTENSIONS_VIEW_ACTIVITY_LOG},
+    {"viewActivityLog", IDS_EXTENSIONS_ACTIVITY_LOG},
     {"viewBackgroundPage", IDS_EXTENSIONS_BACKGROUND_PAGE},
     {"viewIncognito", IDS_EXTENSIONS_VIEW_INCOGNITO},
     {"viewInactive", IDS_EXTENSIONS_VIEW_INACTIVE},
diff --git a/chrome/common/extensions/api/PRESUBMIT.py b/chrome/common/extensions/api/PRESUBMIT.py
index 851496e..71ed9c8 100644
--- a/chrome/common/extensions/api/PRESUBMIT.py
+++ b/chrome/common/extensions/api/PRESUBMIT.py
@@ -26,7 +26,6 @@
   externs_root = join(src_root, 'third_party', 'closure_compiler', 'externs')
 
   api_pair_names = {
-    'activity_log_private.json': 'activity_log_private.js',
     'autofill_private.idl': 'autofill_private.js',
     'automation.idl': 'automation.js',
     'developer_private.idl': 'developer_private.js',
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 8807e438..1c396a6 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -61,16 +61,10 @@
     "dependencies": ["manifest:action"],
     "contexts": ["blessed_extension"]
   },
-  "activityLogPrivate": [{
+  "activityLogPrivate": {
     "dependencies": ["permission:activityLogPrivate"],
     "contexts": ["blessed_extension"]
-  }, {
-    "channel": "stable",
-    "contexts": ["webui"],
-    "matches": [
-      "chrome://extensions/*"
-    ]
-  }],
+  },
   "app": {
     "blacklist": [
       "2FC374607C2DF285634B67C64A2E356C607091C3",  // Quickoffice
diff --git a/chrome/services/printing/DEPS b/chrome/services/printing/DEPS
index a3eb010..6d3ada73 100644
--- a/chrome/services/printing/DEPS
+++ b/chrome/services/printing/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+components/crash/core/common/crash_key.h",
   "+components/pwg_encoder",
+  "+pdf/pdf.h",
 ]
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f6464379..fc33066 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2310,6 +2310,10 @@
     # enums.xml changes.
     "../../tools/metrics/histograms/enums.xml",
 
+    # flag-metadata.json is analyzed by AboutFlagsTest, so this dependency is
+    # needed to re-run unit_tests on changes to that file.
+    "../browser/flag-metadata.json",
+
     # All unittests in browser, common, renderer and service.
     "../browser/about_flags_unittest.cc",
     "../browser/active_use_util_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderTestRule.java
index cf3cdb5..3c4ea744 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/RenderTestRule.java
@@ -340,6 +340,9 @@
     private static Pair<ComparisonResult, Bitmap> compareBitmapToGolden(
             Bitmap render, Bitmap golden) {
         if (golden == null) return Pair.create(ComparisonResult.GOLDEN_NOT_FOUND, null);
+        // This comparison is much, much faster than doing a pixel-by-pixel comparison, so try this
+        // first and only fall back to the pixel comparison if it fails.
+        if (render.sameAs(golden)) return Pair.create(ComparisonResult.MATCH, null);
 
         Bitmap diff = Bitmap.createBitmap(Math.max(render.getWidth(), golden.getWidth()),
                 Math.max(render.getHeight(), golden.getHeight()), render.getConfig());
diff --git a/chrome/test/data/extensions/activity_log/simple_call/background.js b/chrome/test/data/extensions/activity_log/simple_call/background.js
deleted file mode 100644
index 95a04ac..0000000
--- a/chrome/test/data/extensions/activity_log/simple_call/background.js
+++ /dev/null
@@ -1,11 +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.
-
-// This extension makes a single call to the tab API, it is used to test the
-// activity log page as it should display a call for this extension.
-// This test might be expanded upon in the future.
-
-chrome.tabs.query({}, (tabs) => {
-  console.log(`queried tabs: ${tabs}`);
-});
diff --git a/chrome/test/data/extensions/activity_log/simple_call/manifest.json b/chrome/test/data/extensions/activity_log/simple_call/manifest.json
deleted file mode 100644
index 0130ee7..0000000
--- a/chrome/test/data/extensions/activity_log/simple_call/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "Activity log call testing extension",
-  "description": "Extension that makes 1 API call, used to test activity log.",
-  "manifest_version": 2,
-  "version": "2.0",
-  "permissions": [
-    "tabs"
-  ],
-  "background": {
-    "scripts": ["background.js"]
-  }
-}
diff --git a/chrome/test/data/extensions/webui/sanity_check_available_apis.js b/chrome/test/data/extensions/webui/sanity_check_available_apis.js
index 3ef7e93..10a2307 100644
--- a/chrome/test/data/extensions/webui/sanity_check_available_apis.js
+++ b/chrome/test/data/extensions/webui/sanity_check_available_apis.js
@@ -13,7 +13,6 @@
 //        related and whose tests fail.
 
 var expected = [
-  'activityLogPrivate',
   'csi',
   'developerPrivate',
   'getVariableValue',
diff --git a/chrome/test/data/prerender/prefetch_nostore_page.html b/chrome/test/data/prerender/prefetch_nostore_page.html
new file mode 100644
index 0000000..cca3df4
--- /dev/null
+++ b/chrome/test/data/prerender/prefetch_nostore_page.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  This is a page containing text.  
+</body>
+</html>
diff --git a/chrome/test/data/prerender/prefetch_nostore_page.html.mock-http-headers b/chrome/test/data/prerender/prefetch_nostore_page.html.mock-http-headers
new file mode 100644
index 0000000..2567dc2f
--- /dev/null
+++ b/chrome/test/data/prerender/prefetch_nostore_page.html.mock-http-headers
@@ -0,0 +1 @@
+Cache-Control: no-store
\ No newline at end of file
diff --git a/chrome/test/data/prerender/prefetch_recurse.html b/chrome/test/data/prerender/prefetch_recurse.html
new file mode 100644
index 0000000..031238d
--- /dev/null
+++ b/chrome/test/data/prerender/prefetch_recurse.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <link rel="prerender" href="prefetch_nostore_page.html">
+  </head>
+  <body>
+    Nonempty body for First Contentful Paint
+  </body>
+</html>
diff --git a/chrome/test/data/prerender/prerender_infinite_a_multiple.html b/chrome/test/data/prerender/prerender_infinite_a_multiple.html
deleted file mode 100644
index ac2e709..0000000
--- a/chrome/test/data/prerender/prerender_infinite_a_multiple.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-  <head>
-    <script>
-      var pageWasPrerendered = false;
-
-      function DidPrerenderPass() {
-        pageWasPrerendered = true;
-        return true;
-      }
-
-      function DidDisplayPass() {
-        return pageWasPrerendered;
-      }
-    </script>
-  </head>
-  <body>
-    <link rel="prerender" href="prerender_infinite_b_multiple.html">
-    <a href="prerender_infinite_b_multiple.html">B</a>
-    <link rel="prerender" href="prerender_infinite_c_multiple.html">
-    <a href="prerender_infinite_c_multiple.html">C</a>
-  </body>
-</html>
diff --git a/chrome/test/data/prerender/prerender_infinite_b_multiple.html b/chrome/test/data/prerender/prerender_infinite_b_multiple.html
deleted file mode 100644
index a47b764..0000000
--- a/chrome/test/data/prerender/prerender_infinite_b_multiple.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
-  <head>
-  </head>
-  <body>
-    <link rel="prerender" href="prerender_infinite_a_multiple.html">
-    <a href="prerender_infinite_a_multiple.html">A</a>
-  </body>
-</html>
diff --git a/chrome/test/data/prerender/prerender_infinite_c_multiple.html b/chrome/test/data/prerender/prerender_infinite_c_multiple.html
deleted file mode 100644
index a47b764..0000000
--- a/chrome/test/data/prerender/prerender_infinite_c_multiple.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
-  <head>
-  </head>
-  <body>
-    <link rel="prerender" href="prerender_infinite_a_multiple.html">
-    <a href="prerender_infinite_a_multiple.html">A</a>
-  </body>
-</html>
diff --git a/chrome/test/data/webui/extensions/activity_log_test.js b/chrome/test/data/webui/extensions/activity_log_test.js
deleted file mode 100644
index 499aa15..0000000
--- a/chrome/test/data/webui/extensions/activity_log_test.js
+++ /dev/null
@@ -1,101 +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.
-
-/** @fileoverview Suite of tests for extensions-activity-log. */
-suite('ExtensionsActivityLogTest', function() {
-  /**
-   * Backing extension id, same id as the one in
-   * extension_test_util.createExtensionInfo
-   * @type {string}
-   */
-  const EXTENSION_ID = 'a'.repeat(32);
-
-  /**
-   * Extension activityLog created before each test.
-   * @type {extensions.ActivityLog}
-   */
-  let activityLog;
-  let proxyDelegate;
-  let testVisible;
-
-  const testActivities = {
-    activities: [
-      {
-        activityId: '299',
-        activityType: 'api_call',
-        apiCall: 'i18n.getUILanguage',
-        args: 'null',
-        count: 10,
-        extensionId: EXTENSION_ID,
-        time: 1541203132002.664
-      },
-      {
-        activityId: '309',
-        activityType: 'dom_access',
-        apiCall: 'Storage.getItem',
-        args: 'null',
-        count: 9,
-        extensionId: EXTENSION_ID,
-        other: {domVerb: 'method'},
-        pageTitle: 'Test Extension',
-        pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
-        time: 1541203131994.837
-      },
-    ]
-  };
-
-  // Initialize an extension activity log before each test.
-  setup(function() {
-    PolymerTest.clearBody();
-
-    activityLog = new extensions.ActivityLog();
-    testVisible = extension_test_util.testVisible.bind(null, activityLog);
-
-    activityLog.extensionId = EXTENSION_ID;
-    proxyDelegate = new extensions.TestService();
-    activityLog.delegate = proxyDelegate;
-    proxyDelegate.testActivities = testActivities;
-    document.body.appendChild(activityLog);
-
-    // Wait until we have finished making the call to fetch the activity log.
-    return proxyDelegate.whenCalled('getExtensionActivityLog');
-  });
-
-  teardown(function() {
-    activityLog.remove();
-  });
-
-  test('activities are present for extension', function() {
-    Polymer.dom.flush();
-
-    expectEquals(
-        activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 2);
-  });
-
-  test('message shown when no activities present for extension', function() {
-    // Spoof an API call and pretend that the extension has no activities.
-    activityLog.activityData_ = {
-      activities: [],
-    };
-
-    Polymer.dom.flush();
-
-    testVisible('#no-activities', true);
-    expectEquals(
-        activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 0);
-  });
-
-  test('clicking on back button navigates to the details page', function() {
-    Polymer.dom.flush();
-
-    let currentPage = null;
-    extensions.navigation.addListener(newPage => {
-      currentPage = newPage;
-    });
-
-    activityLog.$$('#close-button').click();
-    expectDeepEquals(
-        currentPage, {page: Page.DETAILS, extensionId: EXTENSION_ID});
-  });
-});
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 9c601d17..241399f 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -197,26 +197,6 @@
 });
 
 ////////////////////////////////////////////////////////////////////////////////
-// Extension Activity Log Tests
-
-CrExtensionsActivityLogTest = class extends CrExtensionsBrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://extensions/activity_log.html';
-  }
-
-  get extraLibraries() {
-    return super.extraLibraries.concat([
-      'activity_log_test.js',
-    ]);
-  }
-};
-
-TEST_F('CrExtensionsActivityLogTest', 'All', () => {
-  mocha.run();
-});
-
-////////////////////////////////////////////////////////////////////////////////
 // Extension Detail View Tests
 
 CrExtensionsDetailViewTest = class extends CrExtensionsBrowserTest {
diff --git a/chrome/test/data/webui/extensions/test_service.js b/chrome/test/data/webui/extensions/test_service.js
index 36b6852..609f675 100644
--- a/chrome/test/data/webui/extensions/test_service.js
+++ b/chrome/test/data/webui/extensions/test_service.js
@@ -8,7 +8,6 @@
     constructor() {
       super([
         'addRuntimeHostPermission',
-        'getExtensionActivityLog',
         'getExtensionsInfo',
         'getExtensionSize',
         'getProfileConfiguration',
@@ -36,9 +35,6 @@
 
       /** @type {boolean} */
       this.forceReloadItemError_ = false;
-
-      /** @type {!chrome.activityLogPrivate.ActivityResultSet|undefined} */
-      this.testActivities = undefined;
     }
 
     /**
@@ -153,12 +149,6 @@
       this.methodCalled('updateAllExtensions');
       return Promise.resolve();
     }
-
-    /** @override */
-    getExtensionActivityLog(id) {
-      this.methodCalled('getExtensionActivityLog', id);
-      return Promise.resolve(this.testActivities);
-    }
   }
 
   return {
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index a47a707..5950a99 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -21,6 +21,8 @@
     "media_router_integration_browsertest.h",
     "media_router_integration_ui_browsertest.cc",
     "media_router_one_ua_integration_browsertest.cc",
+    "media_router_ui_for_test.cc",
+    "media_router_ui_for_test.h",
   ]
   deps = [
     ":test_support",
diff --git a/chrome/test/media_router/media_router_ui_for_test.cc b/chrome/test/media_router/media_router_ui_for_test.cc
new file mode 100644
index 0000000..95587b4
--- /dev/null
+++ b/chrome/test/media_router/media_router_ui_for_test.cc
@@ -0,0 +1,241 @@
+// 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/test/media_router/media_router_ui_for_test.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/media_router/cast_dialog_sink_button.h"
+#include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
+#include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace media_router {
+
+namespace {
+
+ui::MouseEvent CreateMousePressedEvent() {
+  return ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
+                        gfx::Point(0, 0), ui::EventTimeForNow(),
+                        ui::EF_LEFT_MOUSE_BUTTON, 0);
+}
+
+ui::MouseEvent CreateMouseReleasedEvent() {
+  return ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(0, 0),
+                        gfx::Point(0, 0), ui::EventTimeForNow(),
+                        ui::EF_LEFT_MOUSE_BUTTON, 0);
+}
+
+}  // namespace
+
+// static
+MediaRouterUiForTest* MediaRouterUiForTest::GetOrCreateForWebContents(
+    content::WebContents* web_contents) {
+  // No-op if an instance already exists for the WebContents.
+  MediaRouterUiForTest::CreateForWebContents(web_contents);
+  return MediaRouterUiForTest::FromWebContents(web_contents);
+}
+
+MediaRouterUiForTest::~MediaRouterUiForTest() {
+  CHECK(!watch_callback_);
+}
+
+void MediaRouterUiForTest::ShowDialog() {
+  dialog_controller_->ShowMediaRouterDialog();
+  base::RunLoop().RunUntilIdle();
+}
+
+void MediaRouterUiForTest::HideDialog() {
+  dialog_controller_->HideMediaRouterDialog();
+  base::RunLoop().RunUntilIdle();
+}
+
+bool MediaRouterUiForTest::IsDialogShown() const {
+  return dialog_controller_->IsShowingMediaRouterDialog();
+}
+
+void MediaRouterUiForTest::ChooseSourceType(
+    CastDialogView::SourceType source_type) {
+  CastDialogView* dialog_view = CastDialogView::GetInstance();
+  CHECK(dialog_view);
+
+  dialog_view->ButtonPressed(dialog_view->sources_button_for_test(),
+                             CreateMousePressedEvent());
+  int source_index;
+  switch (source_type) {
+    case CastDialogView::kTab:
+      source_index = 0;
+      break;
+    case CastDialogView::kDesktop:
+      source_index = 1;
+      break;
+    case CastDialogView::kLocalFile:
+      source_index = 2;
+      break;
+  }
+  dialog_view->sources_menu_model_for_test()->ActivatedAt(source_index);
+}
+
+void MediaRouterUiForTest::StartCasting(const MediaSink::Id& sink_id) {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  sink_button->OnMousePressed(CreateMousePressedEvent());
+  sink_button->OnMouseReleased(CreateMouseReleasedEvent());
+  base::RunLoop().RunUntilIdle();
+}
+
+void MediaRouterUiForTest::StopCasting(const MediaSink::Id& sink_id) {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  sink_button->icon_view()->OnMousePressed(CreateMousePressedEvent());
+  sink_button->icon_view()->OnMouseReleased(CreateMouseReleasedEvent());
+  base::RunLoop().RunUntilIdle();
+}
+
+void MediaRouterUiForTest::StopCasting() {
+  CastDialogView* dialog_view = CastDialogView::GetInstance();
+  CHECK(dialog_view);
+  for (CastDialogSinkButton* sink_button :
+       dialog_view->sink_buttons_for_test()) {
+    if (sink_button->sink().state == UIMediaSinkState::CONNECTED) {
+      sink_button->icon_view()->OnMousePressed(CreateMousePressedEvent());
+      sink_button->icon_view()->OnMouseReleased(CreateMouseReleasedEvent());
+      base::RunLoop().RunUntilIdle();
+      return;
+    }
+  }
+  NOTREACHED() << "Sink was not found";
+}
+
+void MediaRouterUiForTest::WaitForSink(const MediaSink::Id& sink_id) {
+  ObserveDialog(WatchType::kSink, sink_id);
+}
+
+void MediaRouterUiForTest::WaitForAnyIssue() {
+  ObserveDialog(WatchType::kAnyIssue);
+}
+
+void MediaRouterUiForTest::WaitForAnyRoute() {
+  ObserveDialog(WatchType::kAnyRoute);
+}
+
+void MediaRouterUiForTest::WaitForDialogClosed() {
+  ObserveDialog(WatchType::kDialogClosed);
+}
+
+std::string MediaRouterUiForTest::GetSinkName(
+    const MediaSink::Id& sink_id) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  return base::UTF16ToUTF8(sink_button->sink().friendly_name);
+}
+
+MediaRoute::Id MediaRouterUiForTest::GetRouteIdForSink(
+    const MediaSink::Id& sink_id) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  return sink_button->sink().route_id;
+}
+
+std::string MediaRouterUiForTest::GetStatusTextForSink(
+    const MediaSink::Id& sink_id) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  return base::UTF16ToUTF8(sink_button->sink().status_text);
+}
+
+std::string MediaRouterUiForTest::GetIssueTextForSink(
+    const MediaSink::Id& sink_id) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+  if (!sink_button->sink().issue) {
+    NOTREACHED() << "Issue not found for sink " << sink_id;
+    return "";
+  }
+  return sink_button->sink().issue->info().title;
+}
+
+MediaRouterUiForTest::MediaRouterUiForTest(content::WebContents* web_contents)
+    : web_contents_(web_contents),
+      dialog_controller_(
+          MediaRouterDialogControllerViews::GetOrCreateForWebContents(
+              web_contents)) {}
+
+void MediaRouterUiForTest::OnDialogModelUpdated(CastDialogView* dialog_view) {
+  if (!watch_callback_ || watch_type_ == WatchType::kDialogClosed)
+    return;
+
+  const std::vector<CastDialogSinkButton*>& sink_buttons =
+      dialog_view->sink_buttons_for_test();
+  if (std::find_if(sink_buttons.begin(), sink_buttons.end(),
+                   [&, this](CastDialogSinkButton* sink_button) {
+                     switch (watch_type_) {
+                       case WatchType::kSink:
+                         return sink_button->sink().id == *watch_sink_id_;
+                       case WatchType::kAnyIssue:
+                         return sink_button->sink().issue.has_value();
+                       case WatchType::kAnyRoute:
+                         return !sink_button->sink().route_id.empty();
+                       case WatchType::kNone:
+                       case WatchType::kDialogClosed:
+                         NOTREACHED() << "Invalid WatchType";
+                         return false;
+                     }
+                   }) != sink_buttons.end()) {
+    std::move(*watch_callback_).Run();
+    watch_callback_.reset();
+    watch_sink_id_.reset();
+    watch_type_ = WatchType::kNone;
+    dialog_view->RemoveObserver(this);
+  }
+}
+
+void MediaRouterUiForTest::OnDialogWillClose(CastDialogView* dialog_view) {
+  if (watch_type_ == WatchType::kDialogClosed) {
+    std::move(*watch_callback_).Run();
+    watch_callback_.reset();
+    watch_type_ = WatchType::kNone;
+  }
+  CHECK(!watch_callback_);
+  if (dialog_view)
+    dialog_view->RemoveObserver(this);
+}
+
+CastDialogSinkButton* MediaRouterUiForTest::GetSinkButton(
+    const MediaSink::Id& sink_id) const {
+  CastDialogView* dialog_view = CastDialogView::GetInstance();
+  CHECK(dialog_view);
+  const std::vector<CastDialogSinkButton*>& sink_buttons =
+      dialog_view->sink_buttons_for_test();
+  auto it = std::find_if(sink_buttons.begin(), sink_buttons.end(),
+                         [sink_id](CastDialogSinkButton* sink_button) {
+                           return sink_button->sink().id == sink_id;
+                         });
+  if (it == sink_buttons.end()) {
+    NOTREACHED() << "Sink button not found for sink ID: " << sink_id;
+    return nullptr;
+  } else {
+    return *it;
+  }
+}
+
+void MediaRouterUiForTest::ObserveDialog(
+    WatchType watch_type,
+    base::Optional<MediaSink::Id> sink_id) {
+  CHECK(!watch_sink_id_);
+  CHECK(!watch_callback_);
+  CHECK_EQ(watch_type_, WatchType::kNone);
+  base::RunLoop run_loop;
+  watch_sink_id_ = std::move(sink_id);
+  watch_callback_ = run_loop.QuitClosure();
+  watch_type_ = watch_type;
+
+  CastDialogView* dialog_view = CastDialogView::GetInstance();
+  CHECK(dialog_view);
+  dialog_view->AddObserver(this);
+  // Check if the current dialog state already meets the condition that we are
+  // waiting for.
+  OnDialogModelUpdated(dialog_view);
+
+  run_loop.Run();
+}
+
+}  // namespace media_router
diff --git a/chrome/test/media_router/media_router_ui_for_test.h b/chrome/test/media_router/media_router_ui_for_test.h
new file mode 100644
index 0000000..908afdc
--- /dev/null
+++ b/chrome/test/media_router/media_router_ui_for_test.h
@@ -0,0 +1,94 @@
+// 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_TEST_MEDIA_ROUTER_MEDIA_ROUTER_UI_FOR_TEST_H_
+#define CHROME_TEST_MEDIA_ROUTER_MEDIA_ROUTER_UI_FOR_TEST_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
+#include "chrome/common/media_router/media_sink.h"
+#include "chrome/common/media_router/media_source.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace media_router {
+
+class MediaRouterDialogControllerViews;
+
+class MediaRouterUiForTest
+    : public content::WebContentsUserData<MediaRouterUiForTest>,
+      public CastDialogView::Observer {
+ public:
+  static MediaRouterUiForTest* GetOrCreateForWebContents(
+      content::WebContents* web_contents);
+
+  ~MediaRouterUiForTest() override;
+
+  void ShowDialog();
+  void HideDialog();
+  bool IsDialogShown() const;
+
+  // Chooses the source type in the dialog. Requires that the dialog is shown.
+  void ChooseSourceType(CastDialogView::SourceType source_type);
+
+  // These methods require that the dialog is shown and the specified sink is
+  // shown in the dialog.
+  void StartCasting(const MediaSink::Id& sink_id);
+  void StopCasting(const MediaSink::Id& sink_id);
+  // Stops casting to the first active sink found on the sink list. Requires
+  // that such a sink exists.
+  void StopCasting();
+
+  // Waits until . Requires that the dialog is shown.
+  void WaitForSink(const MediaSink::Id& sink_id);
+  void WaitForAnyIssue();
+  void WaitForAnyRoute();
+  void WaitForDialogClosed();
+
+  // These methods require that the dialog is shown, and the sink specified by
+  // |sink_id| is in the dialog.
+  std::string GetSinkName(const MediaSink::Id& sink_id) const;
+  MediaRoute::Id GetRouteIdForSink(const MediaSink::Id& sink_id) const;
+  std::string GetStatusTextForSink(const MediaSink::Id& sink_id) const;
+  std::string GetIssueTextForSink(const MediaSink::Id& sink_id) const;
+
+  content::WebContents* web_contents() const { return web_contents_; }
+
+ private:
+  friend class content::WebContentsUserData<MediaRouterUiForTest>;
+
+  enum class WatchType { kNone, kSink, kAnyIssue, kAnyRoute, kDialogClosed };
+
+  explicit MediaRouterUiForTest(content::WebContents* web_contents);
+
+  // CastDialogView::Observer:
+  void OnDialogModelUpdated(CastDialogView* dialog_view) override;
+  void OnDialogWillClose(CastDialogView* dialog_view) override;
+
+  CastDialogSinkButton* GetSinkButton(const MediaSink::Id& sink_id) const;
+
+  // Registers itself as an observer to the dialog, and waits until an event
+  // of |watch_type| is observed. |sink_id| should be set only if observing for
+  // a sink.
+  void ObserveDialog(WatchType watch_type,
+                     base::Optional<MediaSink::Id> sink_id = base::nullopt);
+
+  content::WebContents* web_contents_;
+  MediaRouterDialogControllerViews* dialog_controller_;
+
+  base::Optional<MediaSink::Id> watch_sink_id_;
+  base::Optional<base::OnceClosure> watch_callback_;
+  WatchType watch_type_ = WatchType::kNone;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaRouterUiForTest);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_TEST_MEDIA_ROUTER_MEDIA_ROUTER_UI_FOR_TEST_H_
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/Unit.java b/chromecast/base/java/src/org/chromium/chromecast/base/Unit.java
index 48c0f96..d79799e 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/Unit.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/Unit.java
@@ -14,10 +14,9 @@
  * instantiable).
  */
 public final class Unit {
-    private static Unit sInstance;
+    private static final Unit sInstance = new Unit();
     private Unit() {}
     public static Unit unit() {
-        if (sInstance == null) sInstance = new Unit();
         return sInstance;
     }
 }
diff --git a/chromeos/services/ime/public/cpp/rulebased/engine.cc b/chromeos/services/ime/public/cpp/rulebased/engine.cc
index dcd8777..d0fe532 100644
--- a/chromeos/services/ime/public/cpp/rulebased/engine.cc
+++ b/chromeos/services/ime/public/cpp/rulebased/engine.cc
@@ -67,12 +67,6 @@
     return res;
   }
 
-  if (code == "Space") {
-    res.commit_text = context_ + key_char;
-    Reset();
-    return res;
-  }
-
   // Deals with the transforms.
   std::string composition;
   // If history exists, use history to match first.
@@ -111,6 +105,15 @@
     context_ += key_char;
   }
 
+  if (!current_data_->PredictTransform(context_, transat_) ||
+      (!history_ambi_.empty() &&
+       !current_data_->PredictTransform(history_context_ + history_ambi_,
+                                        history_transat_))) {
+    res.commit_text = context_;
+    Reset();
+    return res;
+  }
+
   // Returns the result according to the current state.
   res.composition_text = context_;
   res.commit_text = "";
diff --git a/chromeos/services/ime/public/cpp/rulebased/rulebased_unittest.cc b/chromeos/services/ime/public/cpp/rulebased/rulebased_unittest.cc
index cb83a2b..5bd3d1b 100644
--- a/chromeos/services/ime/public/cpp/rulebased/rulebased_unittest.cc
+++ b/chromeos/services/ime/public/cpp/rulebased/rulebased_unittest.cc
@@ -162,6 +162,48 @@
   EXPECT_EQ("..ba", transformed);
 }
 
+TEST_F(RulebasedImeTest, PredictTransform) {
+  const char* transforms[] = {
+      u8"10",        u8"A",           u8"([aeou])\u001d?`",
+      u8"\\1\u0300", u8"[\\[\\]]{2}", u8"ʘ"};
+  auto data = rulebased::RulesData::Create(us::kKeyMap, false, transforms,
+                                           base::size(transforms), nullptr);
+  bool res = data->PredictTransform("..x", -1);
+  EXPECT_FALSE(res);
+  res = data->PredictTransform(u8"..0", -1);
+  EXPECT_FALSE(res);
+  res = data->PredictTransform(u8"..1", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..100", -1);
+  EXPECT_FALSE(res);
+
+  res = data->PredictTransform(u8"..a", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..a\u001d", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..a\u001d`", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..a`", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..a``", -1);
+
+  EXPECT_FALSE(res);
+  res = data->PredictTransform(u8"..[", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..]", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..[]", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..][", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..[][", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..[][][", -1);
+  EXPECT_TRUE(res);
+  res = data->PredictTransform(u8"..[][][)", -1);
+  EXPECT_FALSE(res);
+}
+
 TEST_F(RulebasedImeTest, Transforms_deva_phone) {
   auto data = rulebased::RulesData::GetById("deva_phone");
   std::string transformed;
diff --git a/chromeos/services/ime/public/cpp/rulebased/rules_data.cc b/chromeos/services/ime/public/cpp/rulebased/rules_data.cc
index 0ccc1c3..74745208 100644
--- a/chromeos/services/ime/public/cpp/rulebased/rules_data.cc
+++ b/chromeos/services/ime/public/cpp/rulebased/rules_data.cc
@@ -5,6 +5,7 @@
 #include "chromeos/services/ime/public/cpp/rulebased/rules_data.h"
 
 #include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chromeos/services/ime/public/cpp/rulebased/def/ar.h"
 #include "chromeos/services/ime/public/cpp/rulebased/def/bn_phone.h"
 #include "chromeos/services/ime/public/cpp/rulebased/def/ckb_ar.h"
@@ -203,24 +204,80 @@
   return key_map;
 }
 
+// The prefix unit can be /[...]/ or a single character
+// (e.g. /[a-z]/, /[\\[\\]]/, /a/, /\\+/, etc.).
+std::string WrapPrefixUnit(const std::string& re_unit) {
+  return "(?:" + re_unit + "|$)";
+}
+
+// Expands the given regexp string so that it can match with prefixes.
+// For example, /(abc)+[a-z]/ can be expanded as:
+// /((?:a|$)(?:b|$)(?:c|$)+(?:[a-z]|$))/
+// So, the string "abcabcx" can match the original regexp, and all its prefix
+// can match the expanded prefix regexp. e.g. "abca".
+std::string Prefixalize(const std::string& re_str) {
+  // Using UTF16 string for iteration in character basis.
+  base::string16 restr16 = base::UTF8ToUTF16(re_str);
+  std::string ret;
+  bool escape = false;
+  int bracket = -1;
+  int brace = -1;
+  for (size_t i = 0; i < restr16.length(); ++i) {
+    std::string ch = base::UTF16ToUTF8(restr16.substr(i, 1));
+    if (escape) {
+      if (bracket < 0) {
+        ret += WrapPrefixUnit("\\" + ch);
+      }
+      escape = false;
+      continue;
+    }
+    // |escape| == false.
+    if (ch == "\\") {
+      escape = true;
+    } else if (ch == "[") {
+      bracket = i;
+    } else if (ch == "{" && bracket < 0) {
+      brace = i;
+    } else if (bracket >= 0) {
+      if (ch == "]") {
+        ret += WrapPrefixUnit(
+            base::UTF16ToUTF8(restr16.substr(bracket, i - bracket + 1)));
+        bracket = -1;
+      }
+    } else if (brace >= 0) {
+      if (ch == "}") {
+        ret += base::UTF16ToUTF8(restr16.substr(brace, i - brace + 1));
+        brace = -1;
+      }
+    } else if (re2::RE2::FullMatch(ch, "[+*?.()|]")) {
+      ret += ch;
+    } else {
+      ret += WrapPrefixUnit(ch);
+    }
+  }
+  return ret;
+}
+
 // Parses the raw transform definition string and generate a transform rule map,
-// and a merged regexp which is used to do the quick check whether a given
-// string can match one of the transform rules.
+// a merged regexp which is used to do the quick check whether a given
+// string can match one of the transform rules, and a prefix regexp which is
+// used to check whether there will be future transform matches.
 // |raw_transforms| is a list of strings, the strings at the even number of
 // index are the regexp to define the rule, and the strings at the odd number of
 // index are the string to replace the matched string. It can contains "\\1",
 // "\\2", etc. to represent the strings in the matched sub groups.
 // e.g. this definition can swap the 2 digits when type "~":
 //      "([0-9])([0-9])~" -> "\\2\\1"
-std::unique_ptr<re2::RE2> ParseTransforms(
+std::pair<std::unique_ptr<re2::RE2>, std::unique_ptr<re2::RE2>> ParseTransforms(
     const char** raw_transforms,
     uint16_t trans_count,
     std::map<uint16_t, TransformRule>& re_map) {
   if (!trans_count)
-    return nullptr;
+    return std::make_pair(nullptr, nullptr);
 
   DCHECK(!(trans_count & 1));
 
+  std::string all_prefixes;
   std::string all_trans;
   uint16_t sum_of_groups = 1;
   for (uint16_t i = 0; i < trans_count; i += 2) {
@@ -230,12 +287,15 @@
     auto from_re = std::make_unique<re2::RE2>(from + "$");
     int group_count = from_re->NumberOfCapturingGroups();
     all_trans += "(" + from + "$)|";
+    all_prefixes += Prefixalize(from) + "|";
 
     re_map[sum_of_groups] = std::make_pair(std::move(from_re), to);
     sum_of_groups += group_count + 1;
   }
-  return std::make_unique<re2::RE2>(
-      all_trans.substr(0, all_trans.length() - 1));
+  return std::make_pair(
+      std::make_unique<re2::RE2>(all_trans.substr(0, all_trans.length() - 1)),
+      std::make_unique<re2::RE2>(
+          all_prefixes.substr(0, all_prefixes.length() - 1)));
 }
 
 // Parses the history prune regexp and returns the RE2 instance.
@@ -266,8 +326,10 @@
   for (uint8_t i = 0; i < kKeyMapCount; ++i) {
     data->key_maps_[i] = ParseKeyMap(key_map[i], is_102_keyboard);
   }
-  data->transform_re_merged_ =
+  auto regexes =
       ParseTransforms(transforms, transforms_count, data->transform_rules_);
+  data->transform_re_merged_ = std::move(regexes.first);
+  data->prefix_re_ = std::move(regexes.second);
   data->history_prune_re_ = ParseHistoryPrune(history_prune);
   return data;
 }
@@ -348,6 +410,23 @@
   return re2::RE2::FullMatch(str, *history_prune_re_);
 }
 
+bool RulesData::PredictTransform(const std::string& str, int transat) const {
+  if (!prefix_re_)
+    return false;
+
+  std::string s = transat > 0 ? str.substr(0, transat) + kTransatDelimit +
+                                    str.substr(transat)
+                              : str;
+  // Try to match the prefix re by all of the suffix of the context string.
+  size_t len = str.length();
+  for (size_t i = 0; i < len; ++i) {
+    std::string suffix = s.substr(len - i - 1);
+    if (re2::RE2::FullMatch(suffix, *prefix_re_))
+      return true;
+  }
+  return false;
+}
+
 }  // namespace rulebased
 }  // namespace ime
 }  // namespace chromeos
diff --git a/chromeos/services/ime/public/cpp/rulebased/rules_data.h b/chromeos/services/ime/public/cpp/rulebased/rules_data.h
index 974db31..6458e19 100644
--- a/chromeos/services/ime/public/cpp/rulebased/rules_data.h
+++ b/chromeos/services/ime/public/cpp/rulebased/rules_data.h
@@ -58,6 +58,10 @@
 
   bool HasTransform() const { return transform_re_merged_.get(); }
 
+  // Predicts whether there will be future transform matches with the given
+  // states of the context string and transat pos.
+  bool PredictTransform(const std::string& str, int transat) const;
+
   bool MatchHistoryPrune(const std::string& str) const;
 
  private:
@@ -72,6 +76,9 @@
   // one of the defined transform rules.
   std::unique_ptr<re2::RE2> transform_re_merged_;
 
+  // The regexp used to check whether there would be future transform matches.
+  std::unique_ptr<re2::RE2> prefix_re_;
+
   // The history prune regexp which is only used by client code of RulesData.
   std::unique_ptr<re2::RE2> history_prune_re_;
 
diff --git a/components/arc/common/app.mojom b/components/arc/common/app.mojom
index 9edf1d58..277080e 100644
--- a/components/arc/common/app.mojom
+++ b/components/arc/common/app.mojom
@@ -156,6 +156,8 @@
   APP_DATA_SEARCH_PROXY_NOT_AVAILABLE = 7,
   // Failed to call globalQuery to GmsCore.
   FAILED_TO_CALL_GLOBALQUERY = 8,
+  // Failed to sign into GmsCore.
+  NOT_SIGNED_IN = 9,
 };
 
 // Describes the category type of app shortcut item.
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
index db50d39..40ef9b4 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
@@ -155,6 +155,28 @@
   }
 }
 
+template <class DataType>
+bool AreLocalUseStatsUpdated(const sync_pb::WalletMetadataSpecifics& remote,
+                             const DataType& local) {
+  return base::checked_cast<size_t>(remote.use_count()) < local.use_count() &&
+         base::Time::FromInternalValue(remote.use_date()) < local.use_date();
+}
+
+bool IsLocalBillingAddressUpdated(
+    const sync_pb::WalletMetadataSpecifics& remote,
+    const CreditCard& local) {
+  std::string remote_billing_address_id;
+  base::Base64Decode(remote.card_billing_address_id(),
+                     &remote_billing_address_id);
+  return local.billing_address_id() != remote_billing_address_id;
+}
+
+bool IsLocalHasConvertedStatusUpdated(
+    const sync_pb::WalletMetadataSpecifics& remote,
+    const AutofillProfile& local) {
+  return remote.address_has_converted() != local.has_converted();
+}
+
 // Merges the metadata of the remote and local versions of the data model.
 void MergeCommonMetadata(
     const sync_pb::WalletMetadataSpecifics& remote_metadata,
@@ -513,9 +535,20 @@
     // Implicitly, we filter out ADD (not in cache) and REMOVE (!data_model()).
     DCHECK(change.type() == AutofillProfileChange::UPDATE);
 
-    AutofillDataModelUpdated(
-        server_id, sync_pb::WalletMetadataSpecifics::ADDRESS,
-        it->GetSpecifics().wallet_metadata(), *change.data_model());
+    const sync_pb::WalletMetadataSpecifics& remote =
+        it->GetSpecifics().wallet_metadata();
+    const AutofillProfile& local = *change.data_model();
+
+    if (!AreLocalUseStatsUpdated(remote, local) &&
+        !IsLocalHasConvertedStatusUpdated(remote, local)) {
+      return;
+    }
+
+    SendChangesToSyncServer(syncer::SyncChangeList(
+        1, syncer::SyncChange(
+               FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
+               BuildSyncData(sync_pb::WalletMetadataSpecifics::ADDRESS,
+                             server_id, local))));
   }
 }
 
@@ -536,9 +569,19 @@
     // Implicitly, we filter out ADD (not in cache) and REMOVE (!data_model()).
     DCHECK(change.type() == AutofillProfileChange::UPDATE);
 
-    AutofillDataModelUpdated(server_id, sync_pb::WalletMetadataSpecifics::CARD,
-                             it->GetSpecifics().wallet_metadata(),
-                             *change.data_model());
+    const sync_pb::WalletMetadataSpecifics& remote =
+        it->GetSpecifics().wallet_metadata();
+    const CreditCard& local = *change.data_model();
+    if (!AreLocalUseStatsUpdated(remote, local) &&
+        !IsLocalBillingAddressUpdated(remote, local)) {
+      return;
+    }
+
+    SendChangesToSyncServer(syncer::SyncChangeList(
+        1,
+        syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
+                           BuildSyncData(sync_pb::WalletMetadataSpecifics::CARD,
+                                         server_id, local))));
   }
 }
 
@@ -718,18 +761,4 @@
   return result;
 }
 
-template <class DataType>
-void AutofillWalletMetadataSyncableService::AutofillDataModelUpdated(
-    const std::string& server_id,
-    const sync_pb::WalletMetadataSpecifics::Type& type,
-    const sync_pb::WalletMetadataSpecifics& remote,
-    const DataType& local) {
-  if (base::checked_cast<size_t>(remote.use_count()) < local.use_count() &&
-      base::Time::FromInternalValue(remote.use_date()) < local.use_date()) {
-    SendChangesToSyncServer(syncer::SyncChangeList(
-        1, syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
-                              BuildSyncData(remote.type(), server_id, local))));
-  }
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
index dad9a99f7..4df82f0 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
@@ -130,15 +130,6 @@
   // is not present locally.
   syncer::SyncMergeResult MergeData(const syncer::SyncDataList& sync_data);
 
-  // Sends the autofill data model updates to the sync server if the local
-  // version is more recent. Used for both profiles and credit cards.
-  template <class DataType>
-  void AutofillDataModelUpdated(
-      const std::string& server_id,
-      const sync_pb::WalletMetadataSpecifics::Type& type,
-      const sync_pb::WalletMetadataSpecifics& remote,
-      const DataType& local);
-
   base::ThreadChecker thread_checker_;
   AutofillWebDataBackend* web_data_backend_;  // Weak ref.
   ScopedObserver<AutofillWebDataBackend, AutofillWalletMetadataSyncableService>
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
index 49913182..bd930b3 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
@@ -476,8 +476,8 @@
   MergeMetadata(&local_, &remote_);
 }
 
-// Verify that lower values of metadata are not sent to the sync server when
-// local metadata is updated.
+// Verify that lower or equal values of metadata are not sent to the sync server
+// when local metadata is updated.
 TEST_F(AutofillWalletMetadataSyncableServiceTest,
        DontSendLowerValueToServerOnSingleChange) {
   local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
@@ -485,8 +485,8 @@
   remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
   remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
   MergeMetadata(&local_, &remote_);
-  AutofillProfile address = BuildAddress(kAddr1, 0, 0, false);
-  CreditCard card = BuildCard(kCard1, 0, 0, kAddr2);
+  AutofillProfile address = BuildAddress(kAddr1, 0, 0, true);
+  CreditCard card = BuildCard(kCard1, 3, 4, kAddr1);
 
   EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
 
@@ -525,6 +525,35 @@
       CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
 }
 
+// Verify that other changed metadata elements are sent to the sync server when
+// local metadata is updated.
+TEST_F(AutofillWalletMetadataSyncableServiceTest,
+       SendChangedMetadataToServerOnLocalSingleChange) {
+  local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+  local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+  remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+  remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+  MergeMetadata(&local_, &remote_);
+  AutofillProfile address = BuildAddress(kAddr1, 1, 2, true);
+  CreditCard card = BuildCard(kCard1, 3, 4, kAddr2);
+
+  EXPECT_CALL(
+      local_,
+      SendChangesToSyncServer(ElementsAre(SyncAddressChangeAndDataMatch(
+          syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+          sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 1, 2, true))));
+  EXPECT_CALL(local_,
+              SendChangesToSyncServer(ElementsAre(SyncCardChangeAndDataMatch(
+                  syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+                  sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 3, 4,
+                  kAddr2Utf8))));
+
+  local_.AutofillProfileChanged(AutofillProfileChange(
+      AutofillProfileChange::UPDATE, address.guid(), &address));
+  local_.CreditCardChanged(
+      CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
+}
+
 // Verify that one-off addition of metadata is not sent to the sync
 // server. Metadata add and delete trigger multiple changes notification
 // instead.
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index afc9c542..91e066a 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -55,9 +55,9 @@
   virtual void WaitForElement(const std::vector<std::string>& selectors,
                               base::OnceCallback<void(bool)> callback) = 0;
 
-  // Click the element given by |selectors| on the web page.
-  virtual void ClickElement(const std::vector<std::string>& selectors,
-                            base::OnceCallback<void(bool)> callback) = 0;
+  // Click or tap the element given by |selectors| on the web page.
+  virtual void ClickOrTapElement(const std::vector<std::string>& selectors,
+                                 base::OnceCallback<void(bool)> callback) = 0;
 
   // Ask user to choose an address in personal data manager. GUID of the chosen
   // address will be returned through callback, otherwise empty string if the
diff --git a/components/autofill_assistant/browser/actions/click_action.cc b/components/autofill_assistant/browser/actions/click_action.cc
index 47fa1cc..d9de7a5 100644
--- a/components/autofill_assistant/browser/actions/click_action.cc
+++ b/components/autofill_assistant/browser/actions/click_action.cc
@@ -38,7 +38,7 @@
     return;
   }
 
-  delegate->ClickElement(
+  delegate->ClickOrTapElement(
       ExtractVector(proto_.click().element_to_click().selectors()),
       base::BindOnce(&::autofill_assistant::ClickAction::OnClick,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
diff --git a/components/autofill_assistant/browser/actions/click_action.h b/components/autofill_assistant/browser/actions/click_action.h
index df7d886..55d0f01e 100644
--- a/components/autofill_assistant/browser/actions/click_action.h
+++ b/components/autofill_assistant/browser/actions/click_action.h
@@ -13,7 +13,8 @@
 #include "components/autofill_assistant/browser/actions/action.h"
 
 namespace autofill_assistant {
-// An action to perform a mouse left button click on a given element on Web.
+// An action to perform a mouse left button click on a given element on Web,
+// which is implemented as a touch tap on Mobile.
 class ClickAction : public Action {
  public:
   explicit ClickAction(const ActionProto& proto);
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 1fa91da..8d22fe5 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -34,7 +34,7 @@
                     base::OnceCallback<void(bool)>&));
 
   MOCK_METHOD1(ShowStatusMessage, void(const std::string& message));
-  MOCK_METHOD2(ClickElement,
+  MOCK_METHOD2(ClickOrTapElement,
                void(const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)> callback));
 
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index d3bcd7e..c70fe75 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -45,13 +45,16 @@
 void Controller::CreateForWebContents(
     content::WebContents* web_contents,
     std::unique_ptr<Client> client,
-    std::unique_ptr<std::map<std::string, std::string>> parameters) {
+    std::unique_ptr<std::map<std::string, std::string>> parameters,
+    const std::string& locale,
+    const std::string& country_code) {
   // Get the key early since |client| will be invalidated when moved below.
   GURL server_url(client->GetServerUrl());
   DCHECK(server_url.is_valid());
+
   std::unique_ptr<Service> service = std::make_unique<Service>(
       client->GetApiKey(), server_url, web_contents->GetBrowserContext(),
-      client->GetAccessTokenFetcher());
+      client->GetAccessTokenFetcher(), locale, country_code);
   new Controller(web_contents, std::move(client),
                  WebController::CreateForWebContents(web_contents),
                  std::move(service), std::move(parameters));
@@ -271,6 +274,12 @@
   GetOrCheckScripts(web_contents()->GetLastCommittedURL());
 }
 
+void Controller::GiveUp() {
+  GetUiController()->ShowStatusMessage(
+      l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP));
+  GetUiController()->ShutdownGracefully();
+}
+
 void Controller::OnClickOverlay() {
   GetUiController()->HideOverlay();
   // TODO(crbug.com/806868): Stop executing scripts.
@@ -316,12 +325,6 @@
   delete this;
 }
 
-void Controller::OnGiveUp() {
-  GetUiController()->ShowStatusMessage(
-      l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP));
-  GetUiController()->ShutdownGracefully();
-}
-
 void Controller::DidAttachInterstitialPage() {
   GetUiController()->Shutdown();
 }
@@ -357,9 +360,7 @@
 
   // We're navigated to a page that has no scripts or the scripts have reached a
   // state from which they cannot recover through a DOM change.
-  GetUiController()->ShowStatusMessage(
-      l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP));
-  GetUiController()->ShutdownGracefully();
+  GiveUp();
   return;
 }
 
@@ -447,7 +448,7 @@
       !script_tracker_->running() && !navigation_handle->WasServerRedirect() &&
       !navigation_handle->IsSameDocument() &&
       !navigation_handle->IsRendererInitiated()) {
-    OnGiveUp();
+    GiveUp();
     return;
   }
 }
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 95850bd3..4409d8b 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -43,7 +43,9 @@
   static void CreateForWebContents(
       content::WebContents* web_contents,
       std::unique_ptr<Client> client,
-      std::unique_ptr<std::map<std::string, std::string>> parameters);
+      std::unique_ptr<std::map<std::string, std::string>> parameters,
+      const std::string& locale,
+      const std::string& country_code);
 
   // Overrides ScriptExecutorDelegate:
   Service* GetService() override;
@@ -78,12 +80,12 @@
   void StartPeriodicScriptChecks();
   void StopPeriodicScriptChecks();
   void OnPeriodicScriptCheck();
+  void GiveUp();
 
   // Overrides content::UiDelegate:
   void Start(const GURL& initialUrl) override;
   void OnClickOverlay() override;
   void OnDestroy() override;
-  void OnGiveUp() override;
   void OnScriptSelected(const std::string& script_path) override;
   std::string GetDebugContext() override;
 
diff --git a/components/autofill_assistant/browser/mock_service.cc b/components/autofill_assistant/browser/mock_service.cc
index e6a28dc..db67dd8 100644
--- a/components/autofill_assistant/browser/mock_service.cc
+++ b/components/autofill_assistant/browser/mock_service.cc
@@ -9,7 +9,7 @@
 namespace autofill_assistant {
 
 MockService::MockService()
-    : Service("api_key", GURL("http://fake"), nullptr, nullptr) {}
+    : Service("api_key", GURL("http://fake"), nullptr, nullptr, "en_US", "") {}
 MockService::~MockService() {}
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/mock_web_controller.h b/components/autofill_assistant/browser/mock_web_controller.h
index 715ac01..9b5a58a3 100644
--- a/components/autofill_assistant/browser/mock_web_controller.h
+++ b/components/autofill_assistant/browser/mock_web_controller.h
@@ -24,13 +24,13 @@
 
   MOCK_METHOD1(LoadURL, void(const GURL&));
 
-  void ClickElement(const std::vector<std::string>& selectors,
-                    base::OnceCallback<void(bool)> callback) override {
+  void ClickOrTapElement(const std::vector<std::string>& selectors,
+                         base::OnceCallback<void(bool)> callback) override {
     // Transforming callback into a references allows using RunOnceCallback on
     // the argument.
-    OnClickElement(selectors, callback);
+    OnClickOrTapElement(selectors, callback);
   }
-  MOCK_METHOD2(OnClickElement,
+  MOCK_METHOD2(OnClickOrTapElement,
                void(const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)>& callback));
 
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 5756db0..09732960 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -8,7 +8,6 @@
 
 #include "base/feature_list.h"
 #include "base/logging.h"
-#include "base/metrics/field_trial.h"
 #include "components/autofill_assistant/browser/actions/autofill_action.h"
 #include "components/autofill_assistant/browser/actions/click_action.h"
 #include "components/autofill_assistant/browser/actions/focus_element_action.h"
@@ -27,7 +26,6 @@
 #include "components/autofill_assistant/browser/actions/upload_dom_action.h"
 #include "components/autofill_assistant/browser/actions/wait_for_dom_action.h"
 #include "components/autofill_assistant/browser/service.pb.h"
-#include "components/version_info/version_info.h"
 #include "url/gurl.h"
 
 namespace autofill_assistant {
@@ -46,32 +44,18 @@
   }
 }
 
-// Fills the destination ClientContextProto fields.
-void FillClientContext(ClientContextProto* destination) {
-  destination->mutable_chrome()->set_chrome_version(
-      version_info::GetProductNameAndVersionForUserAgent());
-
-  base::FieldTrial::ActiveGroups active_groups;
-  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-  for (const auto& group : active_groups) {
-    FieldTrialProto* field_trial =
-        destination->mutable_chrome()->add_active_field_trials();
-    field_trial->set_trial_name(group.trial_name);
-    field_trial->set_group_name(group.group_name);
-  }
-}
-
 }  // namespace
 
 // static
 std::string ProtocolUtils::CreateGetScriptsRequest(
     const GURL& url,
-    const std::map<std::string, std::string>& parameters) {
+    const std::map<std::string, std::string>& parameters,
+    const ClientContextProto& client_context) {
   DCHECK(!url.is_empty());
 
   SupportsScriptRequestProto script_proto;
   script_proto.set_url(url.spec());
-  FillClientContext(script_proto.mutable_client_context());
+  script_proto.mutable_client_context()->CopyFrom(client_context);
   AddScriptParametersToProto(parameters,
                              script_proto.mutable_script_parameters());
   std::string serialized_script_proto;
@@ -123,7 +107,8 @@
     const std::string& script_path,
     const GURL& url,
     const std::map<std::string, std::string>& parameters,
-    const std::string& server_payload) {
+    const std::string& server_payload,
+    const ClientContextProto& client_context) {
   ScriptActionRequestProto request_proto;
   InitialScriptActionsRequestProto* initial_request_proto =
       request_proto.mutable_initial_request();
@@ -134,7 +119,7 @@
   query->set_policy(PolicyType::SCRIPT);
   AddScriptParametersToProto(
       parameters, initial_request_proto->mutable_script_parameters());
-  FillClientContext(request_proto.mutable_client_context());
+  request_proto.mutable_client_context()->CopyFrom(client_context);
 
   if (!server_payload.empty()) {
     request_proto.set_server_payload(server_payload);
@@ -150,7 +135,8 @@
 // static
 std::string ProtocolUtils::CreateNextScriptActionsRequest(
     const std::string& previous_server_payload,
-    const std::vector<ProcessedActionProto>& processed_actions) {
+    const std::vector<ProcessedActionProto>& processed_actions,
+    const ClientContextProto& client_context) {
   ScriptActionRequestProto request_proto;
   request_proto.set_server_payload(previous_server_payload);
   NextScriptActionsRequestProto* next_request =
@@ -158,7 +144,7 @@
   for (const auto& processed_action : processed_actions) {
     next_request->add_processed_actions()->MergeFrom(processed_action);
   }
-  FillClientContext(request_proto.mutable_client_context());
+  request_proto.mutable_client_context()->CopyFrom(client_context);
   std::string serialized_request_proto;
   bool success = request_proto.SerializeToString(&serialized_request_proto);
   DCHECK(success);
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index 854e02f..f0212d3 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -25,7 +25,8 @@
   // |url|.
   static std::string CreateGetScriptsRequest(
       const GURL& url,
-      const std::map<std::string, std::string>& parameters);
+      const std::map<std::string, std::string>& parameters,
+      const ClientContextProto& client_context);
 
   using Scripts = std::map<Script*, std::unique_ptr<Script>>;
   // Parse assistant scripts from the given |response|, which should not be an
@@ -44,12 +45,14 @@
       const std::string& script_path,
       const GURL& url,
       const std::map<std::string, std::string>& parameters,
-      const std::string& server_payload);
+      const std::string& server_payload,
+      const ClientContextProto& client_context);
 
   // Create request to get next sequence of actions for a script.
   static std::string CreateNextScriptActionsRequest(
       const std::string& previous_server_payload,
-      const std::vector<ProcessedActionProto>& processed_actions);
+      const std::vector<ProcessedActionProto>& processed_actions,
+      const ClientContextProto& client_context);
 
   // Parse actions from the given |response|, which can be an empty string.
   //
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index fd005f0..7b065fc 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -17,6 +17,16 @@
 using ::testing::IsEmpty;
 using ::testing::ElementsAre;
 
+ClientContextProto CreateClientContextProto() {
+  ClientContextProto context;
+  context.mutable_chrome()->set_chrome_version("v");
+  return context;
+}
+
+void AssertClientContext(const ClientContextProto& context) {
+  EXPECT_EQ("v", context.chrome().chrome_version());
+}
+
 TEST(ProtocolUtilsTest, NoScripts) {
   std::vector<std::unique_ptr<Script>> scripts;
   EXPECT_TRUE(ProtocolUtils::ParseScripts("", &scripts));
@@ -76,10 +86,9 @@
   EXPECT_TRUE(
       request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
           "script_path", GURL("http://example.com/"), parameters,
-          "server_payload")));
+          "server_payload", CreateClientContextProto())));
 
-  EXPECT_THAT(request.client_context().chrome().chrome_version(),
-              Not(IsEmpty()));
+  AssertClientContext(request.client_context());
 
   const InitialScriptActionsRequestProto& initial = request.initial_request();
   EXPECT_THAT(initial.query().script_path(), ElementsAre("script_path"));
@@ -99,11 +108,11 @@
 
   SupportsScriptRequestProto request;
   EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
-      GURL("http://example.com/"), parameters)));
+      GURL("http://example.com/"), parameters, CreateClientContextProto())));
+
+  AssertClientContext(request.client_context());
 
   EXPECT_EQ("http://example.com/", request.url());
-  EXPECT_THAT(request.client_context().chrome().chrome_version(),
-              Not(IsEmpty()));
   ASSERT_EQ(2, request.script_parameters_size());
   EXPECT_EQ("a", request.script_parameters(0).name());
   EXPECT_EQ("b", request.script_parameters(0).value());
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 6b560310..bb3260f9 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -87,9 +87,11 @@
   delegate_->GetUiController()->ShowStatusMessage(message);
 }
 
-void ScriptExecutor::ClickElement(const std::vector<std::string>& selectors,
-                                  base::OnceCallback<void(bool)> callback) {
-  delegate_->GetWebController()->ClickElement(selectors, std::move(callback));
+void ScriptExecutor::ClickOrTapElement(
+    const std::vector<std::string>& selectors,
+    base::OnceCallback<void(bool)> callback) {
+  delegate_->GetWebController()->ClickOrTapElement(selectors,
+                                                   std::move(callback));
 }
 
 void ScriptExecutor::GetPaymentInformation(
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index c69845d..08b57113 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -75,8 +75,8 @@
   void WaitForElement(const std::vector<std::string>& selectors,
                       base::OnceCallback<void(bool)> callback) override;
   void ShowStatusMessage(const std::string& message) override;
-  void ClickElement(const std::vector<std::string>& selectors,
-                    base::OnceCallback<void(bool)> callback) override;
+  void ClickOrTapElement(const std::vector<std::string>& selectors,
+                         base::OnceCallback<void(bool)> callback) override;
   void GetPaymentInformation(
       payments::mojom::PaymentOptionsPtr payment_options,
       base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 9ca1fb80..de199a5f 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -45,7 +45,7 @@
     // fail. The following makes a click action fail immediately
     ON_CALL(mock_web_controller_, OnElementCheck(_, _, _))
         .WillByDefault(RunOnceCallback<2>(true));
-    ON_CALL(mock_web_controller_, OnClickElement(_, _))
+    ON_CALL(mock_web_controller_, OnClickOrTapElement(_, _))
         .WillByDefault(RunOnceCallback<1>(false));
     ON_CALL(mock_web_controller_, OnFocusElement(_, _))
         .WillByDefault(RunOnceCallback<1>(true));
diff --git a/components/autofill_assistant/browser/service.cc b/components/autofill_assistant/browser/service.cc
index b372f42..7669b148 100644
--- a/components/autofill_assistant/browser/service.cc
+++ b/components/autofill_assistant/browser/service.cc
@@ -9,9 +9,11 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/metrics/field_trial.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "components/autofill_assistant/browser/protocol_utils.h"
+#include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "net/base/load_flags.h"
@@ -56,7 +58,9 @@
 Service::Service(const std::string& api_key,
                  const GURL& server_url,
                  content::BrowserContext* context,
-                 AccessTokenFetcher* access_token_fetcher)
+                 AccessTokenFetcher* access_token_fetcher,
+                 const std::string& locale,
+                 const std::string& country_code)
     : context_(context),
       api_key_(api_key),
       access_token_fetcher_(access_token_fetcher),
@@ -64,6 +68,7 @@
       auth_enabled_("false" !=
                     base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                         switches::kAutofillAssistantAuth)),
+      client_context_(CreateClientContext(locale, country_code)),
       weak_ptr_factory_(this) {
   DCHECK(server_url.is_valid());
 
@@ -84,9 +89,10 @@
     ResponseCallback callback) {
   DCHECK(url.is_valid());
 
-  SendRequest(AddLoader(script_server_url_,
-                        ProtocolUtils::CreateGetScriptsRequest(url, parameters),
-                        std::move(callback)));
+  SendRequest(AddLoader(
+      script_server_url_,
+      ProtocolUtils::CreateGetScriptsRequest(url, parameters, client_context_),
+      std::move(callback)));
 }
 
 void Service::GetActions(const std::string& script_path,
@@ -96,10 +102,11 @@
                          ResponseCallback callback) {
   DCHECK(!script_path.empty());
 
-  SendRequest(AddLoader(script_action_server_url_,
-                        ProtocolUtils::CreateInitialScriptActionsRequest(
-                            script_path, url, parameters, server_payload),
-                        std::move(callback)));
+  SendRequest(AddLoader(
+      script_action_server_url_,
+      ProtocolUtils::CreateInitialScriptActionsRequest(
+          script_path, url, parameters, server_payload, client_context_),
+      std::move(callback)));
 }
 
 void Service::GetNextActions(
@@ -108,10 +115,11 @@
     ResponseCallback callback) {
   DCHECK(!previous_server_payload.empty());
 
-  SendRequest(AddLoader(script_action_server_url_,
-                        ProtocolUtils::CreateNextScriptActionsRequest(
-                            previous_server_payload, processed_actions),
-                        std::move(callback)));
+  SendRequest(AddLoader(
+      script_action_server_url_,
+      ProtocolUtils::CreateNextScriptActionsRequest(
+          previous_server_payload, processed_actions, client_context_),
+      std::move(callback)));
 }
 
 void Service::SendRequest(Loader* loader) {
@@ -246,4 +254,25 @@
   }
 }
 
+// static
+ClientContextProto Service::CreateClientContext(
+    const std::string& locale,
+    const std::string& country_code) {
+  ClientContextProto context;
+  context.mutable_chrome()->set_chrome_version(
+      version_info::GetProductNameAndVersionForUserAgent());
+  context.set_locale(locale);
+  context.set_country(country_code);
+
+  base::FieldTrial::ActiveGroups active_groups;
+  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+  for (const auto& group : active_groups) {
+    FieldTrialProto* field_trial =
+        context.mutable_chrome()->add_active_field_trials();
+    field_trial->set_trial_name(group.trial_name);
+    field_trial->set_group_name(group.group_name);
+  }
+  return context;
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.h b/components/autofill_assistant/browser/service.h
index 7fa893a..6723daad 100644
--- a/components/autofill_assistant/browser/service.h
+++ b/components/autofill_assistant/browser/service.h
@@ -33,7 +33,9 @@
   Service(const std::string& api_key,
           const GURL& server_url,
           content::BrowserContext* context,
-          AccessTokenFetcher* token_fetcher);
+          AccessTokenFetcher* token_fetcher,
+          const std::string& locale,
+          const std::string& country_code);
   virtual ~Service();
 
   using ResponseCallback =
@@ -89,6 +91,11 @@
   void FetchAccessToken();
   void OnFetchAccessToken(bool success, const std::string& access_token);
 
+  // Creates and fills a client context protobuf message.
+  static ClientContextProto CreateClientContext(
+      const std::string& locale,
+      const std::string& country_code);
+
   content::BrowserContext* context_;
   GURL script_server_url_;
   GURL script_action_server_url_;
@@ -112,6 +119,10 @@
   // invalidated.
   std::string access_token_;
 
+  // The client context is cached here to avoid having to recreate it for
+  // every message.
+  const ClientContextProto client_context_;
+
   base::WeakPtrFactory<Service> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Service);
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index e9a2cc62..fd106fd 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -22,6 +22,16 @@
     repeated FieldTrialProto active_field_trials = 2;
   }
   oneof client { Chrome chrome = 1; }
+
+  // locale should be a comma separated list of language tags. Each tag should
+  // be a well-formed IETF BCP 47 language tag with language and country code
+  // (e.g., "en-US").
+  // The intent is to communicate the client language preferences to the server.
+  optional string locale = 5;
+
+  // country should be a country code as defined by ISO 3166-1-alpha-2.
+  // The intent is to communicate the client's location to the server.
+  optional string country = 6;
 }
 
 // Get the list of scripts that can potentially be run on a url.
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index 5310dd9..ea7b6fd 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -23,11 +23,6 @@
   // detached from the associated activity.
   virtual void OnDestroy() = 0;
 
-  // Called when the Autofill Assistant should display a message saying that
-  // it's shutting down and then shut down, because something happened that
-  // scripts cannot handle, such as a new window being opened.
-  virtual void OnGiveUp() = 0;
-
   // Called when a script was selected for execution.
   virtual void OnScriptSelected(const std::string& script_path) = 0;
 
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index 51d93cb0..e1d98c4 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/credit_card.h"
@@ -165,6 +166,17 @@
       content::NavigationController::LoadURLParams(url));
 }
 
+void WebController::ClickOrTapElement(const std::vector<std::string>& selectors,
+                                      base::OnceCallback<void(bool)> callback) {
+#if defined(OS_ANDROID)
+  TapElement(selectors, std::move(callback));
+#else
+  // TODO(crbug.com/806868): Remove 'ClickElement' since this feature is only
+  // available on Android.
+  ClickElement(selectors, std::move(callback));
+#endif
+}
+
 void WebController::ClickElement(const std::vector<std::string>& selectors,
                                  base::OnceCallback<void(bool)> callback) {
   DCHECK(!selectors.empty());
@@ -960,6 +972,14 @@
     return;
   }
 
+  // TODO(crbug.com/806868): Substitute mouse click with touch tap.
+  //
+  // Note that 'KeyDown' will not be handled by the element immediately after
+  // touch tap. Add ~1 second delay before 'DispatchKeyDownEvent' in
+  // 'OnClickOrTapElementForDispatchKeyEvent' solved the problem, needs more
+  // investigation for this timing issue. One possible reason is that events
+  // from different devices are not guarranteed to be handled in order (needs a
+  // way to make sure previous events have been handled).
   ClickElement(selectors,
                base::BindOnce(&WebController::OnClickElementForDispatchKeyEvent,
                               weak_ptr_factory_.GetWeakPtr(), value,
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index c38548d..0fba1ab9 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -63,18 +63,12 @@
   // been loaded.
   virtual void LoadURL(const GURL& url);
 
-  // Perform a mouse left button click on the element given by |selectors| and
-  // return the result through callback.
+  // Perform a mouse left button click or a touch tap on the element given by
+  // |selectors| and return the result through callback.
   // CSS selectors in |selectors| are ordered from top frame to the frame
   // contains the element and the element.
-  virtual void ClickElement(const std::vector<std::string>& selectors,
-                            base::OnceCallback<void(bool)> callback);
-
-  // Perform a touch tap on the element given by |selectors| and return the
-  // result through callback. CSS selectors in |selectors| are ordered from top
-  // frame to the frame contains the element and the element.
-  virtual void TapElement(const std::vector<std::string>& selectors,
-                          base::OnceCallback<void(bool)> callback);
+  virtual void ClickOrTapElement(const std::vector<std::string>& selectors,
+                                 base::OnceCallback<void(bool)> callback);
 
   // Fill the address form given by |selectors| with the given address
   // |profile|.
@@ -165,6 +159,19 @@
  private:
   friend class WebControllerBrowserTest;
 
+  // Perform a mouse left button click on the element given by |selectors| and
+  // return the result through callback.
+  // CSS selectors in |selectors| are ordered from top frame to the frame
+  // contains the element and the element.
+  void ClickElement(const std::vector<std::string>& selectors,
+                    base::OnceCallback<void(bool)> callback);
+
+  // Perform a touch tap on the element given by |selectors| and return the
+  // result through callback. CSS selectors in |selectors| are ordered from top
+  // frame to the frame contains the element and the element.
+  void TapElement(const std::vector<std::string>& selectors,
+                  base::OnceCallback<void(bool)> callback);
+
   struct FindElementResult {
     FindElementResult() = default;
     ~FindElementResult() = default;
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 536525e..4efa913 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -26,6 +26,7 @@
 #include "components/history/core/browser/sync/typed_url_model_type_controller.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/sync/password_data_type_controller.h"
+#include "components/password_manager/core/browser/sync/password_model_type_controller.h"
 #include "components/password_manager/core/browser/sync/password_syncable_service_based_model_type_controller.h"
 #include "components/prefs/pref_service.h"
 #include "components/reading_list/features/reading_list_switches.h"
@@ -322,7 +323,12 @@
   // Password sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::PASSWORDS)) {
-    if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSPasswords)) {
+    if (base::FeatureList::IsEnabled(switches::kSyncUSSPasswords)) {
+      controllers.push_back(
+          std::make_unique<password_manager::PasswordModelTypeController>(
+              password_store_->CreateSyncControllerDelegate(), sync_client_));
+    } else if (base::FeatureList::IsEnabled(
+                   switches::kSyncPseudoUSSPasswords)) {
       controllers.push_back(
           std::make_unique<password_manager::
                                PasswordSyncableServiceBasedModelTypeController>(
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 3b00214..2ea8f1a 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -470,6 +470,7 @@
   script = "//components/cronet/tools/generate_proguard_file.py"
   sources = [
     "//base/android/proguard/chromium_code.flags",
+    "//base/android/proguard/explicit_jni_registration.flags",
     "//components/cronet/android/cronet_impl_native_proguard.cfg",
   ]
   outputs = [
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 77fbe8e..86bb3ed 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -35,6 +35,8 @@
     "server.h",
     "server_util.cc",
     "server_util.h",
+    "wayland_input_delegate.cc",
+    "wayland_input_delegate.h",
     "zcr_notification_shell.cc",
     "zcr_notification_shell.h",
     "zwp_text_input_manager.cc",
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index af95f83..3361faeb 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -95,6 +95,7 @@
 #include "components/exo/touch_delegate.h"
 #include "components/exo/touch_stylus_delegate.h"
 #include "components/exo/wayland/server_util.h"
+#include "components/exo/wayland/wayland_input_delegate.h"
 #include "components/exo/wayland/zcr_notification_shell.h"
 #include "components/exo/wayland/zwp_text_input_manager.h"
 #include "components/exo/wm_helper.h"
@@ -179,41 +180,6 @@
   return WMHelper::GetInstance()->GetDefaultDeviceScaleFactor();
 }
 
-class WaylandInputDelegate {
- public:
-  class Observer {
-   public:
-    virtual void OnDelegateDestroying(WaylandInputDelegate* delegate) = 0;
-    virtual void OnSendTimestamp(base::TimeTicks time_stamp) = 0;
-
-   protected:
-    virtual ~Observer() = default;
-  };
-
-  void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
-
-  void RemoveObserver(Observer* observer) {
-    observers_.RemoveObserver(observer);
-  }
-
-  void SendTimestamp(base::TimeTicks time_stamp) {
-    for (auto& observer : observers_)
-      observer.OnSendTimestamp(time_stamp);
-  }
-
- protected:
-  WaylandInputDelegate() = default;
-  virtual ~WaylandInputDelegate() {
-    for (auto& observer : observers_)
-      observer.OnDelegateDestroying(this);
-  }
-
- private:
-  base::ObserverList<Observer>::Unchecked observers_;
-
-  DISALLOW_COPY_AND_ASSIGN(WaylandInputDelegate);
-};
-
 uint32_t WaylandDataDeviceManagerDndAction(DndAction action) {
   switch (action) {
     case DndAction::kNone:
diff --git a/components/exo/wayland/wayland_input_delegate.cc b/components/exo/wayland/wayland_input_delegate.cc
new file mode 100644
index 0000000..4bfef5cd
--- /dev/null
+++ b/components/exo/wayland/wayland_input_delegate.cc
@@ -0,0 +1,31 @@
+// 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/exo/wayland/wayland_input_delegate.h"
+
+namespace exo {
+namespace wayland {
+
+WaylandInputDelegate::WaylandInputDelegate() = default;
+
+void WaylandInputDelegate::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void WaylandInputDelegate::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void WaylandInputDelegate::SendTimestamp(base::TimeTicks time_stamp) {
+  for (auto& observer : observers_)
+    observer.OnSendTimestamp(time_stamp);
+}
+
+WaylandInputDelegate::~WaylandInputDelegate() {
+  for (auto& observer : observers_)
+    observer.OnDelegateDestroying(this);
+}
+
+}  // namespace wayland
+}  // namespace exo
diff --git a/components/exo/wayland/wayland_input_delegate.h b/components/exo/wayland/wayland_input_delegate.h
new file mode 100644
index 0000000..29c3fc0
--- /dev/null
+++ b/components/exo/wayland/wayland_input_delegate.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 COMPONENTS_EXO_WAYLAND_WAYLAND_INPUT_DELEGATE_H_
+#define COMPONENTS_EXO_WAYLAND_WAYLAND_INPUT_DELEGATE_H_
+
+#include "base/observer_list.h"
+#include "base/time/time.h"
+
+namespace exo {
+namespace wayland {
+
+class WaylandInputDelegate {
+ public:
+  class Observer {
+   public:
+    virtual void OnDelegateDestroying(WaylandInputDelegate* delegate) = 0;
+    virtual void OnSendTimestamp(base::TimeTicks time_stamp) = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  void AddObserver(Observer* observer);
+
+  void RemoveObserver(Observer* observer);
+
+  void SendTimestamp(base::TimeTicks time_stamp);
+
+ protected:
+  WaylandInputDelegate();
+  virtual ~WaylandInputDelegate();
+
+ private:
+  base::ObserverList<Observer>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandInputDelegate);
+};
+
+}  // namespace wayland
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_WAYLAND_WAYLAND_INPUT_DELEGATE_H_
diff --git a/components/flags_ui/flags_state.cc b/components/flags_ui/flags_state.cc
index 7218bfd..774b38b 100644
--- a/components/flags_ui/flags_state.cc
+++ b/components/flags_ui/flags_state.cc
@@ -327,46 +327,30 @@
                            disable_features_flag_name);
 }
 
-std::set<std::string> FlagsState::GetSwitchesFromFlags(
-    FlagsStorage* flags_storage) {
+void FlagsState::GetSwitchesAndFeaturesFromFlags(
+    FlagsStorage* flags_storage,
+    std::set<std::string>* switches,
+    std::set<std::string>* features) const {
   std::set<std::string> enabled_entries;
   std::map<std::string, SwitchEntry> name_to_switch_map;
   GenerateFlagsToSwitchesMapping(flags_storage, &enabled_entries,
                                  &name_to_switch_map);
 
-  std::set<std::string> switches;
   for (const std::string& entry_name : enabled_entries) {
     const auto& entry_it = name_to_switch_map.find(entry_name);
     DCHECK(entry_it != name_to_switch_map.end());
 
     const SwitchEntry& entry = entry_it->second;
     if (!entry.switch_name.empty())
-      switches.insert("--" + entry.switch_name);
-  }
-  return switches;
-}
+      switches->insert("--" + entry.switch_name);
 
-std::set<std::string> FlagsState::GetFeaturesFromFlags(
-    FlagsStorage* flags_storage) {
-  std::set<std::string> enabled_entries;
-  std::map<std::string, SwitchEntry> name_to_switch_map;
-  GenerateFlagsToSwitchesMapping(flags_storage, &enabled_entries,
-                                 &name_to_switch_map);
-
-  std::set<std::string> features;
-  for (const std::string& entry_name : enabled_entries) {
-    const auto& entry_it = name_to_switch_map.find(entry_name);
-    DCHECK(entry_it != name_to_switch_map.end());
-
-    const SwitchEntry& entry = entry_it->second;
     if (!entry.feature_name.empty()) {
       if (entry.feature_state)
-        features.insert(entry.feature_name + ":enabled");
+        features->insert(entry.feature_name + ":enabled");
       else
-        features.insert(entry.feature_name + ":disabled");
+        features->insert(entry.feature_name + ":disabled");
     }
   }
-  return features;
 }
 
 bool FlagsState::IsRestartNeededToCommitChanges() {
@@ -699,7 +683,7 @@
     const std::string& key,
     const std::string& switch_name,
     const std::string& switch_value,
-    std::map<std::string, SwitchEntry>* name_to_switch_map) {
+    std::map<std::string, SwitchEntry>* name_to_switch_map) const {
   DCHECK(!base::ContainsKey(*name_to_switch_map, key));
 
   SwitchEntry* entry = &(*name_to_switch_map)[key];
@@ -711,7 +695,7 @@
     const std::string& key,
     const std::string& feature_name,
     bool feature_state,
-    std::map<std::string, SwitchEntry>* name_to_switch_map) {
+    std::map<std::string, SwitchEntry>* name_to_switch_map) const {
   DCHECK(!base::ContainsKey(*name_to_switch_map, key));
 
   SwitchEntry* entry = &(*name_to_switch_map)[key];
@@ -798,7 +782,7 @@
     command_line->AppendSwitchASCII(switch_name, switch_value);
 }
 
-void FlagsState::SanitizeList(FlagsStorage* flags_storage) {
+void FlagsState::SanitizeList(FlagsStorage* flags_storage) const {
   std::set<std::string> known_entries;
   for (size_t i = 0; i < num_feature_entries_; ++i) {
     DCHECK(ValidateFeatureEntry(feature_entries_[i]));
@@ -816,14 +800,14 @@
 }
 
 void FlagsState::GetSanitizedEnabledFlags(FlagsStorage* flags_storage,
-                                          std::set<std::string>* result) {
+                                          std::set<std::string>* result) const {
   SanitizeList(flags_storage);
   *result = flags_storage->GetFlags();
 }
 
 void FlagsState::GetSanitizedEnabledFlagsForCurrentPlatform(
     FlagsStorage* flags_storage,
-    std::set<std::string>* result) {
+    std::set<std::string>* result) const {
   GetSanitizedEnabledFlags(flags_storage, result);
 
   // Filter out any entries that aren't enabled on the current platform.  We
@@ -851,7 +835,7 @@
 void FlagsState::GenerateFlagsToSwitchesMapping(
     FlagsStorage* flags_storage,
     std::set<std::string>* enabled_entries,
-    std::map<std::string, SwitchEntry>* name_to_switch_map) {
+    std::map<std::string, SwitchEntry>* name_to_switch_map) const {
   GetSanitizedEnabledFlagsForCurrentPlatform(flags_storage, enabled_entries);
 
   for (size_t i = 0; i < num_feature_entries_; ++i) {
diff --git a/components/flags_ui/flags_state.h b/components/flags_ui/flags_state.h
index 0ec9b06..21ad59df5 100644
--- a/components/flags_ui/flags_state.h
+++ b/components/flags_ui/flags_state.h
@@ -70,15 +70,13 @@
                               const char* enable_features_flag_name,
                               const char* disable_features_flag_name);
 
-  // Reads the state from |flags_storage| and returns a set of strings
-  // corresponding to enabled entries. Does not populate any information about
-  // entries that enable/disable base::Feature states.
-  std::set<std::string> GetSwitchesFromFlags(FlagsStorage* flags_storage);
-
-  // Reads the state from |flags_storage| and returns a set of strings
-  // corresponding to enabled/disabled base::Feature states. Feature names are
-  // suffixed with ":enabled" or ":disabled" depending on their state.
-  std::set<std::string> GetFeaturesFromFlags(FlagsStorage* flags_storage);
+  // Reads the state from |flags_storage| and fills |switches| with the set of
+  // switches corresponding to enabled entries and |features| with the set of
+  // strings corresponding to enabled/disabled base::Feature states. Feature
+  // names are suffixed with ":enabled" or ":disabled" depending on their state.
+  void GetSwitchesAndFeaturesFromFlags(FlagsStorage* flags_storage,
+                                       std::set<std::string>* switches,
+                                       std::set<std::string>* features) const;
 
   bool IsRestartNeededToCommitChanges();
   void SetFeatureEntryEnabled(FlagsStorage* flags_storage,
@@ -142,10 +140,11 @@
   struct SwitchEntry;
 
   // Adds mapping to |name_to_switch_map| to set the given switch name/value.
-  void AddSwitchMapping(const std::string& key,
-                        const std::string& switch_name,
-                        const std::string& switch_value,
-                        std::map<std::string, SwitchEntry>* name_to_switch_map);
+  void AddSwitchMapping(
+      const std::string& key,
+      const std::string& switch_name,
+      const std::string& switch_value,
+      std::map<std::string, SwitchEntry>* name_to_switch_map) const;
 
   // Adds mapping to |name_to_switch_map| to toggle base::Feature |feature_name|
   // to state |feature_state|.
@@ -153,7 +152,7 @@
       const std::string& key,
       const std::string& feature_name,
       bool feature_state,
-      std::map<std::string, SwitchEntry>* name_to_switch_map);
+      std::map<std::string, SwitchEntry>* name_to_switch_map) const;
 
   // Updates the switches in |command_line| by applying the modifications
   // specified in |name_to_switch_map| for each entry in |enabled_entries|.
@@ -179,16 +178,16 @@
 
   // Removes all entries from prefs::kEnabledLabsExperiments that are unknown,
   // to prevent this list to become very long as entries are added and removed.
-  void SanitizeList(FlagsStorage* flags_storage);
+  void SanitizeList(FlagsStorage* flags_storage) const;
 
   void GetSanitizedEnabledFlags(FlagsStorage* flags_storage,
-                                std::set<std::string>* result);
+                                std::set<std::string>* result) const;
 
   // Variant of GetSanitizedEnabledFlags that also removes any flags that aren't
   // enabled on the current platform.
   void GetSanitizedEnabledFlagsForCurrentPlatform(
       FlagsStorage* flags_storage,
-      std::set<std::string>* result);
+      std::set<std::string>* result) const;
 
   // Generates a flags to switches mapping based on the set of enabled flags
   // from |flags_storage|. On output, |enabled_entries| will contain the
@@ -197,7 +196,7 @@
   void GenerateFlagsToSwitchesMapping(
       FlagsStorage* flags_storage,
       std::set<std::string>* enabled_entries,
-      std::map<std::string, SwitchEntry>* name_to_switch_map);
+      std::map<std::string, SwitchEntry>* name_to_switch_map) const;
 
   // Called when the value of an entry with ORIGIN_LIST_VALUE is modified.
   // Modifies the corresponding command line by adding or removing the switch
diff --git a/components/heap_profiling/test_driver.cc b/components/heap_profiling/test_driver.cc
index a070690..e17fe78f 100644
--- a/components/heap_profiling/test_driver.cc
+++ b/components/heap_profiling/test_driver.cc
@@ -33,7 +33,7 @@
 
 namespace {
 
-const char kTestCategory[] = "kTestCategory";
+constexpr const char kTestCategory[] = "kTestCategory";
 const char kMallocEvent[] = "kMallocEvent";
 const char kMallocTypeTag[] = "kMallocTypeTag";
 const char kPAEvent[] = "kPAEvent";
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn
index 4da54a9..f86f34f 100644
--- a/components/offline_pages/core/BUILD.gn
+++ b/components/offline_pages/core/BUILD.gn
@@ -12,6 +12,8 @@
     "archive_manager.h",
     "archive_validator.cc",
     "archive_validator.h",
+    "auto_fetch.cc",
+    "auto_fetch.h",
     "background_snapshot_controller.cc",
     "background_snapshot_controller.h",
     "client_id.cc",
@@ -152,6 +154,7 @@
   sources = [
     "archive_manager_unittest.cc",
     "archive_validator_unittest.cc",
+    "auto_fetch_unittest.cc",
     "background_snapshot_controller_unittest.cc",
     "client_policy_controller_unittest.cc",
     "model/add_page_task_unittest.cc",
diff --git a/components/offline_pages/core/auto_fetch.cc b/components/offline_pages/core/auto_fetch.cc
new file mode 100644
index 0000000..95b55698
--- /dev/null
+++ b/components/offline_pages/core/auto_fetch.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/auto_fetch.h"
+
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+
+namespace offline_pages {
+namespace auto_fetch {
+ClientIdMetadata::ClientIdMetadata() = default;
+ClientIdMetadata::ClientIdMetadata(const ClientIdMetadata&) = default;
+
+ClientId MakeClientId(const ClientIdMetadata& metadata) {
+  // Here, the 'A' prefix is used so that future versions can easily change the
+  // format if necessary.
+  return ClientId(kAutoAsyncNamespace,
+                  base::StrCat({"A", std::to_string(metadata.android_tab_id)}));
+}
+
+base::Optional<ClientIdMetadata> ExtractMetadata(const ClientId& id) {
+  if (id.name_space != kAutoAsyncNamespace)
+    return base::nullopt;
+  if (id.id.empty() || id.id[0] != 'A')
+    return base::nullopt;
+  ClientIdMetadata metadata;
+  if (!base::StringToInt(base::StringPiece(id.id).substr(1),
+                         &metadata.android_tab_id))
+    return base::nullopt;
+  return metadata;
+}
+
+}  // namespace auto_fetch
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/auto_fetch.h b/components/offline_pages/core/auto_fetch.h
new file mode 100644
index 0000000..25ecee5
--- /dev/null
+++ b/components/offline_pages/core/auto_fetch.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_AUTO_FETCH_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_AUTO_FETCH_H_
+
+#include "base/optional.h"
+#include "components/offline_pages/core/client_id.h"
+
+// Most auto-fetch code is in browser/offline_pages. This file contains code
+// that needs to be accessed within components/offline_pages.
+
+namespace offline_pages {
+namespace auto_fetch {
+
+// This metadata is stored in the |ClientId|'s |id| field.
+struct ClientIdMetadata {
+  ClientIdMetadata();
+  ClientIdMetadata(const ClientIdMetadata&);
+  explicit ClientIdMetadata(int android_tab_id)
+      : android_tab_id(android_tab_id) {}
+  // ID of the Android tab that initiated the request.
+  int android_tab_id;
+};
+
+ClientId MakeClientId(const ClientIdMetadata& metadata);
+// Extract metadata from a |ClientId| that was created with |MakeClientId|.
+base::Optional<ClientIdMetadata> ExtractMetadata(const ClientId& id);
+
+}  // namespace auto_fetch
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_AUTO_FETCH_H_
diff --git a/components/offline_pages/core/auto_fetch_unittest.cc b/components/offline_pages/core/auto_fetch_unittest.cc
new file mode 100644
index 0000000..e677426
--- /dev/null
+++ b/components/offline_pages/core/auto_fetch_unittest.cc
@@ -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.
+
+#include "components/offline_pages/core/auto_fetch.h"
+
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace auto_fetch {
+namespace {
+
+TEST(AutoFetch, MakeClientId) {
+  EXPECT_EQ(ClientId(kAutoAsyncNamespace, "A123"),
+            auto_fetch::MakeClientId(auto_fetch::ClientIdMetadata(123)));
+}
+
+TEST(AutoFetch, ExtractMetadataSuccess) {
+  base::Optional<auto_fetch::ClientIdMetadata> metadata =
+      auto_fetch::ExtractMetadata(
+          auto_fetch::MakeClientId(auto_fetch::ClientIdMetadata(123)));
+  ASSERT_TRUE(metadata);
+  EXPECT_EQ(123, metadata.value().android_tab_id);
+}
+
+TEST(AutoFetch, ExtractMetadataFail) {
+  EXPECT_FALSE(
+      auto_fetch::ExtractMetadata(ClientId(kAutoAsyncNamespace, "123")));
+  EXPECT_FALSE(auto_fetch::ExtractMetadata(ClientId(kAutoAsyncNamespace, "")));
+  EXPECT_FALSE(auto_fetch::ExtractMetadata(ClientId("other", "A123")));
+}
+
+}  // namespace
+}  // namespace auto_fetch
+}  // namespace offline_pages
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 38935d5..8fc08ce 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -164,6 +164,8 @@
     "suppressed_form_fetcher.h",
     "sync/password_data_type_controller.cc",
     "sync/password_data_type_controller.h",
+    "sync/password_model_type_controller.cc",
+    "sync/password_model_type_controller.h",
     "sync/password_model_worker.cc",
     "sync/password_model_worker.h",
     "sync/password_sync_bridge.cc",
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 75c9f83..f4b376b 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -31,6 +31,7 @@
 #include "components/password_manager/core/browser/sync/password_syncable_service.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/model_impl/proxy_model_type_controller_delegate.h"
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 #include "components/password_manager/core/browser/password_store_signin_notifier.h"
@@ -377,6 +378,20 @@
   return syncable_service_->AsWeakPtr();
 }
 
+std::unique_ptr<syncer::ProxyModelTypeControllerDelegate>
+PasswordStore::CreateSyncControllerDelegate() {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  // Note that a callback is bound for
+  // GetSyncControllerDelegateOnBackgroundSequence() because this getter itself
+  // must also run in the backend sequence, and the proxy object below will take
+  // care of that.
+  return std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
+      background_task_runner_,
+      base::BindRepeating(
+          &PasswordStore::GetSyncControllerDelegateOnBackgroundSequence,
+          base::Unretained(this)));
+}
+
 // TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 void PasswordStore::CheckReuse(const base::string16& input,
@@ -977,6 +992,13 @@
                           updated_android_form, affiliated_web_realms));
 }
 
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+PasswordStore::GetSyncControllerDelegateOnBackgroundSequence() {
+  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(sync_bridge_);
+  return sync_bridge_->change_processor()->GetControllerDelegate();
+}
+
 void PasswordStore::DestroyOnBackgroundSequence() {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
   syncable_service_.reset();
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 0a12ec9af..56b1b15 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -38,6 +38,8 @@
 }
 
 namespace syncer {
+class ModelTypeControllerDelegate;
+class ProxyModelTypeControllerDelegate;
 class SyncableService;
 }
 
@@ -266,6 +268,11 @@
 
   base::WeakPtr<syncer::SyncableService> GetPasswordSyncableService();
 
+  // For sync codebase only: instantiates a proxy controller delegate to
+  // interact with PasswordSyncBridge. Must be called from the UI thread.
+  std::unique_ptr<syncer::ProxyModelTypeControllerDelegate>
+  CreateSyncControllerDelegate();
+
 // TODO(crbug.com/706392): Fix password reuse detection for Android.
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Immediately called after |Init()| to retrieve password hash data for
@@ -676,6 +683,11 @@
       const autofill::PasswordForm& updated_android_form,
       const std::vector<std::string>& affiliated_web_realms);
 
+  // Returns the sync controller delegate for syncing passwords. It must be
+  // called on the background sequence.
+  base::WeakPtr<syncer::ModelTypeControllerDelegate>
+  GetSyncControllerDelegateOnBackgroundSequence();
+
   // Schedules UpdateAffiliatedWebLoginsImpl() to run on the background
   // sequence. Should be called from the main sequence.
   void ScheduleUpdateAffiliatedWebLoginsImpl(
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.cc b/components/password_manager/core/browser/sync/password_model_type_controller.cc
new file mode 100644
index 0000000..7cbbd42
--- /dev/null
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.cc
@@ -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.
+
+#include "components/password_manager/core/browser/sync/password_model_type_controller.h"
+
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_client.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/model/model_type_controller_delegate.h"
+
+namespace password_manager {
+
+PasswordModelTypeController::PasswordModelTypeController(
+    std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk,
+    syncer::SyncClient* sync_client)
+    : ModelTypeController(syncer::PASSWORDS, std::move(delegate_on_disk)),
+      sync_client_(sync_client) {}
+
+PasswordModelTypeController::~PasswordModelTypeController() = default;
+
+void PasswordModelTypeController::LoadModels(
+    const syncer::ConfigureContext& configure_context,
+    const ModelLoadCallback& model_load_callback) {
+  DCHECK(CalledOnValidThread());
+  sync_client_->GetSyncService()->AddObserver(this);
+  ModelTypeController::LoadModels(configure_context, model_load_callback);
+  sync_client_->GetPasswordStateChangedCallback().Run();
+}
+
+void PasswordModelTypeController::Stop(syncer::ShutdownReason shutdown_reason,
+                                       StopCallback callback) {
+  DCHECK(CalledOnValidThread());
+  sync_client_->GetSyncService()->RemoveObserver(this);
+  ModelTypeController::Stop(shutdown_reason, std::move(callback));
+  sync_client_->GetPasswordStateChangedCallback().Run();
+}
+
+void PasswordModelTypeController::OnStateChanged(syncer::SyncService* sync) {
+  DCHECK(CalledOnValidThread());
+  sync_client_->GetPasswordStateChangedCallback().Run();
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.h b/components/password_manager/core/browser/sync/password_model_type_controller.h
new file mode 100644
index 0000000..41306c2
--- /dev/null
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.h
@@ -0,0 +1,45 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_SYNC_PASSWORD_MODEL_TYPE_CONTROLLER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SYNC_PASSWORD_MODEL_TYPE_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "components/sync/driver/model_type_controller.h"
+#include "components/sync/driver/sync_service_observer.h"
+
+namespace syncer {
+class ModelTypeControllerDelegate;
+class SyncClient;
+}  // namespace syncer
+
+namespace password_manager {
+
+// A class that manages the startup and shutdown of password sync.
+class PasswordModelTypeController : public syncer::ModelTypeController,
+                                    public syncer::SyncServiceObserver {
+ public:
+  PasswordModelTypeController(
+      std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk,
+      syncer::SyncClient* sync_client);
+  ~PasswordModelTypeController() override;
+
+  // DataTypeController overrides.
+  void LoadModels(const syncer::ConfigureContext& configure_context,
+                  const ModelLoadCallback& model_load_callback) override;
+  void Stop(syncer::ShutdownReason shutdown_reason,
+            StopCallback callback) override;
+
+  // SyncServiceObserver overrides.
+  void OnStateChanged(syncer::SyncService* sync) override;
+
+ private:
+  syncer::SyncClient* const sync_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(PasswordModelTypeController);
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SYNC_PASSWORD_MODEL_TYPE_CONTROLLER_H_
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 71ed290..0ac8683 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -60,6 +60,8 @@
 
 jumbo_static_library("utils") {
   sources = [
+    "developer_console_logger.cc",
+    "developer_console_logger.h",
     "installable_payment_app_crawler.cc",
     "installable_payment_app_crawler.h",
     "manifest_verifier.cc",
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
index ceda5acb..3049846 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
@@ -7,6 +7,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.content_public.browser.WebContents;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -46,11 +47,13 @@
     /**
      * Init the native side of this class.
      * Must be called before parsePaymentMethodManifest or parseWebAppManifest can be called.
+     * @param webContents The web contents in whose developer console parsing errors and warnings
+     *                    will be printed.
      */
-    public void createNative() {
+    public void createNative(WebContents webContents) {
         ThreadUtils.assertOnUiThread();
         assert mNativePaymentManifestParserAndroid == 0;
-        mNativePaymentManifestParserAndroid = nativeCreatePaymentManifestParserAndroid();
+        mNativePaymentManifestParserAndroid = nativeCreatePaymentManifestParserAndroid(webContents);
     }
 
     /** Releases the resources held by the native side. */
@@ -123,7 +126,7 @@
         manifest[sectionIndex].fingerprints[fingerprintIndex] = fingerprint;
     }
 
-    private static native long nativeCreatePaymentManifestParserAndroid();
+    private static native long nativeCreatePaymentManifestParserAndroid(WebContents webContents);
     private static native void nativeDestroyPaymentManifestParserAndroid(
             long nativePaymentManifestParserAndroid);
     private static native void nativeParsePaymentMethodManifest(
diff --git a/components/payments/content/android/payment_manifest_downloader_android.cc b/components/payments/content/android/payment_manifest_downloader_android.cc
index ac19e0e2..4574ddaa 100644
--- a/components/payments/content/android/payment_manifest_downloader_android.cc
+++ b/components/payments/content/android/payment_manifest_downloader_android.cc
@@ -8,6 +8,7 @@
 
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "components/payments/content/developer_console_logger.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
@@ -59,8 +60,9 @@
 }  // namespace
 
 PaymentManifestDownloaderAndroid::PaymentManifestDownloaderAndroid(
+    std::unique_ptr<ErrorLogger> log,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : downloader_(std::move(url_loader_factory)) {}
+    : downloader_(std::move(log), std::move(url_loader_factory)) {}
 
 PaymentManifestDownloaderAndroid::~PaymentManifestDownloaderAndroid() {}
 
@@ -106,6 +108,7 @@
     return 0;
 
   return reinterpret_cast<jlong>(new PaymentManifestDownloaderAndroid(
+      std::make_unique<DeveloperConsoleLogger>(web_contents),
       content::BrowserContext::GetDefaultStoragePartition(
           web_contents->GetBrowserContext())
           ->GetURLLoaderFactoryForBrowserProcess()));
diff --git a/components/payments/content/android/payment_manifest_downloader_android.h b/components/payments/content/android/payment_manifest_downloader_android.h
index b57222f..05a4d76 100644
--- a/components/payments/content/android/payment_manifest_downloader_android.h
+++ b/components/payments/content/android/payment_manifest_downloader_android.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_DOWNLOADER_ANDROID_H_
 
 #include <jni.h>
+#include <memory>
 
 #include "base/android/jni_android.h"
 #include "base/macros.h"
@@ -18,10 +19,13 @@
 
 namespace payments {
 
+class ErrorLogger;
+
 // Android wrapper for the payment manifest downloader.
 class PaymentManifestDownloaderAndroid {
  public:
-  explicit PaymentManifestDownloaderAndroid(
+  PaymentManifestDownloaderAndroid(
+      std::unique_ptr<ErrorLogger> log,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~PaymentManifestDownloaderAndroid();
 
diff --git a/components/payments/content/android/payment_manifest_parser_android.cc b/components/payments/content/android/payment_manifest_parser_android.cc
index 575b2b91..8fd2438 100644
--- a/components/payments/content/android/payment_manifest_parser_android.cc
+++ b/components/payments/content/android/payment_manifest_parser_android.cc
@@ -5,6 +5,7 @@
 #include "components/payments/content/android/payment_manifest_parser_android.h"
 
 #include <stddef.h>
+#include <utility>
 #include <vector>
 
 #include "base/android/jni_array.h"
@@ -14,6 +15,9 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
+#include "components/payments/content/developer_console_logger.h"
+#include "components/payments/core/error_logger.h"
+#include "content/public/browser/web_contents.h"
 #include "jni/PaymentManifestParser_jni.h"
 #include "url/gurl.h"
 
@@ -119,7 +123,9 @@
 
 }  // namespace
 
-PaymentManifestParserAndroid::PaymentManifestParserAndroid() {}
+PaymentManifestParserAndroid::PaymentManifestParserAndroid(
+    std::unique_ptr<ErrorLogger> log)
+    : parser_(std::move(log)) {}
 
 PaymentManifestParserAndroid::~PaymentManifestParserAndroid() {}
 
@@ -154,8 +160,15 @@
 // Caller owns the result.
 jlong JNI_PaymentManifestParser_CreatePaymentManifestParserAndroid(
     JNIEnv* env,
-    const base::android::JavaParamRef<jclass>& jcaller) {
-  return reinterpret_cast<jlong>(new PaymentManifestParserAndroid);
+    const base::android::JavaParamRef<jclass>& jcaller,
+    const base::android::JavaParamRef<jobject>& jweb_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
+  auto log = web_contents
+                 ? std::make_unique<DeveloperConsoleLogger>(web_contents)
+                 : std::make_unique<ErrorLogger>();
+  return reinterpret_cast<jlong>(
+      new PaymentManifestParserAndroid(std::move(log)));
 }
 
 }  // namespace payments
diff --git a/components/payments/content/android/payment_manifest_parser_android.h b/components/payments/content/android/payment_manifest_parser_android.h
index 19f663d..e763e549 100644
--- a/components/payments/content/android/payment_manifest_parser_android.h
+++ b/components/payments/content/android/payment_manifest_parser_android.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_PARSER_ANDROID_H_
 
 #include <jni.h>
+#include <memory>
 
 #include "base/android/jni_android.h"
 #include "base/macros.h"
@@ -13,11 +14,13 @@
 
 namespace payments {
 
+class ErrorLogger;
+
 // Android wrapper for the host of the utility process that parses manifest
 // contents.
 class PaymentManifestParserAndroid {
  public:
-  PaymentManifestParserAndroid();
+  explicit PaymentManifestParserAndroid(std::unique_ptr<ErrorLogger> log);
   ~PaymentManifestParserAndroid();
 
   void ParsePaymentMethodManifest(
diff --git a/components/payments/content/developer_console_logger.cc b/components/payments/content/developer_console_logger.cc
new file mode 100644
index 0000000..a4b4634
--- /dev/null
+++ b/components/payments/content/developer_console_logger.cc
@@ -0,0 +1,39 @@
+// 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/payments/content/developer_console_logger.h"
+
+#include "content/public/browser/web_contents.h"
+
+namespace payments {
+
+DeveloperConsoleLogger::DeveloperConsoleLogger(
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
+
+DeveloperConsoleLogger::~DeveloperConsoleLogger() {}
+
+void DeveloperConsoleLogger::Warn(const std::string& warning_message) const {
+  if (!enabled_)
+    return;
+  if (web_contents() && web_contents()->GetMainFrame()) {
+    web_contents()->GetMainFrame()->AddMessageToConsole(
+        content::CONSOLE_MESSAGE_LEVEL_WARNING, warning_message);
+  } else {
+    ErrorLogger::Warn(warning_message);
+  }
+}
+
+void DeveloperConsoleLogger::Error(const std::string& error_message) const {
+  if (!enabled_)
+    return;
+  if (web_contents() && web_contents()->GetMainFrame()) {
+    web_contents()->GetMainFrame()->AddMessageToConsole(
+        content::CONSOLE_MESSAGE_LEVEL_ERROR, error_message);
+  } else {
+    ErrorLogger::Error(error_message);
+  }
+}
+
+}  // namespace payments
diff --git a/components/payments/content/developer_console_logger.h b/components/payments/content/developer_console_logger.h
new file mode 100644
index 0000000..cc8e35b
--- /dev/null
+++ b/components/payments/content/developer_console_logger.h
@@ -0,0 +1,35 @@
+// 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_PAYMENTS_CONTENT_DEVELOPER_CONSOLE_LOGGER_H_
+#define COMPONENTS_PAYMENTS_CONTENT_DEVELOPER_CONSOLE_LOGGER_H_
+
+#include "base/macros.h"
+#include "components/payments/core/error_logger.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace payments {
+
+// Logs messages for web developers to the developer console.
+class DeveloperConsoleLogger : public ErrorLogger,
+                               public content::WebContentsObserver {
+ public:
+  explicit DeveloperConsoleLogger(content::WebContents* web_contents);
+  ~DeveloperConsoleLogger() override;
+
+  // ErrorLogger;
+  void Warn(const std::string& warning_message) const override;
+  void Error(const std::string& error_message) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeveloperConsoleLogger);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_DEVELOPER_CONSOLE_LOGGER_H_
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index 8f3deab..0785101 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -34,6 +34,7 @@
     PaymentManifestParser* parser,
     PaymentManifestWebDataService* cache)
     : WebContentsObserver(web_contents),
+      log_(web_contents),
       downloader_(downloader),
       parser_(parser),
       number_of_payment_method_manifest_to_download_(0),
@@ -123,9 +124,9 @@
     if (!net::registry_controlled_domains::SameDomainOrHost(
             method_manifest_url, url,
             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      WarnIfPossible("Installable payment app from " + url.spec() +
-                     " is not allowed for the method " +
-                     method_manifest_url.spec());
+      log_.Warn("Installable payment handler from \"" + url.spec() +
+                "\" is not allowed for the method \"" +
+                method_manifest_url.spec() + "\" because of different domain.");
       continue;
     }
 
@@ -191,9 +192,13 @@
   if (app_info == nullptr)
     return false;
 
+  std::string log_prefix = "Web app manifest \"" + web_app_manifest_url.spec() +
+                           "\" for payment method manifest \"" +
+                           method_manifest_url.spec() + "\": ";
   if (app_info->sw_js_url.empty() || !base::IsStringUTF8(app_info->sw_js_url)) {
-    WarnIfPossible(
-        "The installable payment app's js url is not a non-empty UTF8 string.");
+    log_.Error(log_prefix +
+               "The installable payment handler's service worker JavaScript "
+               "file URL is not a non-empty UTF8 string.");
     return false;
   }
 
@@ -201,17 +206,21 @@
   if (!GURL(app_info->sw_js_url).is_valid()) {
     GURL absolute_url = web_app_manifest_url.Resolve(app_info->sw_js_url);
     if (!absolute_url.is_valid()) {
-      WarnIfPossible(
-          "Failed to resolve the installable payment app's js url (" +
-          app_info->sw_js_url + ").");
+      log_.Error(log_prefix +
+                 "Failed to resolve the installable payment handler's service "
+                 "worker JavaScript file URL \"" +
+                 app_info->sw_js_url + "\".");
       return false;
     }
     if (!net::registry_controlled_domains::SameDomainOrHost(
             method_manifest_url, absolute_url,
             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      WarnIfPossible("Installable payment app's js url " + absolute_url.spec() +
-                     " is not allowed for the method " +
-                     method_manifest_url.spec());
+      log_.Error(log_prefix +
+                 "Installable payment handler's service worker JavaScript file "
+                 "URL \"" +
+                 absolute_url.spec() + "\" is not allowed for the method \"" +
+                 method_manifest_url.spec() +
+                 "\" because of different domain.");
       return false;
     }
     app_info->sw_js_url = absolute_url.spec();
@@ -221,18 +230,19 @@
     GURL absolute_scope =
         web_app_manifest_url.GetWithoutFilename().Resolve(app_info->sw_scope);
     if (!absolute_scope.is_valid()) {
-      WarnIfPossible(
-          "Failed to resolve the installable payment app's registration "
-          "scope (" +
-          app_info->sw_scope + ").");
+      log_.Error(log_prefix +
+                 "Failed to resolve the installable payment handler's "
+                 "registration scope \"" +
+                 app_info->sw_scope + "\".");
       return false;
     }
     if (!net::registry_controlled_domains::SameDomainOrHost(
             method_manifest_url, absolute_scope,
             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      WarnIfPossible("Installable payment app's registration scope " +
-                     absolute_scope.spec() + " is not allowed for the method " +
-                     method_manifest_url.spec());
+      log_.Error(
+          log_prefix + "Installable payment handler's registration scope \"" +
+          absolute_scope.spec() + "\" is not allowed for the method \"" +
+          method_manifest_url.spec() + "\" because of different domain.");
       return false;
     }
     app_info->sw_scope = absolute_scope.spec();
@@ -242,14 +252,18 @@
   if (!content::PaymentAppProvider::GetInstance()->IsValidInstallablePaymentApp(
           web_app_manifest_url, GURL(app_info->sw_js_url),
           GURL(app_info->sw_scope), &error_message)) {
-    WarnIfPossible(error_message);
+    log_.Error(log_prefix + error_message);
     return false;
   }
 
   // TODO(crbug.com/782270): Support multiple installable payment apps for a
   // payment method.
-  if (installable_apps_.find(method_manifest_url) != installable_apps_.end())
+  if (installable_apps_.find(method_manifest_url) != installable_apps_.end()) {
+    log_.Error(log_prefix +
+               "Multiple installable payment handlers from for a single "
+               "payment method: not yet supported.");
     return false;
+  }
 
   installable_apps_[method_manifest_url] = std::move(app_info);
 
@@ -260,15 +274,23 @@
     const GURL& method_manifest_url,
     const GURL& web_app_manifest_url,
     std::unique_ptr<std::vector<PaymentManifestParser::WebAppIcon>> icons) {
-  if (icons == nullptr || icons->empty())
+  if (icons == nullptr || icons->empty()) {
+    log_.Error(
+        "No valid icon information for installable payment handler found in "
+        "web app manifest \"" +
+        web_app_manifest_url.spec() + "\" for payment handler manifest \"" +
+        method_manifest_url.spec() + "\".");
     return;
+  }
 
   std::vector<blink::Manifest::ImageResource> manifest_icons;
   for (const auto& icon : *icons) {
     if (icon.src.empty() || !base::IsStringUTF8(icon.src)) {
-      WarnIfPossible(
-          "The installable payment app's icon src url is not a non-empty UTF8 "
-          "string.");
+      log_.Warn(
+          "The installable payment handler's icon src URL is not a non-empty "
+          "UTF8 string in web app manifest \"" +
+          web_app_manifest_url.spec() + "\" for payment handler manifest \"" +
+          method_manifest_url.spec() + "\".");
       continue;
     }
 
@@ -276,9 +298,12 @@
     if (!icon_src.is_valid()) {
       icon_src = web_app_manifest_url.Resolve(icon.src);
       if (!icon_src.is_valid()) {
-        WarnIfPossible(
-            "Failed to resolve the installable payment app's icon src url (" +
-            icon.src + ").");
+        log_.Warn(
+            "Failed to resolve the installable payment handler's icon src url "
+            "\"" +
+            icon.src + "\" in web app manifest \"" +
+            web_app_manifest_url.spec() + "\" for payment handler manifest \"" +
+            method_manifest_url.spec() + "\".");
         continue;
       }
     }
@@ -293,8 +318,13 @@
     manifest_icons.emplace_back(manifest_icon);
   }
 
-  if (manifest_icons.empty())
+  if (manifest_icons.empty()) {
+    log_.Error("No valid icons found in web app manifest \"" +
+               web_app_manifest_url.spec() +
+               "\" for payment handler manifest \"" +
+               method_manifest_url.spec() + "\".");
     return;
+  }
 
   // TODO(crbug.com/782270): Choose appropriate icon size dynamically on
   // different platforms. Here we choose a large ideal icon size to be big
@@ -306,15 +336,22 @@
       manifest_icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
       blink::Manifest::ImageResource::Purpose::ANY);
   if (!best_icon_url.is_valid()) {
-    WarnIfPossible(
-        "No suitable icon found in the installabble payment app's manifest (" +
-        web_app_manifest_url.spec() + ").");
+    log_.Error("No suitable icon found in web app manifest \"" +
+               web_app_manifest_url.spec() +
+               "\" for payment handler manifest \"" +
+               method_manifest_url.spec() + "\".");
     return;
   }
 
   // Stop if the web_contents is gone.
-  if (web_contents() == nullptr)
+  if (web_contents() == nullptr) {
+    log_.Error(
+        "Cannot download icons after the webpage has been closed (web app "
+        "manifest \"" +
+        web_app_manifest_url.spec() + "\" for payment handler manifest \"" +
+        method_manifest_url.spec() + "\").");
     return;
+  }
 
   number_of_web_app_icons_to_download_and_decode_++;
   bool can_download_icon = content::ManifestIconDownloader::Download(
@@ -333,10 +370,10 @@
     const SkBitmap& icon) {
   number_of_web_app_icons_to_download_and_decode_--;
   if (icon.drawsNothing()) {
-    WarnIfPossible(
-        "Failed to download or decode installable payment app's icon for web "
-        "app manifest " +
-        web_app_manifest_url.spec() + ".");
+    log_.Error(
+        "Failed to download or decode the icon from web app manifest \"" +
+        web_app_manifest_url.spec() + "\" for payment handler manifest \"" +
+        method_manifest_url.spec() + "\".");
   } else {
     auto it = installable_apps_.find(method_manifest_url);
     DCHECK(it != installable_apps_.end());
@@ -362,13 +399,4 @@
   std::move(finished_using_resources_).Run();
 }
 
-void InstallablePaymentAppCrawler::WarnIfPossible(const std::string& message) {
-  if (web_contents()) {
-    web_contents()->GetMainFrame()->AddMessageToConsole(
-        content::ConsoleMessageLevel::CONSOLE_MESSAGE_LEVEL_WARNING, message);
-  } else {
-    LOG(WARNING) << message;
-  }
-}
-
 }  // namespace payments.
diff --git a/components/payments/content/installable_payment_app_crawler.h b/components/payments/content/installable_payment_app_crawler.h
index 73be81b..a7b50e8 100644
--- a/components/payments/content/installable_payment_app_crawler.h
+++ b/components/payments/content/installable_payment_app_crawler.h
@@ -13,6 +13,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "components/payments/content/developer_console_logger.h"
 #include "components/payments/content/manifest_verifier.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/payments/content/utility/payment_manifest_parser.h"
@@ -85,8 +86,7 @@
                                              const SkBitmap& icon);
   void FinishCrawlingPaymentAppsIfReady();
 
-  void WarnIfPossible(const std::string& message);
-
+  DeveloperConsoleLogger log_;
   PaymentManifestDownloader* downloader_;
   PaymentManifestParser* parser_;
   FinishedCrawlingCallback callback_;
diff --git a/components/payments/content/manifest_verifier.cc b/components/payments/content/manifest_verifier.cc
index ee040c9..ca1d6e2 100644
--- a/components/payments/content/manifest_verifier.cc
+++ b/components/payments/content/manifest_verifier.cc
@@ -61,7 +61,7 @@
                                    PaymentManifestDownloader* downloader,
                                    PaymentManifestParser* parser,
                                    PaymentManifestWebDataService* cache)
-    : dev_tools_(web_contents),
+    : log_(web_contents),
       downloader_(downloader),
       parser_(parser),
       cache_(cache),
@@ -98,16 +98,16 @@
       // https://w3c.github.io/webpayments-methods-tokenization/
       if (method == "basic-card" || method == "interledger" ||
           method == "payee-credit-transfer" ||
-          method == "payer-credit-transfer" ||
-          method == "tokenized-card") {
+          method == "payer-credit-transfer" || method == "tokenized-card") {
         verified_method_names.emplace_back(method);
         continue;
       }
 
       // GURL constructor may crash with some invalid unicode strings.
       if (!base::IsStringUTF8(method)) {
-        dev_tools_.WarnIfPossible("Payment method name \"" + method +
-                                  "\" is not valid unicode.");
+        log_.Warn("Payment method name \"" + method +
+                  "\" in payment handler \"" + app.second->scope.spec() +
+                  "\"  is not valid unicode.");
         continue;
       }
 
@@ -116,8 +116,10 @@
       if (!method_manifest_url.is_valid() ||
           (method_manifest_url.scheme() != "https" &&
            !net::IsLocalhost(method_manifest_url))) {
-        dev_tools_.WarnIfPossible("\"" + method +
-                                  "\" is not a valid payment method name.");
+        log_.Warn(
+            "\"" + method +
+            "\" is not a valid payment method name in payment handler \"" +
+            app.second->scope.spec() + "\".");
         continue;
       }
 
@@ -155,22 +157,6 @@
   }
 }
 
-ManifestVerifier::DevToolsHelper::DevToolsHelper(
-    content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {}
-
-ManifestVerifier::DevToolsHelper::~DevToolsHelper() {}
-
-void ManifestVerifier::DevToolsHelper::WarnIfPossible(
-    const std::string& message) {
-  if (web_contents()) {
-    web_contents()->GetMainFrame()->AddMessageToConsole(
-        content::CONSOLE_MESSAGE_LEVEL_WARNING, message);
-  } else {
-    LOG(WARNING) << message;
-  }
-}
-
 void ManifestVerifier::OnWebDataServiceRequestDone(
     WebDataServiceBase::Handle h,
     std::unique_ptr<WDTypedResult> result) {
@@ -303,16 +289,16 @@
     const std::set<GURL>& methods = it.second;
     for (const GURL& method : methods) {
       DCHECK(method.is_valid());
-      dev_tools_.WarnIfPossible(
-          "The payment handler \"" + app_scope +
-          "\" is not allowed to use payment method \"" + method.spec() +
-          "\", because the payment handler origin \"" + app_origin +
-          "\" is different from the payment method origin \"" +
-          method.GetOrigin().spec() +
-          "\" and the \"supported_origins\" field in the payment method "
-          "manifest for \"" +
-          method.spec() + "\" is not \"*\" and is not a list that includes \"" +
-          app_origin + "\".");
+      log_.Warn("The payment handler \"" + app_scope +
+                "\" is not allowed to use payment method \"" + method.spec() +
+                "\", because the payment handler origin \"" + app_origin +
+                "\" is different from the payment method origin \"" +
+                method.GetOrigin().spec() +
+                "\" and the \"supported_origins\" field in the payment method "
+                "manifest for \"" +
+                method.spec() +
+                "\" is not \"*\" and is not a list that includes \"" +
+                app_origin + "\".");
     }
   }
 
diff --git a/components/payments/content/manifest_verifier.h b/components/payments/content/manifest_verifier.h
index c3d5771..0e189540 100644
--- a/components/payments/content/manifest_verifier.h
+++ b/components/payments/content/manifest_verifier.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "components/payments/content/developer_console_logger.h"
 #include "components/webdata/common/web_data_service_base.h"
 #include "components/webdata/common/web_data_service_consumer.h"
 #include "content/public/browser/payment_app_provider.h"
@@ -72,13 +73,6 @@
               base::OnceClosure finished_using_resources);
 
  private:
-  class DevToolsHelper : public content::WebContentsObserver {
-   public:
-    explicit DevToolsHelper(content::WebContents* web_contents);
-    ~DevToolsHelper() override;
-    void WarnIfPossible(const std::string& message);
-  };
-
   // Called when a manifest is retrieved from cache.
   void OnWebDataServiceRequestDone(
       WebDataServiceBase::Handle h,
@@ -98,8 +92,7 @@
   // Called immediately preceding the verification callback invocation.
   void RemoveInvalidPaymentApps();
 
-  // Logs messages to the DevTools console.
-  DevToolsHelper dev_tools_;
+  DeveloperConsoleLogger log_;
 
   // Downloads the manifests.
   PaymentManifestDownloader* downloader_;
diff --git a/components/payments/content/service_worker_payment_app_factory.cc b/components/payments/content/service_worker_payment_app_factory.cc
index 95686053..f516cb9 100644
--- a/components/payments/content/service_worker_payment_app_factory.cc
+++ b/components/payments/content/service_worker_payment_app_factory.cc
@@ -13,6 +13,7 @@
 #include "base/memory/singleton.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/payments/content/developer_console_logger.h"
 #include "components/payments/content/installable_payment_app_crawler.h"
 #include "components/payments/content/manifest_verifier.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
@@ -108,7 +109,8 @@
     DCHECK(!verifier_);
 
     downloader_ = std::move(downloader);
-    parser_ = std::make_unique<PaymentManifestParser>();
+    parser_ = std::make_unique<PaymentManifestParser>(
+        std::make_unique<DeveloperConsoleLogger>(web_contents));
     cache_ = cache;
     verifier_ = std::make_unique<ManifestVerifier>(
         web_contents, downloader_.get(), parser_.get(), cache_.get());
@@ -257,19 +259,22 @@
     base::OnceClosure finished_writing_cache_callback_for_testing) {
   SelfDeletingServiceWorkerPaymentAppFactory* self_delete_factory =
       new SelfDeletingServiceWorkerPaymentAppFactory();
-  if (test_downloader_ != nullptr)
+
+  std::unique_ptr<PaymentManifestDownloader> downloader;
+  if (test_downloader_ != nullptr) {
+    downloader = std::move(test_downloader_);
     self_delete_factory->IgnorePortInAppScopeForTesting();
+  } else {
+    downloader = std::make_unique<payments::PaymentManifestDownloader>(
+        std::make_unique<DeveloperConsoleLogger>(web_contents),
+        content::BrowserContext::GetDefaultStoragePartition(
+            web_contents->GetBrowserContext())
+            ->GetURLLoaderFactoryForBrowserProcess());
+  }
 
   self_delete_factory->GetAllPaymentApps(
-      web_contents,
-      test_downloader_ == nullptr
-          ? std::make_unique<payments::PaymentManifestDownloader>(
-                content::BrowserContext::GetDefaultStoragePartition(
-                    web_contents->GetBrowserContext())
-                    ->GetURLLoaderFactoryForBrowserProcess())
-          : std::move(test_downloader_),
-      cache, requested_method_data, may_crawl_for_installable_payment_apps,
-      std::move(callback),
+      web_contents, std::move(downloader), cache, requested_method_data,
+      may_crawl_for_installable_payment_apps, std::move(callback),
       std::move(finished_writing_cache_callback_for_testing));
 }
 
diff --git a/components/payments/content/utility/BUILD.gn b/components/payments/content/utility/BUILD.gn
index 43b7b80..f51b14dce 100644
--- a/components/payments/content/utility/BUILD.gn
+++ b/components/payments/content/utility/BUILD.gn
@@ -14,6 +14,7 @@
   deps = [
     "//base",
     "//components/payments/content:content_common",
+    "//components/payments/core",
     "//content/public/common",
     "//net",
     "//services/data_decoder/public/cpp",
@@ -30,6 +31,7 @@
   deps = [
     ":utility",
     "//base",
+    "//components/payments/core",
     "//skia",
     "//testing/gtest",
     "//url",
@@ -44,6 +46,7 @@
     ":utility",
     "//base",
     "//base:i18n",
+    "//components/payments/core",
     "//skia",
     "//url",
   ]
@@ -59,6 +62,7 @@
     ":utility",
     "//base",
     "//components/payments/content:content_common",
+    "//components/payments/core",
     "//skia",
   ]
   dict = "payment_manifest_json.dict"
@@ -72,6 +76,7 @@
   deps = [
     ":utility",
     "//base",
+    "//components/payments/core",
   ]
   dict = "fingerprint_fuzzer.dict"
   seed_corpus = "fingerprint_fuzzer_corpus"
diff --git a/components/payments/content/utility/fingerprint_parser.cc b/components/payments/content/utility/fingerprint_parser.cc
index 12e0259..b16f7483 100644
--- a/components/payments/content/utility/fingerprint_parser.cc
+++ b/components/payments/content/utility/fingerprint_parser.cc
@@ -7,6 +7,8 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/payments/core/error_logger.h"
 
 namespace payments {
 namespace {
@@ -23,24 +25,27 @@
 
 }  // namespace
 
-std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input) {
+std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input,
+                                                  const ErrorLogger& log) {
   std::vector<uint8_t> output;
   if (!base::IsStringASCII(input)) {
-    LOG(ERROR) << "Fingerprint should be an ASCII string.";
+    log.Error("Fingerprint should be an ASCII string.");
     return output;
   }
 
   const size_t kLength = 32 * 3 - 1;
   if (input.size() != kLength) {
-    LOG(ERROR) << "Fingerprint \"" << input << "\" should contain exactly "
-               << kLength << " characters.";
+    log.Error(base::StringPrintf(
+        "Fingerprint \"%s\" should contain exactly %zu characters.",
+        input.c_str(), kLength));
     return output;
   }
 
   for (size_t i = 0; i < input.size(); i += 3) {
     if (i < input.size() - 2 && input[i + 2] != ':') {
-      LOG(ERROR) << "Bytes in fingerprint \"" << input
-                 << "\" should separated by \":\" characters.";
+      log.Error(base::StringPrintf(
+          "Bytes in fingerprint \"%s\" should separated by \":\" characters.",
+          input.c_str()));
       output.clear();
       return output;
     }
@@ -48,8 +53,10 @@
     char big_end = input[i];
     char little_end = input[i + 1];
     if (!IsUpperCaseHexDigit(big_end) || !IsUpperCaseHexDigit(little_end)) {
-      LOG(ERROR) << "Bytes in fingerprint \"" << input
-                 << "\" should be upper case hex digits 0-9 and A-F.";
+      log.Error(base::StringPrintf(
+          "Bytes in fingerprint \"%s\" should be upper case hex digits 0-9 and "
+          "A-F.",
+          input.c_str()));
       output.clear();
       return output;
     }
diff --git a/components/payments/content/utility/fingerprint_parser.h b/components/payments/content/utility/fingerprint_parser.h
index c4115ff..d35ee593 100644
--- a/components/payments/content/utility/fingerprint_parser.h
+++ b/components/payments/content/utility/fingerprint_parser.h
@@ -12,9 +12,12 @@
 
 namespace payments {
 
+class ErrorLogger;
+
 // Converts a string representation of a 32-byte array (e.g., "01:02:03:04")
 // into a list of the corresponding bytes. Returns an empty list on error.
-std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input);
+std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input,
+                                                  const ErrorLogger& log);
 
 }  // namespace payments
 
diff --git a/components/payments/content/utility/fingerprint_parser_fuzzer.cc b/components/payments/content/utility/fingerprint_parser_fuzzer.cc
index 51ee437..a98c5579 100644
--- a/components/payments/content/utility/fingerprint_parser_fuzzer.cc
+++ b/components/payments/content/utility/fingerprint_parser_fuzzer.cc
@@ -8,15 +8,12 @@
 
 #include "base/logging.h"
 #include "components/payments/content/utility/fingerprint_parser.h"
-
-struct Environment {
-  Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
-};
-
-Environment* env = new Environment();
+#include "components/payments/core/error_logger.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  payments::ErrorLogger log;
+  log.DisableInTest();
   payments::FingerprintStringToByteArray(
-      std::string(reinterpret_cast<const char*>(data), size));
+      std::string(reinterpret_cast<const char*>(data), size), log);
   return 0;
 }
diff --git a/components/payments/content/utility/fingerprint_parser_unittest.cc b/components/payments/content/utility/fingerprint_parser_unittest.cc
index d7e6409c..25842508 100644
--- a/components/payments/content/utility/fingerprint_parser_unittest.cc
+++ b/components/payments/content/utility/fingerprint_parser_unittest.cc
@@ -4,89 +4,114 @@
 
 #include "components/payments/content/utility/fingerprint_parser.h"
 
+#include "components/payments/core/error_logger.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace payments {
 namespace {
 
 TEST(FingerprintParserTest, CheckInputSize) {
+  ErrorLogger log;
+  log.DisableInTest();
   // To short.
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C")
+                                           "C0:C",
+                                           log)
                   .empty());
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:")
+                                           "C0:",
+                                           log)
                   .empty());
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0")
+                                           "C0",
+                                           log)
                   .empty());
 
   // To long.
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C11")
+                                           "C0:C11",
+                                           log)
                   .empty());
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C1:")
+                                           "C0:C1:",
+                                           log)
                   .empty());
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C1:C")
+                                           "C0:C1:C",
+                                           log)
                   .empty());
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C1:C2")
+                                           "C0:C1:C2",
+                                           log)
                   .empty());
 }
 
 TEST(FingerprintParserTest, CheckColonSeparator) {
+  ErrorLogger log;
+  log.DisableInTest();
   EXPECT_TRUE(FingerprintStringToByteArray("00-01-02-03-04-05-06-07-08-09-"
                                            "A0-A1-A2-A3-A4-A5-A6-A7-A8-A9-"
                                            "B0-B1-B2-B3-B4-B5-B6-B7-B8-B9-"
-                                           "C0-C1")
+                                           "C0-C1",
+                                           log)
                   .empty());
 }
 
 TEST(FingerprintParserTest, MustBeHex) {
+  ErrorLogger log;
+  log.DisableInTest();
   EXPECT_TRUE(FingerprintStringToByteArray("G0:G1:G2:G3:G4:G5:G6:G7:G8:G9:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C1")
+                                           "C0:C1",
+                                           log)
                   .empty());
 }
 
 TEST(FingerprintParserTest, MustBeUpperCaseHex) {
+  ErrorLogger log;
+  log.DisableInTest();
   EXPECT_TRUE(FingerprintStringToByteArray("00:01:02:03:04:05:06:07:08:09:"
                                            "a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:"
                                            "b0:b1:b2:b3:b4:b5:b6:b7:b8:b9:"
-                                           "c0:c1")
+                                           "c0:c1",
+                                           log)
                   .empty());
 }
 
 TEST(FingerprintParserTest, MustBeASCII) {
+  ErrorLogger log;
+  log.DisableInTest();
   EXPECT_TRUE(FingerprintStringToByteArray("β:01:02:03:04:05:06:07:08:09:"
                                            "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
                                            "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:"
-                                           "C0:C1")
+                                           "C0:C1",
+                                           log)
                   .empty());
 }
 
 TEST(FingerprintParserTest, CorrectParsing) {
+  ErrorLogger log;
+  log.DisableInTest();
   std::vector<uint8_t> actual_output = FingerprintStringToByteArray(
       "00:01:02:03:04:05:06:07:08:09:A0:"
       "A1:A2:A3:A4:A5:A6:A7:A8:A9:B0:B1:"
-      "B2:B3:B4:B5:B6:B7:B8:B9:FE:FF");
+      "B2:B3:B4:B5:B6:B7:B8:B9:FE:FF",
+      log);
   std::vector<uint8_t> expect_output = {
       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0,
       0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1,
diff --git a/components/payments/content/utility/payment_manifest_parser.cc b/components/payments/content/utility/payment_manifest_parser.cc
index 2b792a1..85e8cf7 100644
--- a/components/payments/content/utility/payment_manifest_parser.cc
+++ b/components/payments/content/utility/payment_manifest_parser.cc
@@ -13,7 +13,9 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "components/payments/content/utility/fingerprint_parser.h"
+#include "components/payments/core/error_logger.h"
 #include "content/public/common/service_manager_connection.h"
 #include "net/base/url_util.h"
 #include "services/data_decoder/public/cpp/safe_json_parser.h"
@@ -41,20 +43,22 @@
 // Parses the "default_applications": ["https://some/url"] from |dict| into
 // |web_app_manifest_urls|. Returns 'false' for invalid data.
 bool ParseDefaultApplications(base::DictionaryValue* dict,
-                              std::vector<GURL>* web_app_manifest_urls) {
+                              std::vector<GURL>* web_app_manifest_urls,
+                              const ErrorLogger& log) {
   DCHECK(dict);
   DCHECK(web_app_manifest_urls);
 
   base::ListValue* list = nullptr;
   if (!dict->GetList(kDefaultApplications, &list)) {
-    LOG(ERROR) << "\"" << kDefaultApplications << "\" must be a list.";
+    log.Error(
+        base::StringPrintf("\"%s\" must be a list.", kDefaultApplications));
     return false;
   }
 
   size_t apps_number = list->GetSize();
   if (apps_number > kMaximumNumberOfItems) {
-    LOG(ERROR) << "\"" << kDefaultApplications << "\" must contain at most "
-               << kMaximumNumberOfItems << " entries.";
+    log.Error(base::StringPrintf("\"%s\" must contain at most %zu entries.",
+                                 kDefaultApplications, kMaximumNumberOfItems));
     return false;
   }
 
@@ -64,9 +68,10 @@
         !base::IsStringUTF8(item) ||
         !(base::StartsWith(item, kHttpsPrefix, base::CompareCase::SENSITIVE) ||
           base::StartsWith(item, kHttpPrefix, base::CompareCase::SENSITIVE))) {
-      LOG(ERROR) << "Each entry in \"" << kDefaultApplications
-                 << "\" must be UTF8 string that starts with \"" << kHttpsPrefix
-                 << "\" or \"" << kHttpPrefix << "\" (for localhost).";
+      log.Error(base::StringPrintf(
+          "Each entry in \"%s\" must be UTF8 string that starts with \"%s\" or "
+          "\"%s\" (for localhost).",
+          kDefaultApplications, kHttpsPrefix, kHttpPrefix));
       web_app_manifest_urls->clear();
       return false;
     }
@@ -75,9 +80,10 @@
     if (!url.is_valid() ||
         !(url.SchemeIs(url::kHttpsScheme) ||
           (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)))) {
-      LOG(ERROR) << "\"" << item << "\" entry in \"" << kDefaultApplications
-                 << "\" is not a valid URL with HTTPS scheme and is not a "
-                    "valid localhost URL with HTTP scheme.";
+      log.Error(base::StringPrintf(
+          "\"%s\" entry in \"%s\" is not a valid URL with HTTPS scheme and is "
+          "not a valid localhost URL with HTTP scheme.",
+          item.c_str(), kDefaultApplications));
       web_app_manifest_urls->clear();
       return false;
     }
@@ -93,7 +99,8 @@
 // invalid data.
 bool ParseSupportedOrigins(base::DictionaryValue* dict,
                            std::vector<url::Origin>* supported_origins,
-                           bool* all_origins_supported) {
+                           bool* all_origins_supported,
+                           const ErrorLogger& log) {
   DCHECK(dict);
   DCHECK(supported_origins);
   DCHECK(all_origins_supported);
@@ -104,9 +111,10 @@
     std::string item;
     if (dict->GetString(kSupportedOrigins, &item)) {
       if (item != "*") {
-        LOG(ERROR) << "\"" << item << "\" is not a valid value for \""
-                   << kSupportedOrigins
-                   << "\". Must be either \"*\" or a list of RFC6454 origins.";
+        log.Error(
+            base::StringPrintf("Invalid value for \"%s\". Must be either \"*\" "
+                               "or a list of RFC6454 origins.",
+                               kSupportedOrigins));
         return false;
       }
 
@@ -117,16 +125,18 @@
 
   base::ListValue* list = nullptr;
   if (!dict->GetList(kSupportedOrigins, &list)) {
-    LOG(ERROR) << "\"" << kSupportedOrigins
-               << "\" must be either \"*\" or a list of origins.";
+    log.Error(
+        base::StringPrintf("\"%s\" must be either \"*\" or a list of origins.",
+                           kSupportedOrigins));
     return false;
   }
 
   size_t supported_origins_number = list->GetSize();
   const size_t kMaximumNumberOfSupportedOrigins = 100000;
   if (supported_origins_number > kMaximumNumberOfSupportedOrigins) {
-    LOG(ERROR) << "\"" << kSupportedOrigins << "\" must contain at most "
-               << kMaximumNumberOfSupportedOrigins << " entires.";
+    log.Error(base::StringPrintf("\"%s\" must contain at most %zu entires.",
+                                 kSupportedOrigins,
+                                 kMaximumNumberOfSupportedOrigins));
     return false;
   }
 
@@ -136,10 +146,11 @@
         !base::IsStringUTF8(item) ||
         !(base::StartsWith(item, kHttpsPrefix, base::CompareCase::SENSITIVE) ||
           base::StartsWith(item, kHttpPrefix, base::CompareCase::SENSITIVE))) {
-      LOG(ERROR) << "Each entry in \"" << kSupportedOrigins
-                 << "\" must be UTF8 string that starts with \"" << kHttpsPrefix
-                 << "\" or \"" << kHttpPrefix << "\" (for localhost).";
       supported_origins->clear();
+      log.Error(base::StringPrintf(
+          "Each entry in \"%s\" must be UTF8 string that starts with \"%s\" or "
+          "\"%s\" (for localhost).",
+          kSupportedOrigins, kHttpsPrefix, kHttpPrefix));
       return false;
     }
 
@@ -149,10 +160,11 @@
           (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url))) ||
         url.path() != "/" || url.has_query() || url.has_ref() ||
         url.has_username() || url.has_password()) {
-      LOG(ERROR) << "\"" << item << "\" entry in \"" << kSupportedOrigins
-                 << "\" is not a valid origin with HTTPS scheme and is not a "
-                    "valid localhost origin with HTTP scheme.";
       supported_origins->clear();
+      log.Error(base::StringPrintf(
+          "\"%s\" entry in \"%s\" is not a valid origin with HTTPS scheme and "
+          "is not a valid localhost origin with HTTP scheme.",
+          item.c_str(), kSupportedOrigins));
       return false;
     }
 
@@ -170,27 +182,31 @@
     : public base::RefCounted<JsonParserCallback<Callback>> {
  public:
   JsonParserCallback(
-      base::Callback<void(Callback, std::unique_ptr<base::Value>)>
-          parser_callback,
+      base::OnceCallback<void(Callback,
+                              std::unique_ptr<base::Value>,
+                              const std::string&)> parser_callback,
       Callback client_callback)
       : parser_callback_(std::move(parser_callback)),
         client_callback_(std::move(client_callback)) {}
 
   void OnSuccess(std::unique_ptr<base::Value> value) {
     std::move(parser_callback_)
-        .Run(std::move(client_callback_), std::move(value));
+        .Run(std::move(client_callback_), std::move(value),
+             /*error_message=*/std::string());
   }
 
   void OnError(const std::string& error_message) {
     std::move(parser_callback_)
-        .Run(std::move(client_callback_), /*value=*/nullptr);
+        .Run(std::move(client_callback_), /*value=*/nullptr, error_message);
   }
 
  private:
   friend class base::RefCounted<JsonParserCallback>;
   ~JsonParserCallback() = default;
 
-  base::Callback<void(Callback, std::unique_ptr<base::Value>)> parser_callback_;
+  base::OnceCallback<
+      void(Callback, std::unique_ptr<base::Value>, const std::string&)>
+      parser_callback_;
   Callback client_callback_;
 };
 
@@ -200,7 +216,10 @@
 
 PaymentManifestParser::WebAppIcon::~WebAppIcon() = default;
 
-PaymentManifestParser::PaymentManifestParser() : weak_factory_(this) {}
+PaymentManifestParser::PaymentManifestParser(std::unique_ptr<ErrorLogger> log)
+    : log_(std::move(log)), weak_factory_(this) {
+  DCHECK(log_);
+}
 
 PaymentManifestParser::~PaymentManifestParser() = default;
 
@@ -267,6 +286,7 @@
 // static
 void PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
     std::unique_ptr<base::Value> value,
+    const ErrorLogger& log,
     std::vector<GURL>* web_app_manifest_urls,
     std::vector<url::Origin>* supported_origins,
     bool* all_origins_supported) {
@@ -279,18 +299,18 @@
   std::unique_ptr<base::DictionaryValue> dict =
       base::DictionaryValue::From(std::move(value));
   if (!dict) {
-    LOG(ERROR) << "Payment method manifest must be a JSON dictionary.";
+    log.Error("Payment method manifest must be a JSON dictionary.");
     return;
   }
 
   if (dict->HasKey(kDefaultApplications) &&
-      !ParseDefaultApplications(dict.get(), web_app_manifest_urls)) {
+      !ParseDefaultApplications(dict.get(), web_app_manifest_urls, log)) {
     return;
   }
 
   if (dict->HasKey(kSupportedOrigins) &&
       !ParseSupportedOrigins(dict.get(), supported_origins,
-                             all_origins_supported)) {
+                             all_origins_supported, log)) {
     web_app_manifest_urls->clear();
   }
 }
@@ -298,17 +318,18 @@
 // static
 bool PaymentManifestParser::ParseWebAppManifestIntoVector(
     std::unique_ptr<base::Value> value,
+    const ErrorLogger& log,
     std::vector<WebAppManifestSection>* output) {
   std::unique_ptr<base::DictionaryValue> dict =
       base::DictionaryValue::From(std::move(value));
   if (!dict) {
-    LOG(ERROR) << "Web app manifest must be a JSON dictionary.";
+    log.Error("Web app manifest must be a JSON dictionary.");
     return false;
   }
 
   base::ListValue* list = nullptr;
   if (!dict->GetList("related_applications", &list)) {
-    LOG(ERROR) << "\"related_applications\" must be a list.";
+    log.Error("\"related_applications\" must be a list.");
     return false;
   }
 
@@ -316,7 +337,7 @@
   for (size_t i = 0; i < related_applications_size; ++i) {
     base::DictionaryValue* related_application = nullptr;
     if (!list->GetDictionary(i, &related_application) || !related_application) {
-      LOG(ERROR) << "\"related_applications\" must be a list of dictionaries.";
+      log.Error("\"related_applications\" must be a list of dictionaries.");
       output->clear();
       return false;
     }
@@ -328,9 +349,10 @@
     }
 
     if (output->size() >= kMaximumNumberOfItems) {
-      LOG(ERROR) << "\"related_applications\" must contain at most "
-                 << kMaximumNumberOfItems
-                 << " entries with \"platform\": \"play\".";
+      log.Error(base::StringPrintf(
+          "\"related_applications\" must contain at most %zu entries with "
+          "\"platform\": \"play\".",
+          kMaximumNumberOfItems));
       output->clear();
       return false;
     }
@@ -341,10 +363,10 @@
     if (!related_application->HasKey(kId) ||
         !related_application->HasKey(kMinVersion) ||
         !related_application->HasKey(kFingerprints)) {
-      LOG(ERROR) << "Each \"platform\": \"play\" entry in "
-                    "\"related_applications\" must contain \""
-                 << kId << "\", \"" << kMinVersion << "\", and \""
-                 << kFingerprints << "\".";
+      log.Error(base::StringPrintf(
+          "Each \"platform\": \"play\" entry in \"related_applications\" must "
+          "contain \"%s\", \"%s\", and \"%s\".",
+          kId, kMinVersion, kFingerprints));
       return false;
     }
 
@@ -353,7 +375,8 @@
 
     if (!related_application->GetString(kId, &section.id) ||
         section.id.empty() || !base::IsStringASCII(section.id)) {
-      LOG(ERROR) << "\"" << kId << "\" must be a non-empty ASCII string.";
+      log.Error(
+          base::StringPrintf("\"%s\" must be a non-empty ASCII string.", kId));
       output->clear();
       return false;
     }
@@ -362,8 +385,8 @@
     if (!related_application->GetString(kMinVersion, &min_version) ||
         min_version.empty() || !base::IsStringASCII(min_version) ||
         !base::StringToInt64(min_version, &section.min_version)) {
-      LOG(ERROR) << "\"" << kMinVersion
-                 << "\" must be a string convertible into a number.";
+      log.Error(base::StringPrintf(
+          "\"%s\" must be a string convertible into a number.", kMinVersion));
       output->clear();
       return false;
     }
@@ -372,9 +395,9 @@
     if (!related_application->GetList(kFingerprints, &fingerprints_list) ||
         fingerprints_list->empty() ||
         fingerprints_list->GetSize() > kMaximumNumberOfItems) {
-      LOG(ERROR) << "\"" << kFingerprints
-                 << "\" must be a non-empty list of at most "
-                 << kMaximumNumberOfItems << " items.";
+      log.Error(base::StringPrintf(
+          "\"%s\" must be a non-empty list of at most %zu items.",
+          kFingerprints, kMaximumNumberOfItems));
       output->clear();
       return false;
     }
@@ -391,15 +414,16 @@
           !fingerprint_dict->GetString("value", &fingerprint_value) ||
           fingerprint_value.empty() ||
           !base::IsStringASCII(fingerprint_value)) {
-        LOG(ERROR) << "Each entry in \"" << kFingerprints
-                   << "\" must be a dictionary with \"type\": "
-                      "\"sha256_cert\" and a non-empty ASCII string \"value\".";
+        log.Error(base::StringPrintf(
+            "Each entry in \"%s\" must be a dictionary with \"type\": "
+            "\"sha256_cert\" and a non-empty ASCII string \"value\".",
+            kFingerprints));
         output->clear();
         return false;
       }
 
       std::vector<uint8_t> hash =
-          FingerprintStringToByteArray(fingerprint_value);
+          FingerprintStringToByteArray(fingerprint_value, log);
       if (hash.empty()) {
         output->clear();
         return false;
@@ -414,101 +438,178 @@
   return true;
 }
 
+// static
+bool PaymentManifestParser::ParseWebAppInstallationInfoIntoStructs(
+    std::unique_ptr<base::Value> value,
+    const ErrorLogger& log,
+    WebAppInstallationInfo* installation_info,
+    std::vector<WebAppIcon>* icons) {
+  DCHECK(installation_info);
+  DCHECK(icons);
+
+  std::unique_ptr<base::DictionaryValue> dict =
+      base::DictionaryValue::From(std::move(value));
+  if (!dict) {
+    log.Error("Web app manifest must be a JSON dictionary.");
+    return false;
+  }
+
+  {
+    base::DictionaryValue* service_worker_dict = nullptr;
+    if (!dict->GetDictionary(kServiceWorker, &service_worker_dict)) {
+      log.Error(
+          base::StringPrintf("\"%s\" must be a dictionary", kServiceWorker));
+      return false;
+    }
+
+    if (!service_worker_dict->GetString(kServiceWorkerSrc,
+                                        &installation_info->sw_js_url) ||
+        installation_info->sw_js_url.empty() ||
+        !base::IsStringUTF8(installation_info->sw_js_url)) {
+      log.Error(
+          base::StringPrintf("\"%s\".\"%s\" must be a non-empty UTF8 string.",
+                             kServiceWorker, kServiceWorkerSrc));
+      return false;
+    }
+
+    service_worker_dict->GetString(kServiceWorkerScope,
+                                   &installation_info->sw_scope);
+
+    bool use_cache = false;
+    if (service_worker_dict->GetBoolean(kServiceWorkerUseCache, &use_cache)) {
+      installation_info->sw_use_cache = use_cache;
+    }
+  }
+
+  dict->GetString(kWebAppName, &installation_info->name);
+  if (installation_info->name.empty()) {
+    log.Warn(
+        base::StringPrintf("No \"%s\" string in the manifest.", kWebAppName));
+  }
+
+  // Extract icons.
+  base::ListValue* icons_list = nullptr;
+  if (!dict->GetList(kWebAppIcons, &icons_list)) {
+    log.Warn(
+        base::StringPrintf("No \"%s\" list in the manifest.", kWebAppIcons));
+    return true;
+  }
+
+  for (const auto& icon : *icons_list) {
+    if (!icon.is_dict()) {
+      log.Warn(base::StringPrintf(
+          "Each item in the list \"%s\" should be a dictionary.",
+          kWebAppIcons));
+      continue;
+    }
+
+    WebAppIcon web_app_icon;
+    const base::Value* icon_src =
+        icon.FindKeyOfType(kWebAppIconSrc, base::Value::Type::STRING);
+    if (!icon_src || icon_src->GetString().empty() ||
+        !base::IsStringUTF8(icon_src->GetString())) {
+      log.Warn(base::StringPrintf(
+          "Each dictionary in the list \"%s\" should contain a non-empty UTF8 "
+          "string field \"%s\".",
+          kWebAppIcons, kWebAppIconSrc));
+      continue;
+    }
+    web_app_icon.src = icon_src->GetString();
+
+    const base::Value* icon_sizes =
+        icon.FindKeyOfType(kWebAppIconSizes, base::Value::Type::STRING);
+    if (!icon_sizes || icon_sizes->GetString().empty() ||
+        !base::IsStringUTF8(icon_sizes->GetString())) {
+      log.Warn(base::StringPrintf(
+          "Each dictionary in the list \"%s\" should contain a non-empty UTF8 "
+          "string field \"%s\".",
+          kWebAppIcons, kWebAppIconSizes));
+    } else {
+      web_app_icon.sizes = icon_sizes->GetString();
+    }
+
+    const base::Value* icon_type =
+        icon.FindKeyOfType(kWebAppIconType, base::Value::Type::STRING);
+    if (!icon_type || icon_type->GetString().empty() ||
+        !base::IsStringUTF8(icon_type->GetString())) {
+      log.Warn(base::StringPrintf(
+          "Each dictionary in the list \"%s\" should contain a non-empty UTF8 "
+          "string field \"%s\".",
+          kWebAppIcons, kWebAppIconType));
+    } else {
+      web_app_icon.type = icon_type->GetString();
+    }
+
+    icons->emplace_back(web_app_icon);
+  }
+
+  return true;
+}
+
 void PaymentManifestParser::OnPaymentMethodParse(
     PaymentMethodCallback callback,
-    std::unique_ptr<base::Value> value) {
+    std::unique_ptr<base::Value> value,
+    const std::string& json_parser_error) {
   parse_payment_callback_counter_--;
 
   std::vector<GURL> web_app_manifest_urls;
   std::vector<url::Origin> supported_origins;
   bool all_origins_supported = false;
-  ParsePaymentMethodManifestIntoVectors(
-      std::move(value), &web_app_manifest_urls, &supported_origins,
-      &all_origins_supported);
 
-  // Can trigger synchronous deletion of this object, so can't access any of the
-  // member variables after this block.
+  if (json_parser_error.empty()) {
+    ParsePaymentMethodManifestIntoVectors(
+        std::move(value), *log_, &web_app_manifest_urls, &supported_origins,
+        &all_origins_supported);
+  } else {
+    log_->Error(json_parser_error);
+  }
+
+  // Can trigger synchronous deletion of this object, so can't access any of
+  // the member variables after this statement.
   std::move(callback).Run(web_app_manifest_urls, supported_origins,
                           all_origins_supported);
 }
 
-void PaymentManifestParser::OnWebAppParse(WebAppCallback callback,
-                                          std::unique_ptr<base::Value> value) {
+void PaymentManifestParser::OnWebAppParse(
+    WebAppCallback callback,
+    std::unique_ptr<base::Value> value,
+    const std::string& json_parser_error) {
   parse_webapp_callback_counter_--;
 
   std::vector<WebAppManifestSection> manifest;
-  ParseWebAppManifestIntoVector(std::move(value), &manifest);
+  if (json_parser_error.empty()) {
+    ParseWebAppManifestIntoVector(std::move(value), *log_, &manifest);
+  } else {
+    log_->Error(json_parser_error);
+  }
 
-  // Can trigger synchronous deletion of this object, so can't access any of the
-  // member variables after this block.
+  // Can trigger synchronous deletion of this object, so can't access any of
+  // the member variables after this statement.
   std::move(callback).Run(manifest);
 }
 
 void PaymentManifestParser::OnWebAppParseInstallationInfo(
     WebAppInstallationInfoCallback callback,
-    std::unique_ptr<base::Value> value) {
-  // TODO(crbug.com/782270): Move this function into a static function for unit
-  // test.
-  if (!value || value->FindKey({kServiceWorker}) == nullptr) {
-    return std::move(callback).Run(nullptr, nullptr);
-  }
-
-  std::unique_ptr<WebAppInstallationInfo> sw =
-      std::make_unique<WebAppInstallationInfo>();
-  auto* sw_path = value->FindPath({kServiceWorker, kServiceWorkerSrc});
-  if (sw_path == nullptr) {
-    LOG(ERROR) << "Service Worker js src cannot be empty.";
-    return std::move(callback).Run(nullptr, nullptr);
-  }
-  sw->sw_js_url = sw_path->GetString();
-
-  sw_path = value->FindPath({kServiceWorker, kServiceWorkerScope});
-  if (sw_path != nullptr) {
-    sw->sw_scope = sw_path->GetString();
-  }
-
-  sw_path = value->FindPath({kServiceWorker, kServiceWorkerUseCache});
-  if (sw_path != nullptr) {
-    sw->sw_use_cache = sw_path->GetBool();
-  }
-
-  auto* name_key = value->FindKey({kWebAppName});
-  if (name_key != nullptr) {
-    sw->name = name_key->GetString();
-  }
-
-  // Extract icons.
+    std::unique_ptr<base::Value> value,
+    const std::string& json_parser_error) {
+  std::unique_ptr<WebAppInstallationInfo> installation_info;
   std::unique_ptr<std::vector<WebAppIcon>> icons;
-  auto* icons_key = value->FindKey({kWebAppIcons});
-  if (icons_key != nullptr) {
+
+  if (json_parser_error.empty()) {
+    installation_info = std::make_unique<WebAppInstallationInfo>();
     icons = std::make_unique<std::vector<WebAppIcon>>();
-    for (const auto& icon : icons_key->GetList()) {
-      if (!icon.is_dict())
-        continue;
-
-      WebAppIcon web_app_icon;
-      const base::Value* icon_src =
-          icon.FindKeyOfType(kWebAppIconSrc, base::Value::Type::STRING);
-      if (!icon_src || icon_src->GetString().empty())
-        continue;
-      web_app_icon.src = icon_src->GetString();
-
-      const base::Value* icon_sizes =
-          icon.FindKeyOfType(kWebAppIconSizes, base::Value::Type::STRING);
-      if (!icon_sizes || icon_sizes->GetString().empty())
-        continue;
-      web_app_icon.sizes = icon_sizes->GetString();
-
-      const base::Value* icon_type =
-          icon.FindKeyOfType(kWebAppIconType, base::Value::Type::STRING);
-      if (icon_type)
-        web_app_icon.type = icon_type->GetString();
-
-      icons->emplace_back(web_app_icon);
+    if (!ParseWebAppInstallationInfoIntoStructs(
+            std::move(value), *log_, installation_info.get(), icons.get())) {
+      installation_info.reset();
+      icons.reset();
     }
+  } else {
+    log_->Error(json_parser_error);
   }
 
-  return std::move(callback).Run(std::move(sw), std::move(icons));
+  // Can trigger synchronous deletion of this object, so can't access any of
+  // the member variables after this statement.
+  std::move(callback).Run(std::move(installation_info), std::move(icons));
 }
 
 }  // namespace payments
diff --git a/components/payments/content/utility/payment_manifest_parser.h b/components/payments/content/utility/payment_manifest_parser.h
index 1c49e25..07c3b32 100644
--- a/components/payments/content/utility/payment_manifest_parser.h
+++ b/components/payments/content/utility/payment_manifest_parser.h
@@ -21,6 +21,8 @@
 
 namespace payments {
 
+class ErrorLogger;
+
 // Parser for payment method manifests and web app manifests.
 //
 // Example 1 of valid payment method manifest structure:
@@ -98,7 +100,7 @@
       base::OnceCallback<void(std::unique_ptr<WebAppInstallationInfo>,
                               std::unique_ptr<std::vector<WebAppIcon>>)>;
 
-  PaymentManifestParser();
+  explicit PaymentManifestParser(std::unique_ptr<ErrorLogger> log);
   ~PaymentManifestParser();
 
   void ParsePaymentMethodManifest(const std::string& content,
@@ -115,25 +117,37 @@
   // Visible for tests.
   static void ParsePaymentMethodManifestIntoVectors(
       std::unique_ptr<base::Value> value,
+      const ErrorLogger& log,
       std::vector<GURL>* web_app_manifest_urls,
       std::vector<url::Origin>* supported_origins,
       bool* all_origins_supported);
 
   static bool ParseWebAppManifestIntoVector(
       std::unique_ptr<base::Value> value,
+      const ErrorLogger& log,
       std::vector<WebAppManifestSection>* output);
 
+  static bool ParseWebAppInstallationInfoIntoStructs(
+      std::unique_ptr<base::Value> value,
+      const ErrorLogger& log,
+      WebAppInstallationInfo* installation_info,
+      std::vector<WebAppIcon>* icons);
+
  private:
   void OnPaymentMethodParse(PaymentMethodCallback callback,
-                            std::unique_ptr<base::Value> value);
+                            std::unique_ptr<base::Value> value,
+                            const std::string& json_parser_error);
   void OnWebAppParse(WebAppCallback callback,
-                     std::unique_ptr<base::Value> value);
+                     std::unique_ptr<base::Value> value,
+                     const std::string& json_parser_error);
   void OnWebAppParseInstallationInfo(WebAppInstallationInfoCallback callback,
-                                     std::unique_ptr<base::Value> value);
+                                     std::unique_ptr<base::Value> value,
+                                     const std::string& json_parser_error);
 
   int64_t parse_payment_callback_counter_ = 0;
   int64_t parse_webapp_callback_counter_ = 0;
 
+  std::unique_ptr<ErrorLogger> log_;
   base::WeakPtrFactory<PaymentManifestParser> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentManifestParser);
diff --git a/components/payments/content/utility/payment_manifest_parser_unittest.cc b/components/payments/content/utility/payment_manifest_parser_unittest.cc
index dd9daa0..15ff429 100644
--- a/components/payments/content/utility/payment_manifest_parser_unittest.cc
+++ b/components/payments/content/utility/payment_manifest_parser_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/payments/content/utility/payment_manifest_parser.h"
 
 #include "base/json/json_reader.h"
+#include "components/payments/core/error_logger.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -22,8 +23,8 @@
   std::unique_ptr<base::Value> value = base::JSONReader::Read(input);
 
   PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
-      std::move(value), &actual_web_app_urls, &actual_supported_origins,
-      &actual_all_origins_supported);
+      std::move(value), ErrorLogger(), &actual_web_app_urls,
+      &actual_supported_origins, &actual_all_origins_supported);
 
   EXPECT_TRUE(actual_web_app_urls.empty()) << actual_web_app_urls.front();
   EXPECT_TRUE(actual_supported_origins.empty())
@@ -43,8 +44,8 @@
   std::unique_ptr<base::Value> value = base::JSONReader::Read(input);
 
   PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
-      std::move(value), &actual_web_app_urls, &actual_supported_origins,
-      &actual_all_origins_supported);
+      std::move(value), ErrorLogger(), &actual_web_app_urls,
+      &actual_supported_origins, &actual_all_origins_supported);
 
   EXPECT_EQ(expected_web_app_urls, actual_web_app_urls);
   EXPECT_EQ(expected_supported_origins, actual_supported_origins);
@@ -276,8 +277,8 @@
 void ExpectUnableToParseWebAppManifest(const std::string& input) {
   std::unique_ptr<base::Value> value = base::JSONReader::Read(input);
   std::vector<WebAppManifestSection> sections;
-  PaymentManifestParser::ParseWebAppManifestIntoVector(std::move(value),
-                                                       &sections);
+  PaymentManifestParser::ParseWebAppManifestIntoVector(
+      std::move(value), ErrorLogger(), &sections);
   EXPECT_TRUE(sections.empty());
 }
 
@@ -289,7 +290,7 @@
   std::unique_ptr<base::Value> value = base::JSONReader::Read(input);
   std::vector<WebAppManifestSection> sections;
   EXPECT_TRUE(PaymentManifestParser::ParseWebAppManifestIntoVector(
-      std::move(value), &sections));
+      std::move(value), ErrorLogger(), &sections));
   ASSERT_EQ(1U, sections.size());
   EXPECT_EQ(expected_id, sections.front().id);
   EXPECT_EQ(expected_min_version, sections.front().min_version);
@@ -716,7 +717,7 @@
 
   std::vector<WebAppManifestSection> sections;
   EXPECT_TRUE(PaymentManifestParser::ParseWebAppManifestIntoVector(
-      std::move(value), &sections));
+      std::move(value), ErrorLogger(), &sections));
 
   ASSERT_EQ(2U, sections.size());
 
@@ -739,5 +740,129 @@
       sections.back().fingerprints);
 }
 
+// Web app installation information parsing:
+
+void ExpectUnableToParseInstallInfo(const std::string& input) {
+  auto value = base::JSONReader::Read(input);
+  auto installation_info = std::make_unique<WebAppInstallationInfo>();
+  auto icons =
+      std::make_unique<std::vector<PaymentManifestParser::WebAppIcon>>();
+  EXPECT_FALSE(PaymentManifestParser::ParseWebAppInstallationInfoIntoStructs(
+      std::move(value), ErrorLogger(), installation_info.get(), icons.get()));
+}
+
+void ExpectParsedInstallInfo(
+    const std::string& input,
+    const WebAppInstallationInfo& expected_installation_info,
+    const std::vector<PaymentManifestParser::WebAppIcon>& expected_icons) {
+  auto value = base::JSONReader::Read(input);
+  WebAppInstallationInfo actual_installation_info;
+  std::vector<PaymentManifestParser::WebAppIcon> actual_icons;
+  EXPECT_TRUE(PaymentManifestParser::ParseWebAppInstallationInfoIntoStructs(
+      std::move(value), ErrorLogger(), &actual_installation_info,
+      &actual_icons));
+  EXPECT_EQ(expected_installation_info.icon == nullptr,
+            actual_installation_info.icon == nullptr);
+  EXPECT_EQ(expected_installation_info.name, actual_installation_info.name);
+  EXPECT_EQ(expected_installation_info.sw_js_url,
+            actual_installation_info.sw_js_url);
+  EXPECT_EQ(expected_installation_info.sw_scope,
+            actual_installation_info.sw_scope);
+  EXPECT_EQ(expected_installation_info.sw_use_cache,
+            actual_installation_info.sw_use_cache);
+  EXPECT_EQ(expected_icons.size(), actual_icons.size());
+  for (size_t i = 0; i < expected_icons.size() && i < actual_icons.size();
+       ++i) {
+    EXPECT_EQ(expected_icons[i].src, actual_icons[i].src);
+    EXPECT_EQ(expected_icons[i].sizes, actual_icons[i].sizes);
+    EXPECT_EQ(expected_icons[i].type, actual_icons[i].type);
+  }
+}
+
+TEST(PaymentManifestParserTest, NullInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo(std::string());
+}
+
+TEST(PaymentManifestParserTest, NonJsonInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("this is not json");
+}
+
+TEST(PaymentManifestParserTest, StringInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("\"this is a string\"");
+}
+
+TEST(PaymentManifestParserTest, EmptyDictionaryInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("{}");
+}
+
+TEST(PaymentManifestParserTest, StringServiceWorkerInInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("{\"serviceworker\": \"sw.js\"}");
+}
+
+TEST(PaymentManifestParserTest, IntegerSrcInInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("{\"serviceworker\": {\"src\": 0}}");
+}
+
+TEST(PaymentManifestParserTest, NullCharInSrcInInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("{\"serviceworker\": {\"src\": \"\0\"}}");
+}
+
+TEST(PaymentManifestParserTest, EmptyServiceWorkerSrcInInstallInfoIsMalformed) {
+  ExpectUnableToParseInstallInfo("{\"serviceworker\": {\"src\": \"\"}}");
+}
+
+TEST(PaymentManifestParserTest, MinimumWellFormedInstallInfo) {
+  WebAppInstallationInfo expected_installation_info;
+  expected_installation_info.sw_js_url = "sw.js";
+  expected_installation_info.sw_scope = "";
+  expected_installation_info.sw_use_cache = false;
+
+  std::vector<PaymentManifestParser::WebAppIcon> expected_icons;
+
+  ExpectParsedInstallInfo(
+      "{"
+      "  \"serviceworker\": {"
+      "    \"src\": \"sw.js\""
+      "  }"
+      "}",
+      expected_installation_info, expected_icons);
+}
+
+TEST(PaymentManifestParserTest, WellFormedInstallInfo) {
+  WebAppInstallationInfo expected_installation_info;
+  expected_installation_info.name = "Pay with BobPay";
+  expected_installation_info.sw_js_url = "sw.js";
+  expected_installation_info.sw_scope = "/some/scope/";
+  expected_installation_info.sw_use_cache = true;
+
+  PaymentManifestParser::WebAppIcon expected_icon;
+  expected_icon.src = "bobpay.png";
+  expected_icon.sizes = "48x48";
+  expected_icon.type = "image/png";
+  std::vector<PaymentManifestParser::WebAppIcon> expected_icons(1,
+                                                                expected_icon);
+
+  ExpectParsedInstallInfo(
+      "{"
+      "  \"name\": \"Pay with BobPay\","
+      "  \"icons\": [{"
+      "    \"src\": \"bobpay.png\","
+      "    \"sizes\": \"48x48\","
+      "    \"type\": \"image/png\""
+      "  }],"
+      "  \"serviceworker\": {"
+      "    \"src\": \"sw.js\","
+      "    \"scope\": \"/some/scope/\","
+      "    \"use_cache\": true"
+      "  },"
+      "  \"prefer_related_applications\": true,"
+      "  \"related_applications\": [{"
+      "      \"platform\": \"play\","
+      "      \"id\": \"com.bobpay\""
+      "  }]"
+      "}",
+      expected_installation_info, expected_icons);
+}
+
 }  // namespace
 }  // namespace payments
diff --git a/components/payments/content/utility/payment_method_manifest_parser_fuzzer.cc b/components/payments/content/utility/payment_method_manifest_parser_fuzzer.cc
index 4bc4053a..a250b5d8 100644
--- a/components/payments/content/utility/payment_method_manifest_parser_fuzzer.cc
+++ b/components/payments/content/utility/payment_method_manifest_parser_fuzzer.cc
@@ -12,14 +12,12 @@
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "components/payments/content/utility/payment_manifest_parser.h"
+#include "components/payments/core/error_logger.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 struct IcuEnvironment {
-  IcuEnvironment() {
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    CHECK(base::i18n::InitializeICU());
-  }
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
   // used by ICU integration.
   base::AtExitManager at_exit_manager;
 };
@@ -34,8 +32,10 @@
   base::StringPiece json_data(reinterpret_cast<const char*>(data), size);
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json_data);
 
+  payments::ErrorLogger log;
+  log.DisableInTest();
   payments::PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
-      std::move(value), &web_app_manifest_urls, &supported_origins,
+      std::move(value), log, &web_app_manifest_urls, &supported_origins,
       &all_origins_supported);
   return 0;
 }
diff --git a/components/payments/content/utility/payment_web_app_manifest_parser_fuzzer.cc b/components/payments/content/utility/payment_web_app_manifest_parser_fuzzer.cc
index d324ef8..d0b6ac6 100644
--- a/components/payments/content/utility/payment_web_app_manifest_parser_fuzzer.cc
+++ b/components/payments/content/utility/payment_web_app_manifest_parser_fuzzer.cc
@@ -10,22 +10,18 @@
 #include <vector>
 
 #include "base/json/json_reader.h"
-#include "base/logging.h"
 #include "components/payments/content/utility/payment_manifest_parser.h"
 #include "components/payments/content/web_app_manifest.h"
-
-struct Environment {
-  Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
-};
-
-Environment* env = new Environment();
+#include "components/payments/core/error_logger.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   std::string json_data(reinterpret_cast<const char*>(data), size);
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json_data);
 
+  payments::ErrorLogger log;
+  log.DisableInTest();
   std::vector<payments::WebAppManifestSection> output;
   payments::PaymentManifestParser::ParseWebAppManifestIntoVector(
-      std::move(value), &output);
+      std::move(value), log, &output);
   return 0;
 }
diff --git a/components/payments/content/web_app_manifest.h b/components/payments/content/web_app_manifest.h
index 5112e0a..25cf2769 100644
--- a/components/payments/content/web_app_manifest.h
+++ b/components/payments/content/web_app_manifest.h
@@ -38,7 +38,7 @@
   std::string name;
   std::string sw_js_url;
   std::string sw_scope;
-  bool sw_use_cache;
+  bool sw_use_cache = false;
 };
 
 }  // namespace payments
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn
index e8dda04..0585f46e 100644
--- a/components/payments/core/BUILD.gn
+++ b/components/payments/core/BUILD.gn
@@ -14,6 +14,8 @@
     "can_make_payment_query.h",
     "currency_formatter.cc",
     "currency_formatter.h",
+    "error_logger.cc",
+    "error_logger.h",
     "features.cc",
     "features.h",
     "journey_logger.cc",
diff --git a/components/payments/core/error_logger.cc b/components/payments/core/error_logger.cc
new file mode 100644
index 0000000..9038c89
--- /dev/null
+++ b/components/payments/core/error_logger.cc
@@ -0,0 +1,29 @@
+// 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/payments/core/error_logger.h"
+
+#include "base/logging.h"
+
+namespace payments {
+
+ErrorLogger::ErrorLogger() = default;
+
+ErrorLogger::~ErrorLogger() = default;
+
+void ErrorLogger::DisableInTest() {
+  enabled_ = false;
+}
+
+void ErrorLogger::Warn(const std::string& warning_message) const {
+  if (enabled_)
+    LOG(WARNING) << warning_message;
+}
+
+void ErrorLogger::Error(const std::string& error_message) const {
+  if (enabled_)
+    LOG(ERROR) << error_message;
+}
+
+}  // namespace payments
diff --git a/components/payments/core/error_logger.h b/components/payments/core/error_logger.h
new file mode 100644
index 0000000..bc1721a6
--- /dev/null
+++ b/components/payments/core/error_logger.h
@@ -0,0 +1,47 @@
+// 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_PAYMENTS_CORE_ERROR_LOGGER_H_
+#define COMPONENTS_PAYMENTS_CORE_ERROR_LOGGER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace payments {
+
+// Logs messages to stderr. See DeveloperConsoleLogger for an implementation
+// that logs messages to the developer console instead, which is more useful for
+// web developers.
+//
+// Sample usage:
+//
+//   ErrorLogger log;
+//   log.Warn("Something's wrong.");
+class ErrorLogger {
+ public:
+  ErrorLogger();
+  virtual ~ErrorLogger();
+
+  // Disables logs for tests to keep test output clean.
+  void DisableInTest();
+
+  // Warn the web developer of a potential problem, e.g., "Could not find icons
+  // for the payment handler, so the user experience will be degraded."
+  virtual void Warn(const std::string& warning_message) const;
+
+  // Let the web developer know that an error has been encountered, e.g.,
+  // "Invalid format for 'serviceworker' field."
+  virtual void Error(const std::string& error_message) const;
+
+ protected:
+  bool enabled_ = true;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ErrorLogger);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CORE_ERROR_LOGGER_H_
diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc
index 3ba5bec..f507a0b 100644
--- a/components/payments/core/payment_manifest_downloader.cc
+++ b/components/payments/core/payment_manifest_downloader.cc
@@ -11,8 +11,10 @@
 #include "base/optional.h"
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/link_header_util/link_header_util.h"
+#include "components/payments/core/error_logger.h"
 #include "net/base/load_flags.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
@@ -29,25 +31,29 @@
 
 GURL ParseResponseHeader(const GURL& original_url,
                          const GURL& final_url,
-                         scoped_refptr<net::HttpResponseHeaders> headers) {
+                         scoped_refptr<net::HttpResponseHeaders> headers,
+                         const ErrorLogger& log) {
   if (!headers) {
-    LOG(ERROR) << "No HTTP headers found on " << final_url
-               << " for payment method manifest.";
+    log.Error(base::StringPrintf(
+        "No HTTP headers found on \"%s\" for payment method manifest.",
+        final_url.spec().c_str()));
     return GURL();
   }
 
   int response_code = headers->response_code();
   if (response_code != net::HTTP_OK && response_code != net::HTTP_NO_CONTENT) {
-    LOG(ERROR) << "Unable to make a HEAD request to " << final_url
-               << " for payment method manifest.";
+    log.Error(base::StringPrintf(
+        "Unable to make a HEAD request to \"%s\" for payment method manifest.",
+        final_url.spec().c_str()));
     return GURL();
   }
 
   std::string link_header;
   headers->GetNormalizedHeader("link", &link_header);
   if (link_header.empty()) {
-    LOG(ERROR) << "No HTTP Link headers found on " << final_url
-               << " for payment method manifest.";
+    log.Error(base::StringPrintf(
+        "No HTTP Link headers found on \"%s\" for payment method manifest.",
+        final_url.spec().c_str()));
     return GURL();
   }
 
@@ -70,18 +76,28 @@
       return original_url.Resolve(payment_method_manifest_url);
   }
 
-  LOG(ERROR) << "No rel=\"payment-method-manifest\" HTTP Link headers found on "
-             << final_url << " for payment method manifest.";
+  log.Error(base::StringPrintf(
+      "No rel=\"payment-method-manifest\" HTTP Link headers found on \"%s\" "
+      "for payment method manifest.",
+      final_url.spec().c_str()));
   return GURL();
 }
 
-bool IsValidManifestUrl(const GURL& url) {
-  return url.is_valid() &&
-         (url.SchemeIs(url::kHttpsScheme) ||
-          (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)));
+bool IsValidManifestUrl(const GURL& url, const ErrorLogger& log) {
+  bool is_valid = url.is_valid() &&
+                  (url.SchemeIs(url::kHttpsScheme) ||
+                   (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)));
+  if (!is_valid) {
+    log.Error(
+        base::StringPrintf("\"%s\" is not a valid payment manifest URL with "
+                           "HTTPS scheme (or HTTP scheme for localhost).",
+                           url.spec().c_str()));
+  }
+  return is_valid;
 }
 
-GURL ParseRedirectUrl(const net::RedirectInfo& redirect_info) {
+GURL ParseRedirectUrl(const net::RedirectInfo& redirect_info,
+                      const ErrorLogger& log) {
   // Do not follow net::HTTP_MULTIPLE_CHOICES, net::HTTP_NOT_MODIFIED and
   // net::HTTP_USE_PROXY redirects.
   if (redirect_info.status_code != net::HTTP_MOVED_PERMANENTLY &&
@@ -89,10 +105,13 @@
       redirect_info.status_code != net::HTTP_SEE_OTHER &&
       redirect_info.status_code != net::HTTP_TEMPORARY_REDIRECT &&
       redirect_info.status_code != net::HTTP_PERMANENT_REDIRECT) {
+    log.Error(
+        "Cannot follow HTTP_MULTIPLE_CHOICES, HTTP_NOT_MODIFIED, and "
+        "HTTP_USE_PROXY redirects for payment manifests.");
     return GURL();
   }
 
-  if (!IsValidManifestUrl(redirect_info.new_url))
+  if (!IsValidManifestUrl(redirect_info.new_url, log))
     return GURL();
 
   return redirect_info.new_url;
@@ -101,10 +120,11 @@
 std::string ParseResponseContent(
     const GURL& final_url,
     const std::string& response_body,
-    scoped_refptr<net::HttpResponseHeaders> headers) {
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    const ErrorLogger& log) {
   if (!headers || headers->response_code() != net::HTTP_OK) {
-    LOG(ERROR) << "Unable to download " << final_url
-               << " for payment manifests.";
+    log.Error(base::StringPrintf("Unable to download payment manifest \"%s\".",
+                                 final_url.spec().c_str()));
     return std::string();
   }
 
@@ -114,15 +134,19 @@
 }  // namespace
 
 PaymentManifestDownloader::PaymentManifestDownloader(
+    std::unique_ptr<ErrorLogger> log,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : url_loader_factory_(std::move(url_loader_factory)) {}
+    : log_(std::move(log)), url_loader_factory_(std::move(url_loader_factory)) {
+  DCHECK(log_);
+  DCHECK(url_loader_factory_);
+}
 
 PaymentManifestDownloader::~PaymentManifestDownloader() {}
 
 void PaymentManifestDownloader::DownloadPaymentMethodManifest(
     const GURL& url,
     PaymentManifestDownloadCallback callback) {
-  DCHECK(IsValidManifestUrl(url));
+  DCHECK(IsValidManifestUrl(url, *log_));
   // Restrict number of redirects for efficiency and breaking circle.
   InitiateDownload(url, "HEAD",
                    /*allowed_number_of_redirects=*/3, std::move(callback));
@@ -131,7 +155,7 @@
 void PaymentManifestDownloader::DownloadWebAppManifest(
     const GURL& url,
     PaymentManifestDownloadCallback callback) {
-  DCHECK(IsValidManifestUrl(url));
+  DCHECK(IsValidManifestUrl(url, *log_));
   InitiateDownload(url, "GET", /*allowed_number_of_redirects=*/0,
                    std::move(callback));
 }
@@ -154,17 +178,26 @@
   // Manually follow some type of redirects.
   if (download->allowed_number_of_redirects > 0) {
     DCHECK(download->method == "HEAD");
-    GURL redirect_url = ParseRedirectUrl(redirect_info);
-    if (!redirect_url.is_empty() &&
-        // Do not allow cross site redirects.
-        net::registry_controlled_domains::SameDomainOrHost(
-            download->original_url, redirect_url,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      InitiateDownload(redirect_url, "HEAD",
-                       --download->allowed_number_of_redirects,
-                       std::move(download->callback));
-      return;
+    GURL redirect_url = ParseRedirectUrl(redirect_info, *log_);
+    if (!redirect_url.is_empty()) {
+      // Do not allow cross site redirects.
+      if (net::registry_controlled_domains::SameDomainOrHost(
+              download->original_url, redirect_url,
+              net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+        InitiateDownload(redirect_url, "HEAD",
+                         --download->allowed_number_of_redirects,
+                         std::move(download->callback));
+        return;
+      }
+      log_->Error(base::StringPrintf(
+          "Cross-site redirect from \"%s\" to \"%s\" not allowed for payment "
+          "manifests.",
+          download->original_url.spec().c_str(), redirect_url.spec().c_str()));
     }
+  } else {
+    log_->Error(
+        "Unable to download the payment manifest because reached the maximum "
+        "number of redirects.");
   }
 
   std::move(download->callback).Run(std::string());
@@ -199,24 +232,20 @@
   downloads_.erase(download_it);
 
   if (download->method == "HEAD") {
-    GURL url = ParseResponseHeader(download->original_url, final_url, headers);
-    if (IsValidManifestUrl(url)) {
+    GURL url =
+        ParseResponseHeader(download->original_url, final_url, headers, *log_);
+    if (IsValidManifestUrl(url, *log_)) {
       InitiateDownload(url, "GET",
                        /*allowed_number_of_redirects=*/0,
                        std::move(download->callback));
     } else {
-      // If the URL is empty, then ParseResponseHeader() has already printed
-      // an explanation.
-      if (!url.is_empty())
-        LOG(ERROR) << url << " is not a valid payment method manifest URL.";
       std::move(download->callback).Run(std::string());
     }
-
     return;
   }
 
   std::move(download->callback)
-      .Run(ParseResponseContent(final_url, response_body, headers));
+      .Run(ParseResponseContent(final_url, response_body, headers, *log_));
 }
 
 network::SimpleURLLoader* PaymentManifestDownloader::GetLoaderForTesting() {
@@ -234,7 +263,7 @@
     const std::string& method,
     int allowed_number_of_redirects,
     PaymentManifestDownloadCallback callback) {
-  DCHECK(IsValidManifestUrl(url));
+  DCHECK(IsValidManifestUrl(url, *log_));
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("payment_manifest_downloader", R"(
diff --git a/components/payments/core/payment_manifest_downloader.h b/components/payments/core/payment_manifest_downloader.h
index d4583a3e..6af6345 100644
--- a/components/payments/core/payment_manifest_downloader.h
+++ b/components/payments/core/payment_manifest_downloader.h
@@ -24,10 +24,12 @@
 class SharedURLLoaderFactory;
 class SimpleURLLoader;
 struct ResourceResponseHead;
-}
+}  // namespace network
 
 namespace payments {
 
+class ErrorLogger;
+
 // Called on completed download of a manifest. Download failure results in empty
 // contents. Failure to download the manifest can happen because of the
 // following reasons:
@@ -50,7 +52,8 @@
 // HTTP response codes are 200 or 204.
 class PaymentManifestDownloader {
  public:
-  explicit PaymentManifestDownloader(
+  PaymentManifestDownloader(
+      std::unique_ptr<ErrorLogger> log,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   virtual ~PaymentManifestDownloader();
@@ -131,6 +134,7 @@
                         int allowed_number_of_redirects,
                         PaymentManifestDownloadCallback callback);
 
+  std::unique_ptr<ErrorLogger> log_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // Downloads are identified by network::SimpleURLLoader pointers, because
diff --git a/components/payments/core/payment_manifest_downloader_unittest.cc b/components/payments/core/payment_manifest_downloader_unittest.cc
index 695c34f..b390c29 100644
--- a/components/payments/core/payment_manifest_downloader_unittest.cc
+++ b/components/payments/core/payment_manifest_downloader_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/payments/core/error_logger.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -23,7 +24,8 @@
         shared_url_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_factory_)),
-        downloader_(shared_url_loader_factory_) {
+        downloader_(std::make_unique<ErrorLogger>(),
+                    shared_url_loader_factory_) {
     downloader_.DownloadPaymentMethodManifest(
         test_url_,
         base::BindOnce(&PaymentMethodManifestDownloaderTest::OnManifestDownload,
@@ -278,7 +280,8 @@
         shared_url_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_factory_)),
-        downloader_(shared_url_loader_factory_) {
+        downloader_(std::make_unique<ErrorLogger>(),
+                    shared_url_loader_factory_) {
     downloader_.DownloadWebAppManifest(
         test_url_,
         base::BindOnce(&WebAppManifestDownloaderTest::OnManifestDownload,
diff --git a/components/payments/core/test_payment_manifest_downloader.cc b/components/payments/core/test_payment_manifest_downloader.cc
index 86360898..a14b37e 100644
--- a/components/payments/core/test_payment_manifest_downloader.cc
+++ b/components/payments/core/test_payment_manifest_downloader.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_util.h"
+#include "components/payments/core/error_logger.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 
@@ -15,7 +16,8 @@
 
 TestDownloader::TestDownloader(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : PaymentManifestDownloader(url_loader_factory) {}
+    : PaymentManifestDownloader(std::make_unique<ErrorLogger>(),
+                                url_loader_factory) {}
 
 TestDownloader::~TestDownloader() {}
 
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index f8e05457..18d2ddd 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -26,8 +26,13 @@
     "account_info.h",
     "signin_metrics.cc",
     "signin_metrics.h",
+    "signin_pref_names.cc",
+    "signin_pref_names.h",
+    "signin_switches.cc",
+    "signin_switches.h",
   ]
   deps = [
+    ":signin_buildflags",
     "//components/account_id",
   ]
   public_deps = [
@@ -66,10 +71,6 @@
     "signin_manager.h",
     "signin_manager_base.cc",
     "signin_manager_base.h",
-    "signin_pref_names.cc",
-    "signin_pref_names.h",
-    "signin_switches.cc",
-    "signin_switches.h",
   ]
 
   if (is_chromeos) {
@@ -78,7 +79,6 @@
 
   deps = [
     ":shared",
-    ":signin_buildflags",
     "//base",
     "//components/data_use_measurement/core",
     "//components/keyed_service/core",
@@ -169,6 +169,7 @@
     "//components/os_crypt",
     "//components/webdata/common",
     "//crypto",
+    "//services/identity/public/cpp",
     "//services/network/public/cpp",
     "//skia",
     "//sql",
@@ -293,6 +294,7 @@
     "//components/webdata/common",
     "//google_apis:test_support",
     "//net:test_support",
+    "//services/identity/public/cpp:test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/signin/core/browser/DEPS b/components/signin/core/browser/DEPS
index 53ed0e4..26ab35f5 100644
--- a/components/signin/core/browser/DEPS
+++ b/components/signin/core/browser/DEPS
@@ -7,6 +7,7 @@
   "+google/cacheinvalidation",
   "+jni",
   "+mojo/public",
+  "+services/identity/public",
   "+services/network/public",
   "+services/network/test",
   "+third_party/re2",
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index 7999ed40..da0487a 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -28,6 +28,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 using signin::AccountReconcilorDelegate;
 
@@ -180,13 +181,13 @@
 
 AccountReconcilor::AccountReconcilor(
     ProfileOAuth2TokenService* token_service,
-    SigninManagerBase* signin_manager,
+    identity::IdentityManager* identity_manager,
     SigninClient* client,
     GaiaCookieManagerService* cookie_manager_service,
     std::unique_ptr<signin::AccountReconcilorDelegate> delegate)
     : delegate_(std::move(delegate)),
       token_service_(token_service),
-      signin_manager_(signin_manager),
+      identity_manager_(identity_manager),
       client_(client),
       cookie_manager_service_(cookie_manager_service),
       registered_with_token_service_(false),
@@ -453,7 +454,7 @@
                                  base::Unretained(this)));
   }
 
-  const std::string& account_id = signin_manager_->GetAuthenticatedAccountId();
+  const std::string& account_id = identity_manager_->GetPrimaryAccountId();
   if (token_service_->RefreshTokenHasError(account_id) &&
       delegate_->ShouldAbortReconcileIfPrimaryHasError()) {
     VLOG(1) << "AccountReconcilor::StartReconcile: primary has error, abort.";
@@ -548,7 +549,7 @@
       << "Ignore " << accounts.size() - verified_gaia_accounts.size()
       << " unverified account(s).";
 
-  std::string primary_account = signin_manager_->GetAuthenticatedAccountId();
+  std::string primary_account = identity_manager_->GetPrimaryAccountId();
   // Revoking tokens for secondary accounts causes the AccountTracker to
   // completely remove them from Chrome.
   // Revoking the token for the primary account is not supported (it should be
@@ -582,8 +583,7 @@
   if (!delegate_->ShouldRevokeTokensOnCookieDeleted())
     return;
 
-  const std::string& primary_account =
-      signin_manager_->GetAuthenticatedAccountId();
+  const std::string& primary_account = identity_manager_->GetPrimaryAccountId();
   // Revoke secondary tokens.
   RevokeAllSecondaryTokens(
       token_service_, AccountReconcilorDelegate::RevokeTokenOption::kRevoke,
@@ -663,7 +663,7 @@
     VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie";
     // Really messed up state.  Blow away the gaia cookie completely and
     // rebuild it, making sure the primary account as specified by the
-    // SigninManager is the first session in the gaia cookie.
+    // IdentityManager is the first session in the gaia cookie.
     PerformLogoutAllAccountsAction();
     gaia_accounts.clear();
   }
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h
index 782faab8..01fb781 100644
--- a/components/signin/core/browser/account_reconcilor.h
+++ b/components/signin/core/browser/account_reconcilor.h
@@ -25,7 +25,6 @@
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_header_helper.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_token_service.h"
@@ -33,6 +32,10 @@
 // Enables usage of Gaia Auth Multilogin endpoint for identity consistency.
 extern const base::Feature kUseMultiloginEndpoint;
 
+namespace identity {
+class IdentityManager;
+}
+
 namespace signin {
 class AccountReconcilorDelegate;
 }
@@ -96,7 +99,7 @@
 
   AccountReconcilor(
       ProfileOAuth2TokenService* token_service,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       SigninClient* client,
       GaiaCookieManagerService* cookie_manager_service,
       std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
@@ -229,8 +232,6 @@
   }
 
   // Register and unregister with dependent services.
-  void RegisterWithSigninManager();
-  void UnregisterWithSigninManager();
   void RegisterWithTokenService();
   void UnregisterWithTokenService();
   void RegisterWithCookieManagerService();
@@ -307,8 +308,8 @@
   // The ProfileOAuth2TokenService associated with this reconcilor.
   ProfileOAuth2TokenService* token_service_;
 
-  // The SigninManager associated with this reconcilor.
-  SigninManagerBase* signin_manager_;
+  // The IdentityManager associated with this reconcilor.
+  identity::IdentityManager* identity_manager_;
 
   // The SigninClient associated with this reconcilor.
   SigninClient* client_;
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index 508f008..ed2fb2e 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -39,6 +39,7 @@
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "net/url_request/test_url_fetcher_factory.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -116,17 +117,17 @@
  public:
   DummyAccountReconcilorWithDelegate(
       ProfileOAuth2TokenService* token_service,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       SigninClient* client,
       GaiaCookieManagerService* cookie_manager_service,
       signin::AccountConsistencyMethod account_consistency)
       : AccountReconcilor(
             token_service,
-            signin_manager,
+            identity_manager,
             client,
             cookie_manager_service,
             CreateAccountReconcilorDelegate(client,
-                                            signin_manager,
+                                            identity_manager,
                                             account_consistency)) {
     Initialize(false /* start_reconcile_if_tokens_available */);
   }
@@ -135,13 +136,13 @@
   // gmock can't work with move only parameters.
   DummyAccountReconcilorWithDelegate(
       ProfileOAuth2TokenService* token_service,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       SigninClient* client,
       GaiaCookieManagerService* cookie_manager_service,
       signin::AccountReconcilorDelegate* delegate)
       : AccountReconcilor(
             token_service,
-            signin_manager,
+            identity_manager,
             client,
             cookie_manager_service,
             std::unique_ptr<signin::AccountReconcilorDelegate>(delegate)) {
@@ -151,12 +152,12 @@
   static std::unique_ptr<signin::AccountReconcilorDelegate>
   CreateAccountReconcilorDelegate(
       SigninClient* signin_client,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       signin::AccountConsistencyMethod account_consistency) {
     switch (account_consistency) {
       case signin::AccountConsistencyMethod::kMirror:
         return std::make_unique<signin::MirrorAccountReconcilorDelegate>(
-            signin_manager);
+            identity_manager);
       case signin::AccountConsistencyMethod::kDisabled:
       case signin::AccountConsistencyMethod::kDiceFixAuthErrors:
         return std::make_unique<signin::AccountReconcilorDelegate>();
@@ -180,14 +181,14 @@
  public:
   explicit MockAccountReconcilor(
       ProfileOAuth2TokenService* token_service,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       SigninClient* client,
       GaiaCookieManagerService* cookie_manager_service,
       signin::AccountConsistencyMethod account_consistency);
 
   explicit MockAccountReconcilor(
       ProfileOAuth2TokenService* token_service,
-      SigninManagerBase* signin_manager,
+      identity::IdentityManager* identity_manager,
       SigninClient* client,
       GaiaCookieManagerService* cookie_manager_service,
       std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
@@ -200,26 +201,26 @@
 
 MockAccountReconcilor::MockAccountReconcilor(
     ProfileOAuth2TokenService* token_service,
-    SigninManagerBase* signin_manager,
+    identity::IdentityManager* identity_manager,
     SigninClient* client,
     GaiaCookieManagerService* cookie_manager_service,
     signin::AccountConsistencyMethod account_consistency)
     : testing::StrictMock<DummyAccountReconcilorWithDelegate>(
           token_service,
-          signin_manager,
+          identity_manager,
           client,
           cookie_manager_service,
           account_consistency) {}
 
 MockAccountReconcilor::MockAccountReconcilor(
     ProfileOAuth2TokenService* token_service,
-    SigninManagerBase* signin_manager,
+    identity::IdentityManager* identity_manager,
     SigninClient* client,
     GaiaCookieManagerService* cookie_manager_service,
     std::unique_ptr<signin::AccountReconcilorDelegate> delegate)
     : testing::StrictMock<DummyAccountReconcilorWithDelegate>(
           token_service,
-          signin_manager,
+          identity_manager,
           client,
           cookie_manager_service,
           delegate.release()) {}
@@ -258,6 +259,8 @@
 
   std::string PickAccountIdForAccount(const std::string& gaia_id,
                                       const std::string& username);
+  std::string SeedAccountInfo(const std::string& gaia_id,
+                              const std::string& username);
 
   void SimulateAddAccountToCookieCompleted(
       GaiaCookieManagerService::Observer* observer,
@@ -291,6 +294,7 @@
   AccountTrackerService account_tracker_;
   FakeGaiaCookieManagerService cookie_manager_service_;
   FakeSigninManagerForTesting signin_manager_;
+  identity::IdentityTestEnvironment identity_test_env_;
   std::unique_ptr<MockAccountReconcilor> mock_reconcilor_;
   base::HistogramTester histogram_tester_;
   GURL get_check_connection_info_url_;
@@ -342,13 +346,17 @@
       cookie_manager_service_(&token_service_,
                               &test_signin_client_),
 #if defined(OS_CHROMEOS)
-      signin_manager_(&test_signin_client_, &account_tracker_) {
+      signin_manager_(&test_signin_client_, &account_tracker_),
 #else
       signin_manager_(&test_signin_client_,
                       &token_service_,
                       &account_tracker_,
-                      &cookie_manager_service_) {
+                      &cookie_manager_service_),
 #endif
+      identity_test_env_(&account_tracker_,
+                         &token_service_,
+                         &signin_manager_,
+                         &cookie_manager_service_) {
   AccountTrackerService::RegisterPrefs(pref_service_.registry());
   ProfileOAuth2TokenService::RegisterProfilePrefs(pref_service_.registry());
   SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
@@ -371,8 +379,8 @@
 MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor() {
   if (!mock_reconcilor_) {
     mock_reconcilor_ = std::make_unique<MockAccountReconcilor>(
-        &token_service_, &signin_manager_, &test_signin_client_,
-        &cookie_manager_service_, account_consistency_);
+        &token_service_, identity_test_env_.identity_manager(),
+        &test_signin_client_, &cookie_manager_service_, account_consistency_);
   }
 
   return mock_reconcilor_.get();
@@ -381,8 +389,8 @@
 MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor(
     std::unique_ptr<signin::AccountReconcilorDelegate> delegate) {
   mock_reconcilor_ = std::make_unique<MockAccountReconcilor>(
-      &token_service_, &signin_manager_, &test_signin_client_,
-      &cookie_manager_service_, std::move(delegate));
+      &token_service_, identity_test_env_.identity_manager(),
+      &test_signin_client_, &cookie_manager_service_, std::move(delegate));
 
   return mock_reconcilor_.get();
 }
@@ -400,7 +408,7 @@
 std::string AccountReconcilorTest::ConnectProfileToAccount(
     const std::string& gaia_id,
     const std::string& username) {
-  const std::string account_id = PickAccountIdForAccount(gaia_id, username);
+  const std::string account_id = SeedAccountInfo(gaia_id, username);
 #if !defined(OS_CHROMEOS)
   signin_manager()->set_password("password");
 #endif
@@ -415,6 +423,12 @@
   return account_tracker()->PickAccountIdForAccount(gaia_id, username);
 }
 
+std::string AccountReconcilorTest::SeedAccountInfo(
+    const std::string& gaia_id,
+    const std::string& username) {
+  return account_tracker()->SeedAccountInfo(gaia_id, username);
+}
+
 void AccountReconcilorTest::SimulateAddAccountToCookieCompleted(
     GaiaCookieManagerService::Observer* observer,
     const std::string& account_id,
@@ -887,8 +901,7 @@
   std::vector<Token> tokens_before_reconcile =
       ParseTokenString(GetParam().tokens);
   for (const Token& token : tokens_before_reconcile) {
-    std::string account_id =
-        PickAccountIdForAccount(token.gaia_id, token.email);
+    std::string account_id = SeedAccountInfo(token.gaia_id, token.email);
     if (token.is_authenticated)
       ConnectProfileToAccount(token.gaia_id, token.email);
     else
@@ -1002,8 +1015,7 @@
       ParseTokenString(GetParam().tokens);
   Token primary_account;
   for (const Token& token : tokens_before_reconcile) {
-    std::string account_id =
-        PickAccountIdForAccount(token.gaia_id, token.email);
+    std::string account_id = SeedAccountInfo(token.gaia_id, token.email);
     if (token.is_authenticated) {
       primary_account = token;
       ConnectProfileToAccount(token.gaia_id, token.email);
@@ -1122,8 +1134,7 @@
 // Tests that reconcile starts even when Sync is not enabled.
 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) {
   // Add a token in Chrome but do not sign in.
-  const std::string account_id =
-      PickAccountIdForAccount("12345", "user@gmail.com");
+  const std::string account_id = SeedAccountInfo("12345", "user@gmail.com");
   token_service()->UpdateCredentials(account_id, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseNoAccounts();
 
@@ -1175,10 +1186,8 @@
 TEST_P(AccountReconcilorDiceEndpointParamTest,
        DiceReconcileReuseGaiaFirstAccount) {
   // Add accounts 1 and 2 to the token service.
-  const std::string account_id_1 =
-      PickAccountIdForAccount("12345", "user@gmail.com");
-  const std::string account_id_2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id_1 = SeedAccountInfo("12345", "user@gmail.com");
+  const std::string account_id_2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id_1, "refresh_token");
   token_service()->UpdateCredentials(account_id_2, "refresh_token");
 
@@ -1187,8 +1196,7 @@
   ASSERT_EQ(account_id_2, token_service()->GetAccounts()[1]);
 
   // Add accounts 2 and 3 to the Gaia cookie.
-  const std::string account_id_3 =
-      PickAccountIdForAccount("9999", "foo@gmail.com");
+  const std::string account_id_3 = SeedAccountInfo("9999", "foo@gmail.com");
   cookie_manager_service()->SetListAccountsResponseTwoAccounts(
       "other@gmail.com", "67890", "foo@gmail.com", "9999");
 
@@ -1229,10 +1237,8 @@
 // lost.
 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) {
   // Add accounts to the token service and the Gaia cookie in a different order.
-  const std::string account_id_1 =
-      PickAccountIdForAccount("12345", "user@gmail.com");
-  const std::string account_id_2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id_1 = SeedAccountInfo("12345", "user@gmail.com");
+  const std::string account_id_2 = SeedAccountInfo("67890", "other@gmail.com");
   cookie_manager_service()->SetListAccountsResponseTwoAccounts(
       "other@gmail.com", "67890", "user@gmail.com", "12345");
   token_service()->UpdateCredentials(account_id_1, "refresh_token");
@@ -1331,7 +1337,7 @@
 
   // Add a token to Chrome.
   const std::string chrome_account_id =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+      SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(chrome_account_id, "refresh_token");
 
   if (!IsMultiloginEnabled()) {
@@ -1373,8 +1379,7 @@
   pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true);
 
   // Chrome account is consistent with the cookie.
-  const std::string account_id =
-      PickAccountIdForAccount("12345", "user@gmail.com");
+  const std::string account_id = SeedAccountInfo("12345", "user@gmail.com");
   token_service()->UpdateCredentials(account_id, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com",
                                                               "12345");
@@ -1404,8 +1409,7 @@
   SetAccountConsistency(signin::AccountConsistencyMethod::kDiceMigration);
 
   // Chrome account is consistent with the cookie.
-  const std::string account_id =
-      PickAccountIdForAccount("12345", "user@gmail.com");
+  const std::string account_id = SeedAccountInfo("12345", "user@gmail.com");
   token_service()->UpdateCredentials(account_id, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com",
                                                               "12345");
@@ -1477,8 +1481,7 @@
   // Add a tokens in Chrome, signin to Sync, but no Gaia cookies.
   const std::string account_id_1 =
       ConnectProfileToAccount("12345", "user@gmail.com");
-  const std::string account_id_2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id_2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id_2, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseNoAccounts();
   ASSERT_TRUE(token_service()->RefreshTokenIsAvailable(account_id_1));
@@ -1524,10 +1527,8 @@
   pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true);
 
   // Add a tokens in Chrome but no Gaia cookies.
-  const std::string account_id_1 =
-      PickAccountIdForAccount("12345", "user@gmail.com");
-  const std::string account_id_2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id_1 = SeedAccountInfo("12345", "user@gmail.com");
+  const std::string account_id_2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id_1, "refresh_token");
   token_service()->UpdateCredentials(account_id_2, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseNoAccounts();
@@ -1562,7 +1563,7 @@
   token_service()->UpdateCredentials(primary_account_id, "refresh_token");
   signin_manager()->SignIn("12345", "user@gmail.com", "password");
   const std::string secondary_account_id =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+      SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(secondary_account_id, "refresh_token");
 
   ASSERT_TRUE(token_service()->RefreshTokenIsAvailable(primary_account_id));
@@ -1686,8 +1687,7 @@
   std::vector<Token> tokens_before_reconcile =
       ParseTokenString(GetParam().tokens);
   for (const Token& token : tokens_before_reconcile) {
-    std::string account_id =
-        PickAccountIdForAccount(token.gaia_id, token.email);
+    std::string account_id = SeedAccountInfo(token.gaia_id, token.email);
     if (token.is_authenticated)
       ConnectProfileToAccount(token.gaia_id, token.email);
     else
@@ -2013,8 +2013,7 @@
 TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileNoopMultiple) {
   const std::string account_id =
       ConnectProfileToAccount("67890", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("12345", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("12345", "other@gmail.com");
   cookie_manager_service()->SetListAccountsResponseTwoAccounts(
       "user@gmail.com", "67890", "other@gmail.com", "12345");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
@@ -2042,8 +2041,7 @@
   cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com",
                                                               "67890");
 
-  const std::string account_id2 =
-      PickAccountIdForAccount("12345", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("12345", "other@gmail.com");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
 
   if (!IsMultiloginEnabled()) {
@@ -2153,8 +2151,7 @@
   cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com",
                                                               "12345");
 
-  const std::string account_id2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
 
   if (!IsMultiloginEnabled()) {
@@ -2263,10 +2260,8 @@
        StartReconcileAddToCookieTwice) {
   const std::string account_id =
       ConnectProfileToAccount("67890", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("12345", "other@gmail.com");
-  const std::string account_id3 =
-      PickAccountIdForAccount("34567", "third@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("12345", "other@gmail.com");
+  const std::string account_id3 = SeedAccountInfo("34567", "third@gmail.com");
 
   cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com",
                                                               "67890");
@@ -2355,8 +2350,7 @@
 TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileBadPrimary) {
   const std::string account_id =
       ConnectProfileToAccount("12345", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("67890", "other@gmail.com");
 
   token_service()->UpdateCredentials(account_id2, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseTwoAccounts(
@@ -2496,8 +2490,7 @@
   SetAccountConsistency(GetParam());
   const std::string account_id =
       ConnectProfileToAccount("12345", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
   cookie_manager_service()->SetListAccountsResponseWithParams(
       {{"user@gmail.com", "12345", false /* valid */, false /* signed_out */,
@@ -2563,8 +2556,7 @@
   // Connect profile to a primary account and then add a secondary account.
   const std::string account_id1 =
       ConnectProfileToAccount("12345", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
 
   if (!IsMultiloginEnabled()) {
@@ -2622,8 +2614,7 @@
   // Connect profile to a primary account and then add a secondary account.
   const std::string account_id1 =
       ConnectProfileToAccount("12345", "user@gmail.com");
-  const std::string account_id2 =
-      PickAccountIdForAccount("67890", "other@gmail.com");
+  const std::string account_id2 = SeedAccountInfo("67890", "other@gmail.com");
   token_service()->UpdateCredentials(account_id2, "refresh_token");
 
   // Mark the secondary account in auth error state.
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
index 4eb032b7..5485e49 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
@@ -10,18 +10,18 @@
 namespace signin {
 
 MirrorAccountReconcilorDelegate::MirrorAccountReconcilorDelegate(
-    SigninManagerBase* signin_manager)
-    : signin_manager_(signin_manager) {
-  DCHECK(signin_manager_);
-  signin_manager_->AddObserver(this);
+    identity::IdentityManager* identity_manager)
+    : identity_manager_(identity_manager) {
+  DCHECK(identity_manager_);
+  identity_manager_->AddObserver(this);
 }
 
 MirrorAccountReconcilorDelegate::~MirrorAccountReconcilorDelegate() {
-  signin_manager_->RemoveObserver(this);
+  identity_manager_->RemoveObserver(this);
 }
 
 bool MirrorAccountReconcilorDelegate::IsReconcileEnabled() const {
-  return signin_manager_->IsAuthenticated();
+  return identity_manager_->HasPrimaryAccount();
 }
 
 bool MirrorAccountReconcilorDelegate::IsAccountConsistencyEnforced() const {
@@ -72,15 +72,13 @@
   return accounts_to_send;
 }
 
-void MirrorAccountReconcilorDelegate::GoogleSigninSucceeded(
-    const std::string& account_id,
-    const std::string& username) {
+void MirrorAccountReconcilorDelegate::OnPrimaryAccountSet(
+    const AccountInfo& primary_account_info) {
   reconcilor()->EnableReconcile();
 }
 
-void MirrorAccountReconcilorDelegate::GoogleSignedOut(
-    const std::string& account_id,
-    const std::string& username) {
+void MirrorAccountReconcilorDelegate::OnPrimaryAccountCleared(
+    const AccountInfo& previous_primary_account_info) {
   reconcilor()->DisableReconcile(true /* logout_all_gaia_accounts */);
 }
 
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
index b9f230e..b69e0119 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
@@ -7,15 +7,16 @@
 
 #include "base/macros.h"
 #include "components/signin/core/browser/account_reconcilor_delegate.h"
-#include "components/signin/core/browser/signin_manager_base.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 namespace signin {
 
 // AccountReconcilorDelegate specialized for Mirror.
-class MirrorAccountReconcilorDelegate : public AccountReconcilorDelegate,
-                                        public SigninManagerBase::Observer {
+class MirrorAccountReconcilorDelegate
+    : public AccountReconcilorDelegate,
+      public identity::IdentityManager::Observer {
  public:
-  MirrorAccountReconcilorDelegate(SigninManagerBase* signin_manager);
+  MirrorAccountReconcilorDelegate(identity::IdentityManager* identity_manager);
   ~MirrorAccountReconcilorDelegate() override;
 
  private:
@@ -37,13 +38,12 @@
       const std::vector<gaia::ListedAccount>& gaia_accounts,
       const signin::MultiloginMode mode) const override;
 
-  // SigninManagerBase::Observer:
-  void GoogleSigninSucceeded(const std::string& account_id,
-                             const std::string& username) override;
-  void GoogleSignedOut(const std::string& account_id,
-                       const std::string& username) override;
+  // IdentityManager::Observer:
+  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountCleared(
+      const AccountInfo& previous_primary_account_info) override;
 
-  SigninManagerBase* signin_manager_;
+  identity::IdentityManager* identity_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(MirrorAccountReconcilorDelegate);
 };
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index b95b48c..7f439ff2 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -351,6 +351,7 @@
 
 base::WeakPtr<ModelTypeControllerDelegate>
 ClientTagBasedModelTypeProcessor::GetControllerDelegate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return weak_ptr_factory_for_controller_.GetWeakPtr();
 }
 
diff --git a/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
index df0631e..533347e 100644
--- a/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
+++ b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <vector>
 
-#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync/driver/test_sync_service.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/unified_consent/pref_names.h"
@@ -17,50 +17,22 @@
 namespace unified_consent {
 namespace {
 
-class TestSyncService : public syncer::FakeSyncService {
+class TestSyncService : public syncer::TestSyncService {
  public:
-  void set_sync_initialized(bool sync_initialized) {
-    sync_initialized_ = sync_initialized;
-  }
+  TestSyncService() { SetActiveDataTypes({}); }
+
   void AddActiveDataType(syncer::ModelType type) {
-    sync_active_data_types_.Put(type);
+    syncer::ModelTypeSet active_types = GetActiveDataTypes();
+    active_types.Put(type);
+    SetActiveDataTypes(active_types);
   }
-  void ClearActiveDataTypes() { sync_active_data_types_.Clear(); }
+
   void FireOnStateChangeOnAllObservers() {
     for (auto& observer : observers_)
       observer.OnStateChanged(this);
   }
 
-  // syncer::FakeSyncService:
-  int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
-  TransportState GetTransportState() const override {
-    return TransportState::ACTIVE;
-  }
-  syncer::ModelTypeSet GetPreferredDataTypes() const override {
-    return syncer::ModelTypeSet(syncer::ModelType::HISTORY_DELETE_DIRECTIVES,
-                                syncer::ModelType::USER_EVENTS,
-                                syncer::ModelType::EXTENSIONS);
-  }
-  bool IsFirstSetupComplete() const override { return true; }
-
-  syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override {
-    if (!sync_initialized_)
-      return syncer::SyncCycleSnapshot();
-    return syncer::SyncCycleSnapshot(
-        syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 5, 2,
-        7, false, 0, base::Time::Now(), base::Time::Now(),
-        std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
-        std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
-        sync_pb::SyncEnums::UNKNOWN_ORIGIN,
-        /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
-        /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
-        /*has_remaining_local_changes=*/false);
-  }
-
-  syncer::ModelTypeSet GetActiveDataTypes() const override {
-    return sync_active_data_types_;
-  }
-
+  // syncer::TestSyncService:
   void AddObserver(syncer::SyncServiceObserver* observer) override {
     observers_.AddObserver(observer);
   }
@@ -69,8 +41,6 @@
   }
 
  private:
-  bool sync_initialized_ = false;
-  syncer::ModelTypeSet sync_active_data_types_;
   base::ObserverList<syncer::SyncServiceObserver>::Unchecked observers_;
 };
 
@@ -133,7 +103,6 @@
   EXPECT_FALSE(helper->IsEnabled());
   EXPECT_TRUE(state_changed_notifications.empty());
 
-  sync_service_.set_sync_initialized(true);
   sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
   sync_service_.FireOnStateChangeOnAllObservers();
   EXPECT_TRUE(helper->IsEnabled());
@@ -162,7 +131,6 @@
   helper->AddObserver(this);
   EXPECT_FALSE(helper->IsEnabled());
   EXPECT_TRUE(state_changed_notifications.empty());
-  sync_service_.set_sync_initialized(true);
 
   // Peronalized data collection is disabled when only USER_EVENTS are enabled.
   sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
@@ -172,7 +140,7 @@
 
   // Peronalized data collection is disabled when only HISTORY_DELETE_DIRECTIVES
   // are enabled.
-  sync_service_.ClearActiveDataTypes();
+  sync_service_.SetActiveDataTypes({});
   sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
   sync_service_.FireOnStateChangeOnAllObservers();
   EXPECT_FALSE(helper->IsEnabled());
@@ -180,7 +148,7 @@
 
   // Personalized data collection is enabled iff USER_EVENTS and
   // HISTORY_DELETE_DIRECTIVES are enabled.
-  sync_service_.ClearActiveDataTypes();
+  sync_service_.SetActiveDataTypes({});
   sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
   sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
   sync_service_.FireOnStateChangeOnAllObservers();
@@ -200,7 +168,6 @@
   EXPECT_FALSE(helper->IsEnabled());
   EXPECT_TRUE(state_changed_notifications.empty());
 
-  sync_service_.set_sync_initialized(true);
   sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
   sync_service_.FireOnStateChangeOnAllObservers();
   EXPECT_TRUE(helper->IsEnabled());
diff --git a/components/viz/client/hit_test_data_provider_draw_quad.cc b/components/viz/client/hit_test_data_provider_draw_quad.cc
index 8cb65f1..83927ee 100644
--- a/components/viz/client/hit_test_data_provider_draw_quad.cc
+++ b/components/viz/client/hit_test_data_provider_draw_quad.cc
@@ -69,8 +69,11 @@
         hit_test_region.flags = HitTestRegionFlags::kHitTestMouse |
                                 HitTestRegionFlags::kHitTestTouch |
                                 HitTestRegionFlags::kHitTestChildSurface;
-        if (should_ask_for_child_region_)
+        if (should_ask_for_child_region_) {
           hit_test_region.flags |= HitTestRegionFlags::kHitTestAsk;
+          hit_test_region.async_hit_test_reasons =
+              AsyncHitTestReasons::kUseDrawQuadData;
+        }
         hit_test_region.rect = surface_quad->rect;
         hit_test_region.transform =
             target_to_quad_transform * transform_from_root_target;
diff --git a/components/viz/common/hit_test/aggregated_hit_test_region.h b/components/viz/common/hit_test/aggregated_hit_test_region.h
index 42476fe..cdb4c29f 100644
--- a/components/viz/common/hit_test/aggregated_hit_test_region.h
+++ b/components/viz/common/hit_test/aggregated_hit_test_region.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include "components/viz/common/hit_test/hit_test_region_list.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "ui/gfx/geometry/rect.h"
@@ -31,16 +32,22 @@
 struct AggregatedHitTestRegion {
   AggregatedHitTestRegion() = default;
 
-  AggregatedHitTestRegion(const FrameSinkId& frame_sink_id,
-                          uint32_t flags,
-                          const gfx::Rect& rect,
-                          const gfx::Transform& transform,
-                          int32_t child_count)
+  AggregatedHitTestRegion(
+      const FrameSinkId& frame_sink_id,
+      uint32_t flags,
+      const gfx::Rect& rect,
+      const gfx::Transform& transform,
+      int32_t child_count,
+      uint32_t async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest)
       : frame_sink_id(frame_sink_id),
         flags(flags),
+        async_hit_test_reasons(async_hit_test_reasons),
         rect(rect),
         child_count(child_count),
-        transform_(transform) {}
+        transform_(transform) {
+    DCHECK_EQ(!!(flags & HitTestRegionFlags::kHitTestAsk),
+              !!async_hit_test_reasons);
+  }
 
   // The FrameSinkId corresponding to this region.  Events that match
   // are routed to this surface.
@@ -49,6 +56,9 @@
   // HitTestRegionFlags to indicate the type of region.
   uint32_t flags = 0;
 
+  // AsyncHitTestReasons to indicate why we are doing slow path hit testing.
+  uint32_t async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
+
   // The rectangle that defines the region in parent region's coordinate space.
   gfx::Rect rect;
 
diff --git a/components/viz/common/hit_test/hit_test_region_list.h b/components/viz/common/hit_test/hit_test_region_list.h
index 8fbe117..8011c16c 100644
--- a/components/viz/common/hit_test/hit_test_region_list.h
+++ b/components/viz/common/hit_test/hit_test_region_list.h
@@ -41,10 +41,36 @@
   kHitTestDebug = 0x80,
 };
 
+// In viz hit testing surface layer, hit test regions are marked as kHitTestAsk
+// for various reasons. This is a class to track the reasons of why a
+// |HitTestRegion| cannot do synchronous targeting.
+enum AsyncHitTestReasons : uint32_t {
+  // The |HitTestRegion| does not have |kHitTestAsk| flag.
+  kNotAsyncHitTest = 0,
+  // The |HitTestRegion| is overlapped with its parent frame's elements.
+  kOverlappedRegion = 1 << 0,
+  // The |HitTestRegion| is clipped by irregular shape.
+  kIrregularClip = 1 << 1,
+  // The |HitTestRegion|'s surface has not been activated yet.
+  kRegionNotActive = 1 << 2,
+  // Synchronous event targeting aborts at the present of perspective transform.
+  kPerspectiveTransform = 1 << 3,
+  // The |HitTestRegion| is marked as |kHitTestAsk| because it comes from draw
+  // quad. This is a reason specifically for slow path |hit-test| with draw quad
+  // variant.
+  kUseDrawQuadData = 1 << 4,
+
+  // The maximum number of flags in this enum excluding itself.
+  kAsyncHitTestReasonCount = 5,
+};
+
 struct HitTestRegion {
   // HitTestRegionFlags to indicate the type of HitTestRegion.
   uint32_t flags = 0;
 
+  // AsyncHitTestReasons to indicate the reason of having |kHitTestAsk| flag.
+  uint32_t async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
+
   // FrameSinkId of this region.
   FrameSinkId frame_sink_id;
 
@@ -69,6 +95,8 @@
   // kHitTestIgnore keeps previous match in the parent (transparent).
   uint32_t flags = 0;
 
+  uint32_t async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
+
   // The bounds of the surface.
   gfx::Rect bounds;
 
diff --git a/components/viz/host/hit_test/hit_test_query.cc b/components/viz/host/hit_test/hit_test_query.cc
index fdc23f29..9cf531ca 100644
--- a/components/viz/host/hit_test/hit_test_query.cc
+++ b/components/viz/host/hit_test/hit_test_query.cc
@@ -176,6 +176,9 @@
     target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
     target->location_in_target = gfx::PointF();
     target->flags = HitTestRegionFlags::kHitTestAsk;
+    RecordSlowPathHitTestReasons(
+        AsyncHitTestReasons::kPerspectiveTransform |
+        hit_test_data_[region_index].async_hit_test_reasons);
     return true;
   }
 
@@ -221,11 +224,18 @@
   const uint32_t flags = hit_test_data_[region_index].flags;
   if (!RegionMatchEventSource(event_source, flags))
     return false;
+
+  // Verify that async_hit_test_reasons is set if and only if there's
+  // a kHitTestAsk flag.
+  DCHECK_EQ(!!(flags & HitTestRegionFlags::kHitTestAsk),
+            !!hit_test_data_[region_index].async_hit_test_reasons);
   if (flags &
       (HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestAsk)) {
     target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
     target->location_in_target = location_in_target;
     target->flags = flags;
+    RecordSlowPathHitTestReasons(
+        hit_test_data_[region_index].async_hit_test_reasons);
     return true;
   }
   return false;
@@ -324,6 +334,27 @@
     bad_message_gpu_callback_.Run();
 }
 
+void HitTestQuery::RecordSlowPathHitTestReasons(uint32_t reasons) const {
+  static const char* kAsyncHitTestReasonsHistogramName =
+      "Event.VizHitTest.AsyncHitTestReasons";
+  if (reasons == AsyncHitTestReasons::kNotAsyncHitTest) {
+    UMA_HISTOGRAM_ENUMERATION(
+        kAsyncHitTestReasonsHistogramName,
+        AsyncHitTestReasons::kNotAsyncHitTest,
+        AsyncHitTestReasons::kAsyncHitTestReasonCount + 1);
+    return;
+  }
+
+  for (uint32_t i = 0; i < AsyncHitTestReasons::kAsyncHitTestReasonCount; ++i) {
+    unsigned val = 1 << i;
+    if (reasons & val) {
+      UMA_HISTOGRAM_ENUMERATION(
+          kAsyncHitTestReasonsHistogramName, i + 1,
+          AsyncHitTestReasons::kAsyncHitTestReasonCount + 1);
+    }
+  }
+}
+
 std::string HitTestQuery::PrintHitTestData() const {
   std::ostringstream oss;
   base::stack<uint32_t> parents;
diff --git a/components/viz/host/hit_test/hit_test_query.h b/components/viz/host/hit_test/hit_test_query.h
index cef04e9..c48af94 100644
--- a/components/viz/host/hit_test/hit_test_query.h
+++ b/components/viz/host/hit_test/hit_test_query.h
@@ -130,6 +130,8 @@
 
   void ReceivedBadMessageFromGpuProcess() const;
 
+  void RecordSlowPathHitTestReasons(uint32_t) const;
+
   std::vector<AggregatedHitTestRegion> hit_test_data_;
 
   // Log bad message and shut down Viz process when it is compromised.
diff --git a/components/viz/host/hit_test/hit_test_query_fuzzer.cc b/components/viz/host/hit_test/hit_test_query_fuzzer.cc
index 0f247e09..7be968ac 100644
--- a/components/viz/host/hit_test/hit_test_query_fuzzer.cc
+++ b/components/viz/host/hit_test/hit_test_query_fuzzer.cc
@@ -26,6 +26,7 @@
     return;
   viz::FrameSinkId frame_sink_id(GetNextUInt32(fuzz), GetNextUInt32(fuzz));
   uint32_t flags = GetNextUInt32(fuzz);
+  uint32_t reasons = GetNextUInt32(fuzz);
   gfx::Rect rect(fuzz->ConsumeUint8(), fuzz->ConsumeUint8(),
                  fuzz->ConsumeUint16(), fuzz->ConsumeUint16());
   int32_t child_count =
@@ -35,7 +36,8 @@
     std::string matrix_bytes = fuzz->ConsumeBytes(sizeof(gfx::Transform));
     memcpy(&transform, matrix_bytes.data(), sizeof(gfx::Transform));
   }
-  regions->emplace_back(frame_sink_id, flags, rect, transform, child_count);
+  regions->emplace_back(frame_sink_id, flags, rect, transform, child_count,
+                        reasons);
   // Always add the first frame sink id, because the root needs to be in the
   // list of FrameSinkId.
   if (regions->size() == 1 || fuzz->ConsumeBool())
diff --git a/components/viz/host/hit_test/hit_test_query_unittest.cc b/components/viz/host/hit_test/hit_test_query_unittest.cc
index aa7172c..afb50b2a 100644
--- a/components/viz/host/hit_test/hit_test_query_unittest.cc
+++ b/components/viz/host/hit_test/hit_test_query_unittest.cc
@@ -1002,7 +1002,8 @@
   gfx::Transform transform_e_to_e;
   active_data_.push_back(AggregatedHitTestRegion(
       e_id, HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
-      e_bounds, transform_e_to_e, 0));  // e
+      e_bounds, transform_e_to_e, 0,
+      AsyncHitTestReasons::kOverlappedRegion));  // e
   SendHitTestData();
 
   // All points are in e's coordinate system when we reach this case.
@@ -1055,7 +1056,8 @@
   active_data_.push_back(AggregatedHitTestRegion(
       c2_id,
       HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
-      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
+      c2_bounds_in_e, transform_e_to_c2, 0,
+      AsyncHitTestReasons::kOverlappedRegion));  // c2
   SendHitTestData();
 
   // All points are in e's coordinate system when we reach this case.
diff --git a/components/viz/service/hit_test/hit_test_aggregator.cc b/components/viz/service/hit_test/hit_test_aggregator.cc
index e0d7017f..b06588ca6 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -175,6 +175,7 @@
   int32_t child_count = region_index - 1;
   UMA_HISTOGRAM_COUNTS_1000("Event.VizHitTest.HitTestRegions", region_index);
   SetRegionAt(0, surface_id.frame_sink_id(), hit_test_region_list->flags,
+              hit_test_region_list->async_hit_test_reasons,
               hit_test_region_list->bounds, hit_test_region_list->transform,
               child_count);
 }
@@ -192,6 +193,7 @@
   }
 
   uint32_t flags = region.flags;
+  uint32_t reasons = region.async_hit_test_reasons;
   gfx::Transform transform = region.transform;
 
   if (region.flags & HitTestRegionFlags::kHitTestChildSurface) {
@@ -213,6 +215,7 @@
       // targeting for this embedded client.
       flags |= (HitTestRegionFlags::kHitTestAsk |
                 HitTestRegionFlags::kHitTestNotActive);
+      reasons |= AsyncHitTestReasons::kRegionNotActive;
     } else {
       // Rather than add a node in the tree for this hit_test_region_list
       // element we can simplify the tree by merging the flags and transform
@@ -221,6 +224,7 @@
         transform.PreconcatTransform(hit_test_region_list->transform);
 
       flags |= hit_test_region_list->flags;
+      reasons |= hit_test_region_list->async_hit_test_reasons;
 
       bool enabled;
       TRACE_EVENT_CATEGORY_GROUP_ENABLED(
@@ -250,19 +254,21 @@
   }
   DCHECK_GE(region_index - parent_index - 1, 0u);
   int32_t child_count = region_index - parent_index - 1;
-  SetRegionAt(parent_index, region.frame_sink_id, flags, region.rect, transform,
-              child_count);
+  SetRegionAt(parent_index, region.frame_sink_id, flags, reasons, region.rect,
+              transform, child_count);
   return region_index;
 }
 
 void HitTestAggregator::SetRegionAt(size_t index,
                                     const FrameSinkId& frame_sink_id,
                                     uint32_t flags,
+                                    uint32_t async_hit_test_reasons,
                                     const gfx::Rect& rect,
                                     const gfx::Transform& transform,
                                     int32_t child_count) {
-  hit_test_data_[index] = AggregatedHitTestRegion(frame_sink_id, flags, rect,
-                                                  transform, child_count);
+  hit_test_data_[index] =
+      AggregatedHitTestRegion(frame_sink_id, flags, rect, transform,
+                              child_count, async_hit_test_reasons);
   hit_test_data_size_++;
 
   hit_test_debug_ |= flags & kHitTestDebug;
diff --git a/components/viz/service/hit_test/hit_test_aggregator.h b/components/viz/service/hit_test/hit_test_aggregator.h
index 70d7580b..6a0f173 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.h
+++ b/components/viz/service/hit_test/hit_test_aggregator.h
@@ -57,6 +57,7 @@
   void SetRegionAt(size_t index,
                    const FrameSinkId& frame_sink_id,
                    uint32_t flags,
+                   uint32_t reasons,
                    const gfx::Rect& rect,
                    const gfx::Transform& transform,
                    int32_t child_count);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8159b6c..7f0b544 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2177,6 +2177,7 @@
       "accessibility/browser_accessibility_android.h",
       "accessibility/browser_accessibility_manager_android.cc",
       "accessibility/browser_accessibility_manager_android.h",
+      "accessibility/browser_accessibility_state_impl_android.cc",
       "accessibility/captioning_controller.cc",
       "accessibility/captioning_controller.h",
       "accessibility/web_contents_accessibility_android.cc",
diff --git a/content/browser/accessibility/browser_accessibility_state_impl.cc b/content/browser/accessibility/browser_accessibility_state_impl.cc
index 2c185d0ad..24aaa70 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -71,7 +71,7 @@
   // Let each platform do its own initialization.
   PlatformInitialize();
 
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_ANDROID)
   // The delay is necessary because assistive technology sometimes isn't
   // detected until after the user interacts in some way, so a reasonable delay
   // gives us better numbers.
@@ -80,8 +80,8 @@
       base::Bind(&BrowserAccessibilityStateImpl::UpdateHistograms, this),
       base::TimeDelta::FromSeconds(ACCESSIBILITY_HISTOGRAM_DELAY_SECS));
 #else
-  // On all other platforms, UpdateHistograms should be called on the UI
-  // thread because it needs to be able to access PrefService.
+  // On MacOS, UpdateHistograms should be called on the UI thread because it
+  // needs to be able to access PrefService.
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&BrowserAccessibilityStateImpl::UpdateHistograms, this),
@@ -170,7 +170,7 @@
   return accessibility_mode_;
 }
 
-#if !defined(OS_WIN) && !defined(OS_MACOSX)
+#if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_MACOSX)
 void BrowserAccessibilityStateImpl::PlatformInitialize() {}
 
 void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() {
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_android.cc b/content/browser/accessibility/browser_accessibility_state_impl_android.cc
new file mode 100644
index 0000000..92daf8c
--- /dev/null
+++ b/content/browser/accessibility/browser_accessibility_state_impl_android.cc
@@ -0,0 +1,27 @@
+// 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/browser/accessibility/browser_accessibility_state_impl.h"
+
+#include "base/android/jni_android.h"
+#include "content/public/browser/browser_thread.h"
+#include "jni/BrowserAccessibilityState_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace content {
+
+void BrowserAccessibilityStateImpl::PlatformInitialize() {}
+
+void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() {
+  // NOTE: this method is run from the file thread to reduce jank, since
+  // there's no guarantee these system calls will return quickly. Be careful
+  // not to add any code that isn't safe to run from a non-main thread!
+  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_BrowserAccessibilityState_recordAccessibilityHistograms(env);
+}
+
+}  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy.cc b/content/browser/background_fetch/background_fetch_delegate_proxy.cc
index 9ac035b..3ac5447 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy.cc
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy.cc
@@ -168,6 +168,13 @@
       delegate_->Abort(job_unique_id);
   }
 
+  void MarkJobComplete(const std::string& job_unique_id) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+    if (delegate_)
+      delegate_->MarkJobComplete(job_unique_id);
+  }
+
   void UpdateUI(const std::string& job_unique_id,
                 const base::Optional<std::string>& title,
                 const base::Optional<SkBitmap>& icon) {
@@ -381,7 +388,13 @@
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&Core::Abort, ui_core_ptr_, job_unique_id));
+}
 
+void BackgroundFetchDelegateProxy::MarkJobComplete(
+    const std::string& job_unique_id) {
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&Core::MarkJobComplete, ui_core_ptr_, job_unique_id));
   job_details_map_.erase(job_unique_id);
 }
 
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy.h b/content/browser/background_fetch/background_fetch_delegate_proxy.h
index 0a9f94b7..6b266b2 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy.h
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy.h
@@ -111,6 +111,9 @@
   // requests already called OnDownloadComplete.
   void Abort(const std::string& job_unique_id);
 
+  // Called when the fetch associated |job_unique_id| is completed.
+  void MarkJobComplete(const std::string& job_unique_id);
+
  private:
   class Core;
 
diff --git a/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc b/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
index 6f60d144..688e14c 100644
--- a/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc
@@ -68,10 +68,13 @@
                          base::Unretained(this), job_unique_id, guid));
     }
   }
+
   void Abort(const std::string& job_unique_id) override {
     aborted_jobs_.insert(job_unique_id);
   }
 
+  void MarkJobComplete(const std::string& job_unique_id) override {}
+
   void UpdateUI(const std::string& job_unique_id,
                 const base::Optional<std::string>& title,
                 const base::Optional<SkBitmap>& icon) override {
@@ -257,7 +260,6 @@
   delegate_proxy_.Abort(kExampleUniqueId);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(controller.request_started_) << "Aborted job started";
   EXPECT_FALSE(controller.request_completed_) << "Aborted job completed";
   EXPECT_TRUE(controller2.request_started_) << "Normal job did not start";
   EXPECT_TRUE(controller2.request_completed_) << "Normal job did not complete";
diff --git a/content/browser/background_fetch/background_fetch_scheduler.cc b/content/browser/background_fetch/background_fetch_scheduler.cc
index 179c9f5..f657895 100644
--- a/content/browser/background_fetch/background_fetch_scheduler.cc
+++ b/content/browser/background_fetch/background_fetch_scheduler.cc
@@ -164,6 +164,9 @@
   // downloaded data around so long as there are references to it, and delete
   // it once there is none. We don't need to do that accounting.
   data_manager_->DeleteRegistration(registration_id, base::DoNothing());
+
+  // Notify other systems that this registration is complete.
+  delegate_proxy_->MarkJobComplete(registration_id.unique_id());
 }
 
 void BackgroundFetchScheduler::DispatchClickEvent(
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index a8e7f4e..468f3a4 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -641,6 +641,10 @@
     ASSERT_FALSE(fetches[i].response->blob->uuid.empty());
     ASSERT_GT(fetches[i].response->blob->size, 0u);
   }
+
+  auto* delegate = static_cast<MockBackgroundFetchDelegate*>(
+      browser_context()->GetBackgroundFetchDelegate());
+  EXPECT_TRUE(delegate->completed_jobs().count(registration.unique_id));
 }
 
 TEST_F(BackgroundFetchServiceTest, FetchFailEventDispatch) {
@@ -732,6 +736,10 @@
               blink::mojom::ServiceWorkerResponseError::kUnknown);
     EXPECT_TRUE(fetches[i].response->cors_exposed_header_names.empty());
   }
+
+  auto* delegate = static_cast<MockBackgroundFetchDelegate*>(
+      browser_context()->GetBackgroundFetchDelegate());
+  EXPECT_TRUE(delegate->completed_jobs().count(registration.unique_id));
 }
 
 TEST_F(BackgroundFetchServiceTest, UpdateUI) {
@@ -815,6 +823,10 @@
   GetRegistration(service_worker_registration_id, kExampleDeveloperId,
                   &second_error, &second_registration);
   ASSERT_EQ(second_error, blink::mojom::BackgroundFetchError::INVALID_ID);
+
+  auto* delegate = static_cast<MockBackgroundFetchDelegate*>(
+      browser_context()->GetBackgroundFetchDelegate());
+  EXPECT_TRUE(delegate->completed_jobs().count(registration_id.unique_id()));
 }
 
 TEST_F(BackgroundFetchServiceTest, AbortInvalidDeveloperIdArgument) {
diff --git a/content/browser/background_fetch/mock_background_fetch_delegate.cc b/content/browser/background_fetch/mock_background_fetch_delegate.cc
index cf678f8..951fc00 100644
--- a/content/browser/background_fetch/mock_background_fetch_delegate.cc
+++ b/content/browser/background_fetch/mock_background_fetch_delegate.cc
@@ -171,6 +171,11 @@
   aborted_jobs_.insert(job_unique_id);
 }
 
+void MockBackgroundFetchDelegate::MarkJobComplete(
+    const std::string& job_unique_id) {
+  completed_jobs_.insert(job_unique_id);
+}
+
 void MockBackgroundFetchDelegate::UpdateUI(
     const std::string& job_unique_id,
     const base::Optional<std::string>& title,
diff --git a/content/browser/background_fetch/mock_background_fetch_delegate.h b/content/browser/background_fetch/mock_background_fetch_delegate.h
index 9808c6f..b4d5fc6 100644
--- a/content/browser/background_fetch/mock_background_fetch_delegate.h
+++ b/content/browser/background_fetch/mock_background_fetch_delegate.h
@@ -83,6 +83,7 @@
                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
                    const net::HttpRequestHeaders& headers) override;
   void Abort(const std::string& job_unique_id) override;
+  void MarkJobComplete(const std::string& job_unique_id) override;
   void UpdateUI(const std::string& job_unique_id,
                 const base::Optional<std::string>& title,
                 const base::Optional<SkBitmap>& icon) override;
@@ -90,6 +91,10 @@
   void RegisterResponse(const GURL& url,
                         std::unique_ptr<TestResponse> response);
 
+  const std::set<std::string>& completed_jobs() const {
+    return completed_jobs_;
+  }
+
  private:
   // Posts (to the default task runner) a callback that is only run if the job
   // indicated by |job_unique_id| has not been aborted.
@@ -113,6 +118,9 @@
   // Set of unique job ids that have been aborted.
   std::set<std::string> aborted_jobs_;
 
+  // Set of unique job ids that have been completed.
+  std::set<std::string> completed_jobs_;
+
   // Map from download GUIDs to unique job ids.
   std::map<std::string, std::string> download_guid_to_job_id_map_;
 
diff --git a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
index b730cee3..5510718 100644
--- a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
@@ -84,6 +84,7 @@
     blink::ParsedFeaturePolicy result(1);
     result[0].feature = feature;
     result[0].matches_all_origins = false;
+    result[0].disposition = blink::mojom::FeaturePolicyDisposition::kEnforce;
     for (const std::string& origin : origins)
       result[0].origins.push_back(url::Origin::Create(GURL(origin)));
     return result;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index d73589c..a44d629 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2820,7 +2820,8 @@
     return;
   // Rebuild the feature policy for this frame.
   ResetFeaturePolicy();
-  feature_policy_->SetHeaderPolicy(parsed_header);
+  feature_policy_->SetHeaderPolicy(*DirectivesWithDisposition(
+      blink::mojom::FeaturePolicyDisposition::kEnforce, parsed_header));
 
   // Update the feature policy and sandbox flags in the frame tree. This will
   // send any updates to proxies if necessary.
@@ -5437,7 +5438,10 @@
   blink::ParsedFeaturePolicy container_policy =
       frame_tree_node()->effective_frame_policy().container_policy;
   feature_policy_ = blink::FeaturePolicy::CreateFromParentPolicy(
-      parent_policy, container_policy, last_committed_origin_);
+      parent_policy,
+      *DirectivesWithDisposition(
+          blink::mojom::FeaturePolicyDisposition::kEnforce, container_policy),
+      last_committed_origin_);
 }
 
 void RenderFrameHostImpl::CreateAudioInputStreamFactory(
diff --git a/content/browser/net/reporting_service_proxy.cc b/content/browser/net/reporting_service_proxy.cc
index d207fa4..f661d32 100644
--- a/content/browser/net/reporting_service_proxy.cc
+++ b/content/browser/net/reporting_service_proxy.cc
@@ -108,12 +108,14 @@
   void QueueFeaturePolicyViolationReport(
       const GURL& url,
       const std::string& policy,
+      const std::string& disposition,
       const std::string& message,
       const base::Optional<std::string>& source_file,
       int line_number,
       int column_number) override {
     auto body = std::make_unique<base::DictionaryValue>();
     body->SetString("policy", policy);
+    body->SetString("disposition", disposition);
     body->SetString("message", message);
     if (source_file)
       body->SetString("sourceFile", *source_file);
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index c0390a5..9145b6f 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -692,8 +692,9 @@
   EXPECT_EQ(last_request_relative_url(), "/title2.html");
 }
 
-// Flaky on Linux TSan (https://crbug.com/889855)
-#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+// Flaky on Linux TSan and Android ASan (https://crbug.com/889855)
+#if (defined(OS_LINUX) && defined(THREAD_SANITIZER)) || \
+    (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
 #define MAYBE_FetchFromServiceWorkerControlledPage_NoFetchHandler \
   DISABLED_FetchFromServiceWorkerControlledPage_NoFetchHandler
 #else
@@ -742,8 +743,9 @@
   service_worker_context->RemoveObserver(&observer);
 }
 
-// Flaky on Linux TSan (https://crbug.com/889855)
-#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+// Flaky on Linux TSan and Android ASan (https://crbug.com/889855)
+#if (defined(OS_LINUX) && defined(THREAD_SANITIZER)) || \
+    (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
 #define MAYBE_FetchFromServiceWorkerControlledPage_PassThrough \
   DISABLED_FetchFromServiceWorkerControlledPage_PassThrough
 #else
@@ -791,8 +793,9 @@
   service_worker_context->RemoveObserver(&observer);
 }
 
-// Flaky on Linux TSan (https://crbug.com/889855)
-#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+// Flaky on Linux TSan and Android ASan (https://crbug.com/889855)
+#if (defined(OS_LINUX) && defined(THREAD_SANITIZER)) || \
+    (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
 #define MAYBE_FetchFromServiceWorkerControlledPage_RespondWithFetch \
   DISABLED_FetchFromServiceWorkerControlledPage_RespondWithFetch
 #else
diff --git a/content/browser/payments/payment_app_info_fetcher.cc b/content/browser/payments/payment_app_info_fetcher.cc
index 4efac89..e34d146 100644
--- a/content/browser/payments/payment_app_info_fetcher.cc
+++ b/content/browser/payments/payment_app_info_fetcher.cc
@@ -79,12 +79,21 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (provider_hosts->size() == 0U) {
+    // Cannot print this error to the developer console, because the appropriate
+    // developer console has not been found.
+    LOG(ERROR)
+        << "Unable to find the top level web content for retrieving the web "
+           "app manifest of a payment handler for \""
+        << context_url << "\".";
     RunCallbackAndDestroy();
     return;
   }
 
   for (const auto& frame : *provider_hosts) {
-    // Find out the render frame host registering the payment app.
+    // Find out the render frame host registering the payment app. Although a
+    // service worker can manage instruments, the first instrument must be set
+    // on a page that has a link to a web app manifest, so it can be fetched
+    // here.
     RenderFrameHostImpl* render_frame_host =
         RenderFrameHostImpl::FromID(frame.child_id, frame.frame_routing_id);
     if (!render_frame_host ||
@@ -102,9 +111,33 @@
     }
     WebContentsImpl* top_level_web_content = static_cast<WebContentsImpl*>(
         WebContents::FromRenderFrameHost(top_level_render_frame_host));
-    if (!top_level_web_content || top_level_web_content->IsHidden() ||
-        !url::IsSameOriginWith(context_url,
+    if (!top_level_web_content) {
+      top_level_render_frame_host->AddMessageToConsole(
+          content::CONSOLE_MESSAGE_LEVEL_ERROR,
+          "Unable to find the web page for \"" + context_url.spec() +
+              "\" to fetch payment handler manifest (for name and icon).");
+      continue;
+    }
+
+    if (top_level_web_content->IsHidden()) {
+      top_level_render_frame_host->AddMessageToConsole(
+          content::CONSOLE_MESSAGE_LEVEL_ERROR,
+          "Unable to fetch payment handler manifest (for name and icon) for "
+          "\"" +
+              context_url.spec() + "\" from a hidden top level web page \"" +
+              top_level_web_content->GetLastCommittedURL().spec() + "\".");
+      continue;
+    }
+
+    if (!url::IsSameOriginWith(context_url,
                                top_level_web_content->GetLastCommittedURL())) {
+      top_level_render_frame_host->AddMessageToConsole(
+          content::CONSOLE_MESSAGE_LEVEL_ERROR,
+          "Unable to fetch payment handler manifest (for name and icon) for "
+          "\"" +
+              context_url.spec() +
+              "\" from a cross-origin top level web page \"" +
+              top_level_web_content->GetLastCommittedURL().spec() + "\".");
       continue;
     }
 
@@ -118,6 +151,13 @@
     return;
   }
 
+  // Cannot print this error to the developer console, because the appropriate
+  // developer console has not been found.
+  LOG(ERROR)
+      << "Unable to find the top level web content for retrieving the web "
+         "app manifest of a payment handler for \""
+      << context_url << "\".";
+
   RunCallbackAndDestroy();
 }
 
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 8eb19b1..f427fc90 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -50,8 +50,14 @@
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   factory->GetContextFactory()->AddObserver(this);
   DCHECK(host_frame_sink_manager_);
+  viz::ReportFirstSurfaceActivation should_report_first_surface_activation =
+      viz::ReportFirstSurfaceActivation::kNo;
+#ifdef CHROME_OS
+  should_report_first_surface_activation =
+      viz::ReportFirstSurfaceActivation::kYes;
+#endif
   host_frame_sink_manager_->RegisterFrameSinkId(
-      frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
+      frame_sink_id_, this, should_report_first_surface_activation);
   host_frame_sink_manager_->EnableSynchronizationReporting(
       frame_sink_id_, "Compositing.MainFrameSynchronization.Duration");
   host_frame_sink_manager_->SetFrameSinkDebugLabel(frame_sink_id_,
@@ -222,7 +228,12 @@
     return;
   }
 
+#ifdef OS_CHROMEOS
+  if (seen_first_activation_)
+    frame_evictor_->OnNewSurfaceEmbedded();
+#else
   frame_evictor_->OnNewSurfaceEmbedded();
+#endif
 
   if (!primary_surface_id ||
       primary_surface_id->local_surface_id() != local_surface_id_) {
@@ -295,7 +306,13 @@
 
 void DelegatedFrameHost::OnFirstSurfaceActivation(
     const viz::SurfaceInfo& surface_info) {
+#ifdef OS_CHROMEOS
+  if (!seen_first_activation_)
+    frame_evictor_->OnNewSurfaceEmbedded();
+  seen_first_activation_ = true;
+#else
   NOTREACHED();
+#endif
 }
 
 void DelegatedFrameHost::OnFrameTokenChanged(uint32_t frame_token) {
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index 95fb1451..6656a1a 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -222,6 +222,10 @@
 
   viz::LocalSurfaceId first_local_surface_id_after_navigation_;
 
+#ifdef OS_CHROMEOS
+  bool seen_first_activation_ = false;
+#endif
+
   base::WeakPtrFactory<DelegatedFrameHost> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DelegatedFrameHost);
diff --git a/content/browser/renderer_host/input/gesture_event_queue.cc b/content/browser/renderer_host/input/gesture_event_queue.cc
index 4526e58ad..e2fecd20 100644
--- a/content/browser/renderer_host/input/gesture_event_queue.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue.cc
@@ -42,7 +42,7 @@
 
 GestureEventQueue::~GestureEventQueue() { }
 
-bool GestureEventQueue::DebounceOrQueueEvent(
+bool GestureEventQueue::DebounceOrForwardEvent(
     const GestureEventWithLatencyInfo& gesture_event) {
   // GFS and GFC should have been filtered in FlingControllerFilterEvent.
   DCHECK_NE(gesture_event.event.GetType(), WebInputEvent::kGestureFlingStart);
@@ -50,7 +50,7 @@
   if (!ShouldForwardForBounceReduction(gesture_event))
     return false;
 
-  QueueAndForwardIfNecessary(gesture_event);
+  ForwardGestureEvent(gesture_event);
   return true;
 }
 
@@ -136,7 +136,6 @@
     case WebInputEvent::kGesturePinchBegin:
     case WebInputEvent::kGesturePinchEnd:
     case WebInputEvent::kGesturePinchUpdate:
-      // TODO(rjkroege): Debounce pinch (http://crbug.com/147647)
       return true;
     default:
       if (scrolling_in_progress_) {
@@ -147,13 +146,13 @@
   }
 }
 
-void GestureEventQueue::QueueAndForwardIfNecessary(
+void GestureEventQueue::ForwardGestureEvent(
     const GestureEventWithLatencyInfo& gesture_event) {
   // GFS and GFC should have been filtered in FlingControllerFilterEvent to
   // get handled by fling controller.
   DCHECK_NE(gesture_event.event.GetType(), WebInputEvent::kGestureFlingStart);
   DCHECK_NE(gesture_event.event.GetType(), WebInputEvent::kGestureFlingCancel);
-  coalesced_gesture_events_.push_back(gesture_event);
+  sent_events_awaiting_ack_.push_back(gesture_event);
   if (gesture_event.event.GetType() == WebInputEvent::kGestureScrollBegin) {
     fling_controller_.RegisterFlingSchedulerObserver();
   } else if (gesture_event.event.GetType() ==
@@ -161,7 +160,6 @@
     fling_controller_.UnregisterFlingSchedulerObserver();
   }
   client_->SendGestureEventImmediately(gesture_event);
-  return;
 }
 
 void GestureEventQueue::ProcessGestureAck(InputEventAckSource ack_source,
@@ -170,14 +168,14 @@
                                           const ui::LatencyInfo& latency) {
   TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck");
 
-  if (coalesced_gesture_events_.empty()) {
+  if (sent_events_awaiting_ack_.empty()) {
     DLOG(ERROR) << "Received unexpected ACK for event type " << type;
     return;
   }
 
   // ACKs could come back out of order. We want to cache them to restore the
   // original order.
-  for (auto& outstanding_event : coalesced_gesture_events_) {
+  for (auto& outstanding_event : sent_events_awaiting_ack_) {
     if (outstanding_event.ack_state() != INPUT_EVENT_ACK_STATE_UNKNOWN)
       continue;
     if (outstanding_event.event.GetType() == type) {
@@ -196,12 +194,12 @@
   if (processing_acks_)
     return;
   base::AutoReset<bool> process_acks(&processing_acks_, true);
-  while (!coalesced_gesture_events_.empty()) {
-    auto iter = coalesced_gesture_events_.begin();
+  while (!sent_events_awaiting_ack_.empty()) {
+    auto iter = sent_events_awaiting_ack_.begin();
     if (iter->ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN)
       break;
     GestureEventWithLatencyInfoAndAckState event = *iter;
-    coalesced_gesture_events_.erase(iter);
+    sent_events_awaiting_ack_.erase(iter);
     AckGestureEventToClient(event, event.ack_source(), event.ack_state());
   }
 }
@@ -210,9 +208,6 @@
     const GestureEventWithLatencyInfo& event_with_latency,
     InputEventAckSource ack_source,
     InputEventAckState ack_result) {
-  // Ack'ing an event may enqueue additional gesture events.  By ack'ing the
-  // event before the forwarding of queued events below, such additional events
-  // can be coalesced with existing queued events prior to dispatch.
   client_->OnGestureEventAck(event_with_latency, ack_source, ack_result);
 }
 
@@ -221,11 +216,6 @@
   return fling_controller_.GetTouchpadTapSuppressionController();
 }
 
-void GestureEventQueue::ForwardGestureEvent(
-    const GestureEventWithLatencyInfo& gesture_event) {
-  QueueAndForwardIfNecessary(gesture_event);
-}
-
 void GestureEventQueue::SendScrollEndingEventsNow() {
   scrolling_in_progress_ = false;
   if (debouncing_deferral_queue_.empty())
@@ -235,7 +225,7 @@
   for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin();
        it != debouncing_deferral_queue.end(); it++) {
     if (!fling_controller_.FilterGestureEvent(*it)) {
-      QueueAndForwardIfNecessary(*it);
+      ForwardGestureEvent(*it);
     }
   }
 }
diff --git a/content/browser/renderer_host/input/gesture_event_queue.h b/content/browser/renderer_host/input/gesture_event_queue.h
index 3e1ae8c0..a357354a 100644
--- a/content/browser/renderer_host/input/gesture_event_queue.h
+++ b/content/browser/renderer_host/input/gesture_event_queue.h
@@ -37,26 +37,28 @@
                                  InputEventAckState ack_result) = 0;
 };
 
-// Maintains WebGestureEvents in a queue before forwarding them to the renderer
-// to apply a sequence of filters on them:
+// Despite its name, this class isn't so much one queue as it is a collection
+// of queues and filters. This class applies logic to determine if an event
+// should be queued, filtered altogether, or sent immediately; it tracks sent
+// events and ACKs them to the clilent in the order they were dispatched. This
+// class applies a series of filters and queues for various scenarios:
 // 1. The sequence is filtered for bounces. A bounce is when the finger lifts
-//    from the screen briefly during an in-progress scroll. Ifco this happens,
+//    from the screen briefly during an in-progress scroll. If this happens,
 //    non-GestureScrollUpdate events are queued until the de-bounce interval
 //    passes or another GestureScrollUpdate event occurs.
 // 2. Unnecessary GestureFlingCancel events are filtered by fling controller.
 //    These are GestureFlingCancels that have no corresponding GestureFlingStart
-//    in the queue.
+//    in the queue. GestureFlingStarts are also filtered and translated to
+//    scroll gestures by the fling controller.
 // 3. Taps immediately after a GestureFlingCancel (caused by the same tap) are
 //    filtered by fling controller.
-// 4. Whenever possible, events in the queue are coalesced to have as few events
-//    as possible and therefore maximize the chance that the event stream can be
-//    handled entirely by the compositor thread.
-// Events in the queue are forwarded to the renderer one by one; i.e., each
-// event is sent after receiving the ACK for previous one. The only exception is
-// that if a GestureScrollUpdate is followed by a GesturePinchUpdate, they are
-// sent together.
-// TODO(rjkroege): Possibly refactor into a filter chain:
-// http://crbug.com/148443.
+// 4. Gesture events are queued while we're waiting to determine the allowed
+//    touch actions.
+// Sent events are kept in a queue until a response from the renderer is
+// received for that event. The client is notified of ACKs in the order the
+// events were sent, not ACK'd. This means an ACK'd event that was sent after
+// an event still awaiting an ACK won't notify the client until the earlier
+// event is ACK'd.
 class CONTENT_EXPORT GestureEventQueue {
  public:
   using GestureQueue = base::circular_deque<GestureEventWithLatencyInfo>;
@@ -79,24 +81,23 @@
   ~GestureEventQueue();
 
   // Uses fling controller to filter the gesture event. Returns true if the
-  // event wasn't queued and was filtered.
+  // event was filtered by the fling controller and shouldn't be further
+  // forwarded.
   bool FlingControllerFilterEvent(const GestureEventWithLatencyInfo&);
 
-  // Check for debouncing, or add the gesture event to the queue. Returns false
-  // if the event wasn't queued.
-  bool DebounceOrQueueEvent(const GestureEventWithLatencyInfo&);
+  // Filter the event for debouncing or forward it to the renderer. Returns
+  // true if the event was forwarded, false if was filtered for debouncing.
+  bool DebounceOrForwardEvent(const GestureEventWithLatencyInfo&);
 
   // Adds a gesture to the queue of events that needs to be deferred until the
   // touch action is known.
   void QueueDeferredEvents(const GestureEventWithLatencyInfo&);
 
-  // Returns events in the |deferred_gesture_queue_| and empty
-  // the queue.
+  // Returns events in the |deferred_gesture_queue_| and empty the queue.
   GestureQueue TakeDeferredEvents();
 
   // Indicates that the caller has received an acknowledgement from the renderer
-  // with state |ack_result| and event |type|. May send events if the queue is
-  // not empty.
+  // with state |ack_result| and event |type|.
   void ProcessGestureAck(InputEventAckSource ack_source,
                          InputEventAckState ack_result,
                          blink::WebInputEvent::Type type,
@@ -105,10 +106,12 @@
   // Returns the |TouchpadTapSuppressionController| instance.
   TouchpadTapSuppressionController* GetTouchpadTapSuppressionController();
 
+  // Sends the gesture event to the renderer. Stores the sent event for when
+  // the renderer replies with an ACK.
   void ForwardGestureEvent(const GestureEventWithLatencyInfo& gesture_event);
 
   bool empty() const {
-    return coalesced_gesture_events_.empty() &&
+    return sent_events_awaiting_ack_.empty() &&
            debouncing_deferral_queue_.empty();
   }
 
@@ -151,12 +154,6 @@
   bool ShouldForwardForBounceReduction(
       const GestureEventWithLatencyInfo& gesture_event);
 
-  // Puts the events in a queue to forward them one by one; i.e., forward them
-  // whenever ACK for previous event is received. This queue also tries to
-  // coalesce events as much as possible.
-  void QueueAndForwardIfNecessary(
-      const GestureEventWithLatencyInfo& gesture_event);
-
   // ACK completed events in order until we have reached an incomplete event.
   // Will preserve the FIFO order as events originally arrived.
   void AckCompletedEvents();
@@ -178,8 +175,12 @@
       base::circular_deque<GestureEventWithLatencyInfoAndAckState>;
 
   // Stores outstanding events that have been sent to the renderer but not yet
-  // been ACKed.
-  GestureQueueWithAckState coalesced_gesture_events_;
+  // been ACK'd. These are kept in the order they were sent in so that they can
+  // be ACK'd back in order. Note, the renderer can reply to these out-of-order.
+  // This class makes a note of the ACK state but doesn't actually let the
+  // client know about the ACK until all events earlier in the queue have been
+  // ACK'd so that the client sees the ACKs in order.
+  GestureQueueWithAckState sent_events_awaiting_ack_;
 
   // Timer to release a previously deferred gesture event.
   base::OneShotTimer debounce_deferring_timer_;
diff --git a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
index e43ce45..fb2da96 100644
--- a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
@@ -119,7 +119,7 @@
   void SimulateGestureEvent(const WebGestureEvent& gesture) {
     GestureEventWithLatencyInfo gesture_event(gesture);
     if (!queue()->FlingControllerFilterEvent(gesture_event)) {
-      queue()->DebounceOrQueueEvent(gesture_event);
+      queue()->DebounceOrForwardEvent(gesture_event);
     }
   }
 
@@ -191,16 +191,17 @@
   }
 
   unsigned GestureEventQueueSize() {
-    return queue()->coalesced_gesture_events_.size();
+    return queue()->sent_events_awaiting_ack_.size();
   }
 
   WebGestureEvent GestureEventSecondFromLastQueueEvent() {
-    return queue()->coalesced_gesture_events_.at(
-        GestureEventQueueSize() - 2).event;
+    return queue()
+        ->sent_events_awaiting_ack_.at(GestureEventQueueSize() - 2)
+        .event;
   }
 
   WebGestureEvent GestureEventLastQueueEvent() {
-    return queue()->coalesced_gesture_events_.back().event;
+    return queue()->sent_events_awaiting_ack_.back().event;
   }
 
   unsigned GestureEventDebouncingQueueSize() {
@@ -208,7 +209,7 @@
   }
 
   WebGestureEvent GestureEventQueueEventAt(int i) {
-    return queue()->coalesced_gesture_events_.at(i).event;
+    return queue()->sent_events_awaiting_ack_.at(i).event;
   }
 
   bool ScrollingInProgress() {
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index 6c5e338..77d4db9e5 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -194,7 +194,7 @@
     return;
   }
 
-  if (!gesture_event_queue_.DebounceOrQueueEvent(gesture_event)) {
+  if (!gesture_event_queue_.DebounceOrForwardEvent(gesture_event)) {
     disposition_handler_->OnGestureEventAck(gesture_event,
                                             InputEventAckSource::BROWSER,
                                             INPUT_EVENT_ACK_STATE_CONSUMED);
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
index 310d8020..d2ef353 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
@@ -299,12 +299,22 @@
       std::make_unique<viz::HitTestQuery>();
 
   std::vector<viz::AggregatedHitTestRegion> hit_test_data;
+  uint32_t async_hit_test_reasons_root =
+      (view_root_flags & viz::HitTestRegionFlags::kHitTestAsk)
+          ? viz::AsyncHitTestReasons::kIrregularClip
+          : viz::AsyncHitTestReasons::kNotAsyncHitTest;
+  uint32_t async_hit_test_reasons_other =
+      (view_other_flags & viz::HitTestRegionFlags::kHitTestAsk)
+          ? viz::AsyncHitTestReasons::kIrregularClip
+          : viz::AsyncHitTestReasons::kNotAsyncHitTest;
   hit_test_data.push_back(viz::AggregatedHitTestRegion(
       view_root_->GetFrameSinkId(), view_root_flags,
-      view_root_->GetViewBounds(), gfx::Transform(), 1));
+      view_root_->GetViewBounds(), gfx::Transform(), 1,
+      async_hit_test_reasons_root));
   hit_test_data.push_back(viz::AggregatedHitTestRegion(
       view_other_->GetFrameSinkId(), view_other_flags,
-      view_other_->GetViewBounds(), gfx::Transform(), 0));
+      view_other_->GetViewBounds(), gfx::Transform(), 0,
+      async_hit_test_reasons_other));
   hit_test_map[view_root_->GetFrameSinkId()]
       ->OnAggregatedHitTestRegionListUpdated(hit_test_data);
 
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 065b42b..6873f27c 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
@@ -3502,6 +3502,13 @@
         views[i]->GetNativeView(),
         parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect());
     views[i]->SetSize(view_rect.size());
+#ifdef OS_CHROMEOS
+    viz::SurfaceId surface_id(
+        views[i]->GetFrameSinkId(),
+        views[i]->GetLocalSurfaceIdAllocation().local_surface_id());
+    views[i]->delegated_frame_host_->OnFirstSurfaceActivation(
+        viz::SurfaceInfo(surface_id, 1.f, view_rect.size()));
+#endif
     EXPECT_HAS_FRAME(views[i]);
   }
 
@@ -3618,6 +3625,13 @@
         gfx::Rect());
     views[i]->SetSize(view_rect.size());
     views[i]->Show();
+#ifdef OS_CHROMEOS
+    viz::SurfaceId surface_id(
+        views[i]->GetFrameSinkId(),
+        views[i]->GetLocalSurfaceIdAllocation().local_surface_id());
+    views[i]->delegated_frame_host_->OnFirstSurfaceActivation(
+        viz::SurfaceInfo(surface_id, 1.f, view_rect.size()));
+#endif
     EXPECT_HAS_FRAME(views[i]);
   }
 
@@ -5817,6 +5831,13 @@
   viz::LocalSurfaceId id1 =
       view_->GetLocalSurfaceIdAllocation().local_surface_id();
   view_->Hide();
+#ifdef OS_CHROMEOS
+  viz::SurfaceId surface_id(
+      view_->GetFrameSinkId(),
+      view_->GetLocalSurfaceIdAllocation().local_surface_id());
+  view_->delegated_frame_host_->OnFirstSurfaceActivation(
+      viz::SurfaceInfo(surface_id, 1.f, gfx::Size(10, 10)));
+#endif
   static_cast<viz::FrameEvictorClient*>(view_->delegated_frame_host_.get())
       ->EvictDelegatedFrame();
   view_->Show();
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index 24370ce..801120a 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -46,17 +46,18 @@
   return gfx::PointF();
 }
 
+constexpr const char kTracingCategory[] = "input,latency";
+
 }  // namespace
 
 class TracingUmaTracker {
  public:
-  TracingUmaTracker(const char* metric_name, const char* tracing_category)
+  explicit TracingUmaTracker(const char* metric_name)
       : id_(next_id_++),
         start_time_(base::TimeTicks::Now()),
-        metric_name_(metric_name),
-        tracing_category_(tracing_category) {
+        metric_name_(metric_name) {
     TRACE_EVENT_ASYNC_BEGIN0(
-        tracing_category_, metric_name_,
+        kTracingCategory, metric_name_,
         TRACE_ID_WITH_SCOPE(metric_name_, TRACE_ID_LOCAL(id_)));
   }
   ~TracingUmaTracker() = default;
@@ -69,7 +70,7 @@
 
   void Stop() {
     TRACE_EVENT_ASYNC_END0(
-        tracing_category_, metric_name_,
+        kTracingCategory, metric_name_,
         TRACE_ID_WITH_SCOPE(metric_name_, TRACE_ID_LOCAL(id_)));
   }
 
@@ -80,7 +81,6 @@
   // These variables must be string literals and live for the duration
   // of the program since tracing stores pointers.
   const char* metric_name_;
-  const char* tracing_category_;
 
   static int next_id_;
 
@@ -157,8 +157,8 @@
     request.root_view = root_view->GetWeakPtr();
     request.event = ui::WebInputEventTraits::Clone(event);
     request.latency = latency;
-    request.tracker = std::make_unique<TracingUmaTracker>(
-        "Event.AsyncTargeting.TimeInQueue", "input,latency");
+    request.tracker =
+        std::make_unique<TracingUmaTracker>("Event.AsyncTargeting.TimeInQueue");
     requests_.push(std::move(request));
     return;
   }
@@ -235,8 +235,7 @@
     request_in_flight_ = true;
     async_depth_++;
   }
-  TracingUmaTracker tracker("Event.AsyncTargeting.ResponseTime",
-                            "input,latency");
+  TracingUmaTracker tracker("Event.AsyncTargeting.ResponseTime");
   auto& hit_test_timeout =
       is_verifying ? async_verify_hit_test_timeout_ : async_hit_test_timeout_;
   hit_test_timeout.reset(new OneShotTimeoutMonitor(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index eafffaaa..92eae27 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -479,6 +479,7 @@
   blink::ParsedFeaturePolicy result(1);
   result[0].feature = feature;
   result[0].matches_all_origins = false;
+  result[0].disposition = blink::mojom::FeaturePolicyDisposition::kEnforce;
   DCHECK(!origins.empty());
   for (const GURL& origin : origins)
     result[0].origins.push_back(url::Origin::Create(origin));
@@ -492,6 +493,7 @@
   blink::ParsedFeaturePolicy result(1);
   result[0].feature = feature;
   result[0].matches_all_origins = true;
+  result[0].disposition = blink::mojom::FeaturePolicyDisposition::kEnforce;
   return result;
 }
 
diff --git a/content/browser/tracing/file_tracing_provider_impl.cc b/content/browser/tracing/file_tracing_provider_impl.cc
index b9dfa4e..e960df4 100644
--- a/content/browser/tracing/file_tracing_provider_impl.cc
+++ b/content/browser/tracing/file_tracing_provider_impl.cc
@@ -9,7 +9,8 @@
 
 namespace content {
 
-const char kFileTracingEventCategoryGroup[] = TRACE_DISABLED_BY_DEFAULT("file");
+constexpr const char kFileTracingEventCategoryGroup[] =
+    TRACE_DISABLED_BY_DEFAULT("file");
 
 FileTracingProviderImpl::FileTracingProviderImpl() {}
 FileTracingProviderImpl::~FileTracingProviderImpl() {}
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index b23fcca1..8984848f 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -153,6 +153,8 @@
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(content::NavigationDownloadPolicy,
                               content::NavigationDownloadPolicy::kAllow,
                               content::NavigationDownloadPolicy::kMaxValue)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::FeaturePolicyDisposition,
+                          blink::mojom::FeaturePolicyDisposition::kMaxValue)
 
 IPC_STRUCT_TRAITS_BEGIN(blink::WebFloatSize)
   IPC_STRUCT_TRAITS_MEMBER(width)
@@ -534,6 +536,7 @@
   IPC_STRUCT_TRAITS_MEMBER(feature)
   IPC_STRUCT_TRAITS_MEMBER(matches_all_origins)
   IPC_STRUCT_TRAITS_MEMBER(matches_opaque_src)
+  IPC_STRUCT_TRAITS_MEMBER(disposition)
   IPC_STRUCT_TRAITS_MEMBER(origins)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index f4b8f5aa..80febce 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -152,6 +152,7 @@
     "java/src/org/chromium/content/browser/accessibility/LollipopWebContentsAccessibility.java",
     "java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java",
     "java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java",
+    "java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java",
     "java/src/org/chromium/content/browser/accessibility/captioning/CaptioningBridgeFactory.java",
     "java/src/org/chromium/content/browser/accessibility/captioning/CaptioningChangeDelegate.java",
     "java/src/org/chromium/content/browser/accessibility/captioning/CaptioningController.java",
@@ -382,6 +383,7 @@
     "java/src/org/chromium/content/browser/SpeechRecognitionImpl.java",
     "java/src/org/chromium/content/browser/SyntheticGestureTarget.java",
     "java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java",
+    "java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java",
     "java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java",
     "java/src/org/chromium/content/browser/accessibility/captioning/CaptioningController.java",
     "java/src/org/chromium/content/browser/androidoverlay/AndroidOverlayProviderImpl.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java
new file mode 100644
index 0000000..686ea43
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java
@@ -0,0 +1,33 @@
+// 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.content.browser.accessibility;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.provider.Settings;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.metrics.RecordHistogram;
+
+/**
+ * Provides utility methods relating to measuring accessibility state on the current platform (i.e.
+ * Android in this case). See content::BrowserAccessibilityStateImpl.
+ */
+@JNINamespace("content")
+public class BrowserAccessibilityState {
+    @CalledByNative
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    private static void recordAccessibilityHistograms() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return;
+
+        float durationScale =
+                Settings.Global.getFloat(ContextUtils.getApplicationContext().getContentResolver(),
+                        Settings.Global.ANIMATOR_DURATION_SCALE, 0);
+        RecordHistogram.recordBooleanHistogram(
+                "Accessibility.Android.AnimationsEnabled", durationScale != 0.0);
+    }
+}
diff --git a/content/public/browser/background_fetch_delegate.h b/content/public/browser/background_fetch_delegate.h
index 46f2e24..123d0db 100644
--- a/content/public/browser/background_fetch_delegate.h
+++ b/content/public/browser/background_fetch_delegate.h
@@ -137,6 +137,9 @@
   // Aborts any downloads associated with |job_unique_id|.
   virtual void Abort(const std::string& job_unique_id) = 0;
 
+  // Called after the fetch has completed so that the delegate can clean up.
+  virtual void MarkJobComplete(const std::string& job_unique_id) = 0;
+
   // Updates the UI shown for the fetch job associated with |job_unique_id| to
   // display a new |title| or |icon|.
   virtual void UpdateUI(const std::string& job_unique_id,
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 13771ff..e1c9fc1 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -725,13 +725,6 @@
 
   gfx::ColorSpace GetRasterColorSpace() const;
 
-  void SendInputEventAck(blink::WebInputEvent::Type type,
-                         uint32_t touch_event_id,
-                         InputEventAckState ack_state,
-                         const ui::LatencyInfo& latency_info,
-                         std::unique_ptr<ui::DidOverscrollParams>,
-                         base::Optional<cc::TouchAction>);
-
   void UpdateZoom(double zoom_level);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/content/shell/browser/layout_test/layout_test_background_fetch_delegate.cc b/content/shell/browser/layout_test/layout_test_background_fetch_delegate.cc
index 3846d18..661b879d 100644
--- a/content/shell/browser/layout_test/layout_test_background_fetch_delegate.cc
+++ b/content/shell/browser/layout_test/layout_test_background_fetch_delegate.cc
@@ -252,6 +252,9 @@
   // TODO(peter): Implement the ability to abort the |job_unique_id|.
 }
 
+void LayoutTestBackgroundFetchDelegate::MarkJobComplete(
+    const std::string& job_unique_id) {}
+
 void LayoutTestBackgroundFetchDelegate::UpdateUI(
     const std::string& job_unique_id,
     const base::Optional<std::string>& title,
diff --git a/content/shell/browser/layout_test/layout_test_background_fetch_delegate.h b/content/shell/browser/layout_test/layout_test_background_fetch_delegate.h
index 6ed853f..26b5050 100644
--- a/content/shell/browser/layout_test/layout_test_background_fetch_delegate.h
+++ b/content/shell/browser/layout_test/layout_test_background_fetch_delegate.h
@@ -40,6 +40,7 @@
                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
                    const net::HttpRequestHeaders& headers) override;
   void Abort(const std::string& job_unique_id) override;
+  void MarkJobComplete(const std::string& job_unique_id) override;
   void UpdateUI(const std::string& job_unique_id,
                 const base::Optional<std::string>& title,
                 const base::Optional<SkBitmap>& icon) override;
diff --git a/content/shell/test_runner/test_interfaces.cc b/content/shell/test_runner/test_interfaces.cc
index 0eceef1..759901b 100644
--- a/content/shell/test_runner/test_interfaces.cc
+++ b/content/shell/test_runner/test_interfaces.cc
@@ -32,7 +32,7 @@
   // progress here in a per-directory manner.
   // TODO(xiaochengh): Progressively allow more tests to use innerText.
   // Remove this function once rebaseline is complete.
-  return test_path >= "LayoutTests/a" && test_path < "LayoutTests/h";
+  return test_path >= "LayoutTests/a" && test_path < "LayoutTests/p";
 }
 }  // namespace
 
diff --git a/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt b/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt
index adbf886..a655f37 100644
--- a/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt
+++ b/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt
@@ -10,11 +10,11 @@
 ++++paragraph
 ++++++staticText name='x=hidden'
 ++++++++inlineTextBox name='x=hidden'
-++genericContainer scrollXMin=0
+++genericContainer name='x=auto' scrollXMin=0
 ++++paragraph
 ++++++staticText name='x=auto'
 ++++++++inlineTextBox name='x=auto'
-++genericContainer scrollXMin=0
+++genericContainer name='x=scroll' scrollXMin=0
 ++++paragraph
 ++++++staticText name='x=scroll'
 ++++++++inlineTextBox name='x=scroll'
@@ -25,11 +25,11 @@
 ++++paragraph
 ++++++staticText name='y=hidden'
 ++++++++inlineTextBox name='y=hidden'
-++genericContainer scrollXMin=0
+++genericContainer name='y=auto' scrollXMin=0
 ++++paragraph
 ++++++staticText name='y=auto'
 ++++++++inlineTextBox name='y=auto'
-++genericContainer scrollXMin=0
+++genericContainer name='y=scroll' scrollXMin=0
 ++++paragraph
 ++++++staticText name='y=scroll'
 ++++++++inlineTextBox name='y=scroll'
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index c5628b1..e75fbfd9 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -272,6 +272,7 @@
   blink::ParsedFeaturePolicy header(1);
   header[0].feature = feature;
   header[0].matches_all_origins = false;
+  header[0].disposition = blink::mojom::FeaturePolicyDisposition::kEnforce;
   header[0].origins = whitelist;
   DidSetFramePolicyHeaders(blink::WebSandboxFlags::kNone, header);
 }
diff --git a/device/fido/ble/fido_ble_device.cc b/device/fido/ble/fido_ble_device.cc
index 819dfe3..da6a3a1 100644
--- a/device/fido/ble/fido_ble_device.cc
+++ b/device/fido/ble/fido_ble_device.cc
@@ -21,7 +21,7 @@
   connection_ = std::make_unique<FidoBleConnection>(
       adapter, std::move(address),
       base::BindRepeating(&FidoBleDevice::OnStatusMessage,
-                          base::Unretained(this)));
+                          weak_factory_.GetWeakPtr()));
 }
 
 FidoBleDevice::FidoBleDevice(std::unique_ptr<FidoBleConnection> connection)
@@ -36,7 +36,7 @@
   StartTimeout();
   state_ = State::kBusy;
   connection_->Connect(
-      base::BindOnce(&FidoBleDevice::OnConnected, base::Unretained(this)));
+      base::BindOnce(&FidoBleDevice::OnConnected, weak_factory_.GetWeakPtr()));
 }
 
 void FidoBleDevice::SendPing(std::vector<uint8_t> data,
@@ -118,7 +118,7 @@
 
 FidoBleConnection::ReadCallback FidoBleDevice::GetReadCallbackForTesting() {
   return base::BindRepeating(&FidoBleDevice::OnStatusMessage,
-                             base::Unretained(this));
+                             weak_factory_.GetWeakPtr());
 }
 
 void FidoBleDevice::DeviceTransact(std::vector<uint8_t> command,
@@ -156,8 +156,9 @@
     case State::kConnected:
       StartTimeout();
       state_ = State::kBusy;
-      connection_->ReadControlPointLength(base::BindOnce(
-          &FidoBleDevice::OnReadControlPointLength, base::Unretained(this)));
+      connection_->ReadControlPointLength(
+          base::BindOnce(&FidoBleDevice::OnReadControlPointLength,
+                         weak_factory_.GetWeakPtr()));
       break;
     case State::kReady:
       if (!pending_frames_.empty()) {
@@ -223,8 +224,8 @@
   transaction_.emplace(connection_.get(), control_point_length_);
   transaction_->WriteRequestFrame(
       std::move(frame),
-      base::BindOnce(&FidoBleDevice::OnResponseFrame, base::Unretained(this),
-                     std::move(callback)));
+      base::BindOnce(&FidoBleDevice::OnResponseFrame,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void FidoBleDevice::StartTimeout() {
diff --git a/docs/security/sheriff.md b/docs/security/sheriff.md
index 8842751..c2e0517 100644
--- a/docs/security/sheriff.md
+++ b/docs/security/sheriff.md
@@ -157,7 +157,7 @@
 
 Tips for reproducing bugs:
 
-* [https://clusterfuzz.com/v2/upload-testcase](https://clusterfuzz.com/v2/upload-testcase)
+* [https://clusterfuzz.com/upload-testcase](https://clusterfuzz.com/upload-testcase)
 allows you to upload files to reproduce crashes on various platforms and will
 identify revision ranges when the regression was introduced. If a test case
 requires multiple files, they can be uploaded together in a zip or tar archive.
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index cdb955b..5729dff 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -280,6 +280,14 @@
 }
 
 builder_mixins {
+  name: "dawn-try"
+  service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+  recipe {
+    properties: "mastername:tryserver.chromium.dawn"
+  }
+}
+
+builder_mixins {
   name: "fuzz-ci"
   recipe {
     properties: "mastername:chromium.fuzz"
@@ -556,6 +564,12 @@
 }
 
 builder_mixins {
+  name: "win-dawn-try"
+  mixins: "win"
+  mixins: "dawn-try"
+}
+
+builder_mixins {
   name: "win-gpu-ci"
   mixins: "win"
   recipe {
@@ -3231,6 +3245,7 @@
       execution_timeout_secs: 14400 # 4h
     }
     builders { mixins: "win-try" name: "win_chromium_x64_rel_ng" }
+    builders { mixins: "win-dawn-try" name: "win-dawn-rel" }
     builders { mixins: "win-try" name: "win_mojo" }
     builders { mixins: "win-optional-gpu-try" name: "win_optional_gpu_tests_rel" }
     builders { mixins: "win-try" mixins: "upload_clang" name: "win_upload_clang" }
diff --git a/ios/chrome/app/application_delegate/url_opener_unittest.mm b/ios/chrome/app/application_delegate/url_opener_unittest.mm
index 95c8c778..69724d0c 100644
--- a/ios/chrome/app/application_delegate/url_opener_unittest.mm
+++ b/ios/chrome/app/application_delegate/url_opener_unittest.mm
@@ -7,19 +7,14 @@
 #import <Foundation/Foundation.h>
 
 #include "ios/chrome/app/application_delegate/app_state.h"
-#include "ios/chrome/app/application_delegate/app_state_testing.h"
 #include "ios/chrome/app/application_delegate/mock_tab_opener.h"
-#include "ios/chrome/app/main_application_delegate.h"
 #include "ios/chrome/app/main_controller.h"
-#include "ios/chrome/app/main_controller_private.h"
+#import "ios/chrome/app/main_controller_private.h"
 #include "ios/chrome/app/startup/chrome_app_startup_parameters.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/tabs/tab.h"
-#import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/test/base/scoped_block_swizzler.h"
-#import "ios/testing/ocmock_complex_type_helper.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
-#import "net/base/mac/url_conversions.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -29,116 +24,6 @@
 #error "This file requires ARC support."
 #endif
 
-#pragma mark - Tab Switcher Mock
-
-// This mocks either a iPad tab switcher controller or a iPhone stack view
-// controller.
-@interface URLOpenerOCMockComplexTypeHandler : OCMockComplexTypeHelper
-@end
-
-@implementation URLOpenerOCMockComplexTypeHandler
-
-typedef Tab* (^mock_gurl_nsuinteger_pagetransition)(const GURL&,
-                                                    NSUInteger,
-                                                    ui::PageTransition);
-
-- (Tab*)dismissWithNewTabAnimationToModel:(TabModel*)targetModel
-                                  withURL:(const GURL&)url
-                                  atIndex:(NSUInteger)position
-                               transition:(ui::PageTransition)transition {
-  static_cast<mock_gurl_nsuinteger_pagetransition>(
-      [self blockForSelector:_cmd])(url, position, transition);
-  id mockTab = [OCMockObject mockForClass:[Tab class]];
-  return mockTab;
-}
-
-- (Tab*)addSelectedTabWithURL:(const GURL&)url
-                      atIndex:(NSUInteger)position
-                   transition:(ui::PageTransition)transition {
-  static_cast<mock_gurl_nsuinteger_pagetransition>(
-      [self blockForSelector:_cmd])(url, position, transition);
-  id mockTab = [OCMockObject mockForClass:[Tab class]];
-  return mockTab;
-}
-
-@end
-
-#pragma mark - BrowserViewController Mock
-
-// Mock BVC class to use for test cases where OCMock gets handled incorrectly
-// by UIViewController.
-@interface URLOpenerMockBVC : UIViewController
-@property(nonatomic, assign) ios::ChromeBrowserState* browserState;
-@property(nonatomic, assign) GURL tabURL;
-@property(nonatomic, assign) NSUInteger position;
-@property(nonatomic, assign) ui::PageTransition transition;
-@property(nonatomic, assign)
-    ProceduralBlock foregroundTabWasAddedCompletionBlock;
-
-- (Tab*)addSelectedTabWithURL:(const GURL&)url
-                      atIndex:(NSUInteger)position
-                   transition:(ui::PageTransition)transition;
-- (void)expectNewForegroundTab;
-- (void)setActive:(BOOL)active;
-- (TabModel*)tabModel;
-- (void)appendTabAddedCompletion:(ProceduralBlock)completion;
-- (void)browserStateDestroyed;
-- (void)shutdown;
-@end
-
-@implementation URLOpenerMockBVC
-@synthesize browserState = _browserState;
-@synthesize tabURL = _tabURL;
-@synthesize position = _position;
-@synthesize transition = _transition;
-@synthesize foregroundTabWasAddedCompletionBlock =
-    _foregroundTabWasAddedCompletionBlock;
-
-- (Tab*)addSelectedTabWithURL:(const GURL&)url
-                      atIndex:(NSUInteger)position
-                   transition:(ui::PageTransition)transition {
-  self.tabURL = url;
-  self.position = position;
-  self.transition = transition;
-  return nil;
-}
-
-- (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
-                           dismissOmnibox:(BOOL)dismissOmnibox {
-  if (completion)
-    completion();
-}
-
-- (void)expectNewForegroundTab {
-  // no-op.
-}
-
-- (void)setActive:(BOOL)active {
-  // no-op
-}
-
-- (void)setPrimary:(BOOL)primary {
-  // no-op
-}
-
-- (TabModel*)tabModel {
-  return nil;
-}
-
-- (void)appendTabAddedCompletion:(ProceduralBlock)completion {
-  self.foregroundTabWasAddedCompletionBlock = completion;
-}
-
-- (void)browserStateDestroyed {
-  // no-op
-}
-
-- (void)shutdown {
-  // no-op
-}
-
-@end
-
 class URLOpenerTest : public PlatformTest {
  protected:
   void TearDown() override {
@@ -183,86 +68,6 @@
   MainController* main_controller_;
 };
 
-TEST_F(URLOpenerTest, HandleOpenURLWithNoOpenTab) {
-  // The tab switcher controller should be dismissed with a new tab containing
-  // the external URL.
-  NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
-  ChromeAppStartupParameters* params =
-      [ChromeAppStartupParameters newChromeAppStartupParametersWithURL:url
-                                                 fromSourceApplication:nil];
-
-  id bvcMock = [[URLOpenerMockBVC alloc] init];
-
-  id tabSwitcherController;
-  tabSwitcherController = [[URLOpenerOCMockComplexTypeHandler alloc]
-      initWithRepresentedObject:[OCMockObject
-                                    mockForProtocol:@protocol(UrlLoader)]];
-
-  id block = [(id) ^ (const GURL& url, NSUInteger position,
-                      ui::PageTransition transition) {
-    EXPECT_EQ(url, [params externalURL]);
-    EXPECT_EQ(NSNotFound, static_cast<NSInteger>(position));
-    EXPECT_TRUE(PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK));
-  } copy];
-  SEL dismissSelector =
-      @selector(dismissWithNewTabAnimationToModel:withURL:atIndex:transition:);
-  [tabSwitcherController onSelector:dismissSelector callBlockExpectation:block];
-
-  // Setup main controller.
-  MainController* controller = GetMainController();
-  controller.browserViewInformation.mainBVC = bvcMock;
-  controller.tabSwitcher = tabSwitcherController;
-  controller.tabSwitcherActive = YES;
-
-  id mainApplicationDelegate =
-      [OCMockObject mockForClass:[MainApplicationDelegate class]];
-
-  AppState* appState =
-      [[AppState alloc] initWithBrowserLauncher:controller
-                             startupInformation:controller
-                            applicationDelegate:mainApplicationDelegate];
-  controller.appState = appState;
-
-  NSDictionary<NSString*, id>* options = nil;
-  [URLOpener openURL:url
-       applicationActive:YES
-                 options:options
-               tabOpener:controller
-      startupInformation:controller];
-
-  EXPECT_OCMOCK_VERIFY(tabSwitcherController);
-}
-
-TEST_F(URLOpenerTest, HandleOpenURLWithOpenTabs) {
-  NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
-  URLOpenerMockBVC* bvc_mock = [[URLOpenerMockBVC alloc] init];
-  URLOpenerMockBVC* otr_bvc_mock = [[URLOpenerMockBVC alloc] init];
-  bvc_mock.browserState = GetChromeBrowserState();
-
-  // Setup main controller.
-  MainController* controller = GetMainController();
-  controller.browserViewInformation.mainBVC =
-      static_cast<BrowserViewController*>(bvc_mock);
-  controller.browserViewInformation.otrBVC =
-      static_cast<BrowserViewController*>(otr_bvc_mock);
-  controller.browserViewInformation.currentBVC =
-      static_cast<BrowserViewController*>(bvc_mock);
-
-  NSDictionary<NSString*, id>* options = nil;
-  [URLOpener openURL:url
-       applicationActive:YES
-                 options:options
-               tabOpener:controller
-      startupInformation:controller];
-
-  EXPECT_EQ(GURL("http://www.google.com/"), [bvc_mock tabURL]);
-  EXPECT_EQ(0, static_cast<NSInteger>([bvc_mock position]));
-  EXPECT_TRUE(PageTransitionCoreTypeIs([bvc_mock transition],
-                                       ui::PAGE_TRANSITION_LINK));
-
-  EXPECT_FALSE([otr_bvc_mock tabURL].is_valid());
-}
-
 TEST_F(URLOpenerTest, HandleOpenURL) {
   // A set of tests for robustness of
   // application:openURL:options:tabOpener:startupInformation:
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
index 4950689..56a416b1 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
@@ -14,6 +14,7 @@
 #include "base/json/json_reader.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
+#include "components/payments/core/error_logger.h"
 #include "ios/chrome/browser/payments/ios_payment_instrument.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -49,7 +50,7 @@
 IOSPaymentInstrumentFinder::IOSPaymentInstrumentFinder(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     id<PaymentRequestUIDelegate> payment_request_ui_delegate)
-    : downloader_(url_loader_factory),
+    : downloader_(std::make_unique<ErrorLogger>(), url_loader_factory),
       image_fetcher_(url_loader_factory),
       payment_request_ui_delegate_(payment_request_ui_delegate),
       num_instruments_to_find_(0),
diff --git a/ios/chrome/browser/signin/account_reconcilor_factory.cc b/ios/chrome/browser/signin/account_reconcilor_factory.cc
index f2b32c7..cc12c80 100644
--- a/ios/chrome/browser/signin/account_reconcilor_factory.cc
+++ b/ios/chrome/browser/signin/account_reconcilor_factory.cc
@@ -12,9 +12,10 @@
 #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "ios/chrome/browser/signin/signin_client_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 namespace ios {
 
@@ -24,8 +25,8 @@
           BrowserStateDependencyManager::GetInstance()) {
   DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
   DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(SigninClientFactory::GetInstance());
-  DependsOn(SigninManagerFactory::GetInstance());
 }
 
 AccountReconcilorFactory::~AccountReconcilorFactory() {}
@@ -46,16 +47,16 @@
     web::BrowserState* context) const {
   ios::ChromeBrowserState* chrome_browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
-  SigninManager* signin_manager =
-      SigninManagerFactory::GetForBrowserState(chrome_browser_state);
+  auto* identity_manager =
+      IdentityManagerFactory::GetForBrowserState(chrome_browser_state);
   std::unique_ptr<AccountReconcilor> reconcilor(new AccountReconcilor(
       ProfileOAuth2TokenServiceFactory::GetForBrowserState(
           chrome_browser_state),
-      signin_manager,
+      identity_manager,
       SigninClientFactory::GetForBrowserState(chrome_browser_state),
       GaiaCookieManagerServiceFactory::GetForBrowserState(chrome_browser_state),
       std::make_unique<signin::MirrorAccountReconcilorDelegate>(
-          signin_manager)));
+          identity_manager)));
   reconcilor->Initialize(true /* start_reconcile_if_tokens_available */);
   return reconcilor;
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index d596ee4..4093ded 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "address_coordinator.h",
     "address_coordinator.mm",
+    "address_form.h",
+    "address_form.mm",
     "address_mediator.h",
     "address_mediator.mm",
     "card_coordinator.h",
@@ -62,6 +64,8 @@
   sources = [
     "action_cell.h",
     "action_cell.mm",
+    "address.h",
+    "address.mm",
     "address_consumer.h",
     "address_list_delegate.h",
     "address_view_controller.h",
@@ -127,6 +131,8 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "address_form_unittest.mm",
+    "address_unittest.mm",
     "credential_password_form_unittest.mm",
     "credential_unittest.mm",
     "credit_card_form_unittest.mm",
@@ -141,8 +147,9 @@
     "//components/autofill/core/common",
     "//components/autofill/ios/form_util:form_util",
     "//components/autofill/ios/form_util:test_support",
-    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/web_state_list:test_support",
+    "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/web/public/test/fakes",
     "//testing/gtest:gtest",
     "//third_party/ocmock:ocmock",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address.h b/ios/chrome/browser/ui/autofill/manual_fill/address.h
new file mode 100644
index 0000000..31a135f
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address.h
@@ -0,0 +1,57 @@
+// 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 IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_H_
+
+#import <Foundation/Foundation.h>
+
+// This represents an address to use with manual fill.
+@interface ManualFillAddress : NSObject
+
+// The addressee's first name.
+@property(nonatomic, readonly) NSString* firstName;
+
+// The addressee's middle name or middle initial, or empty.
+@property(nonatomic, readonly) NSString* middleNameOrInitial;
+
+// The addressee's last name.
+@property(nonatomic, readonly) NSString* lastName;
+
+// The first line of this address.
+@property(nonatomic, readonly) NSString* line1;
+
+// The second, optional, line of this address.
+@property(nonatomic, readonly) NSString* line2;
+
+// The zip code of the address.
+@property(nonatomic, readonly) NSString* zip;
+
+// The city of the address.
+@property(nonatomic, readonly) NSString* city;
+
+// The state or province of the address.
+@property(nonatomic, readonly) NSString* state;
+
+// The country of the address.
+@property(nonatomic, readonly) NSString* country;
+
+// Default init.
+- (instancetype)initWithFirstName:(NSString*)firstName
+              middleNameOrInitial:(NSString*)middleNameOrInitial
+                         lastName:(NSString*)lastName
+                            line1:(NSString*)line1
+                            line2:(NSString*)line2
+                              zip:(NSString*)zip
+                             city:(NSString*)city
+                            state:(NSString*)state
+                          country:(NSString*)country NS_DESIGNATED_INITIALIZER;
+
+// Unavailable. Please use |initWithFirstName:middleNameOrInitial:lastName:
+// line1:line2:zip:city:state:country:|.
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address.mm b/ios/chrome/browser/ui/autofill/manual_fill/address.mm
new file mode 100644
index 0000000..9754091
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address.mm
@@ -0,0 +1,96 @@
+// 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 "ios/chrome/browser/ui/autofill/manual_fill/address.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation ManualFillAddress
+
+- (instancetype)initWithFirstName:(NSString*)firstName
+              middleNameOrInitial:(NSString*)middleNameOrInitial
+                         lastName:(NSString*)lastName
+                            line1:(NSString*)line1
+                            line2:(NSString*)line2
+                              zip:(NSString*)zip
+                             city:(NSString*)city
+                            state:(NSString*)state
+                          country:(NSString*)country {
+  self = [super init];
+  if (self) {
+    _firstName = [firstName copy];
+    _middleNameOrInitial = [middleNameOrInitial copy];
+    _lastName = [lastName copy];
+    _line1 = [line1 copy];
+    _line2 = [line2 copy];
+    _zip = [zip copy];
+    _city = [city copy];
+    _state = [state copy];
+    _country = [country copy];
+  }
+  return self;
+}
+
+- (BOOL)isEqual:(id)object {
+  if (!object) {
+    return NO;
+  }
+  if (self == object) {
+    return YES;
+  }
+  if (![object isMemberOfClass:[ManualFillAddress class]]) {
+    return NO;
+  }
+  ManualFillAddress* otherObject = (ManualFillAddress*)object;
+  if (![otherObject.firstName isEqual:self.firstName]) {
+    return NO;
+  }
+  if (![otherObject.middleNameOrInitial isEqual:self.middleNameOrInitial]) {
+    return NO;
+  }
+  if (![otherObject.lastName isEqual:self.lastName]) {
+    return NO;
+  }
+  if (![otherObject.line1 isEqual:self.line1]) {
+    return NO;
+  }
+  if (![otherObject.line2 isEqual:self.line2]) {
+    return NO;
+  }
+  if (![otherObject.zip isEqual:self.zip]) {
+    return NO;
+  }
+  if (![otherObject.city isEqual:self.city]) {
+    return NO;
+  }
+  if (![otherObject.state isEqual:self.state]) {
+    return NO;
+  }
+  if (![otherObject.country isEqual:self.country]) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return [self.firstName hash] ^ [self.middleNameOrInitial hash] ^
+         [self.lastName hash] ^ [self.line1 hash] ^ [self.line2 hash] ^
+         [self.zip hash] ^ [self.city hash] ^ [self.state hash] ^
+         [self.country hash];
+}
+
+- (NSString*)description {
+  return
+      [NSString stringWithFormat:
+                    @"<%@ (%p): firstName: %@, middleNameOrInitial: %@, "
+                    @"lastName: %@, line1: %@, "
+                    @"line2: %@, zip: %@, city: %@, state: %@, country: %@>",
+                    NSStringFromClass([self class]), self, self.firstName,
+                    self.middleNameOrInitial, self.lastName, self.line1,
+                    self.line2, self.zip, self.city, self.state, self.country];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_form.h b/ios/chrome/browser/ui/autofill/manual_fill/address_form.h
new file mode 100644
index 0000000..e62de54
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_form.h
@@ -0,0 +1,23 @@
+// 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 IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_FORM_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_FORM_H_
+
+#import "ios/chrome/browser/ui/autofill/manual_fill/address.h"
+
+namespace autofill {
+class AutofillProfile;
+}
+
+// Extends |ManualFillAddress| with a convenience initializer from c++
+// |autofill::AutofillProfile|.
+@interface ManualFillAddress (AutofillProfile)
+
+// Convenience initializer from an autofill::AutofillProfile.
+- (instancetype)initWithProfile:(const autofill::AutofillProfile&)profile;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ADDRESS_FORM_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_form.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_form.mm
new file mode 100644
index 0000000..9309cd55
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_form.mm
@@ -0,0 +1,66 @@
+// 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 "ios/chrome/browser/ui/autofill/manual_fill/address_form.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "ios/chrome/browser/application_context.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Takes in an autofill profile and an autofill field type and returns the
+// corresponding field value.
+NSString* FieldValueOfTypeOnProfile(const autofill::AutofillProfile& profile,
+                                    autofill::ServerFieldType fieldType) {
+  return base::SysUTF16ToNSString(
+      profile.GetInfo(autofill::AutofillType(fieldType),
+                      GetApplicationContext()->GetApplicationLocale()));
+}
+
+}  // namespace
+
+@implementation ManualFillAddress (AutofillProfile)
+
+- (instancetype)initWithProfile:(const autofill::AutofillProfile&)profile {
+  NSString* firstName =
+      FieldValueOfTypeOnProfile(profile, autofill::NAME_FIRST);
+  NSString* middleNameOrInitial =
+      FieldValueOfTypeOnProfile(profile, autofill::NAME_MIDDLE);
+  if (!middleNameOrInitial || middleNameOrInitial.length == 0) {
+    middleNameOrInitial =
+        FieldValueOfTypeOnProfile(profile, autofill::NAME_MIDDLE_INITIAL);
+  }
+  NSString* lastName = FieldValueOfTypeOnProfile(profile, autofill::NAME_LAST);
+  NSString* line1 =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_LINE1);
+  NSString* line2 =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_LINE2);
+  NSString* zip =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_ZIP);
+  NSString* city =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_CITY);
+  NSString* state =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_STATE);
+  NSString* country =
+      FieldValueOfTypeOnProfile(profile, autofill::ADDRESS_HOME_COUNTRY);
+
+  return [self initWithFirstName:firstName
+             middleNameOrInitial:middleNameOrInitial
+                        lastName:lastName
+                           line1:line1
+                           line2:line2
+                             zip:zip
+                            city:city
+                           state:state
+                         country:country];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_form_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_form_unittest.mm
new file mode 100644
index 0000000..ca436c1
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_form_unittest.mm
@@ -0,0 +1,116 @@
+// 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 "ios/chrome/browser/ui/autofill/manual_fill/address_form.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "ios/chrome/browser/application_context.h"
+#include "testing/platform_test.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using autofill::AutofillProfile;
+using ManualFillAddressFormAutofilliOSTest = PlatformTest;
+
+namespace {
+
+void SetProfileFieldTypeValue(AutofillProfile* profile,
+                              const autofill::ServerFieldType fieldType,
+                              NSString* value) {
+  const base::string16 v = base::SysNSStringToUTF16(value);
+  const std::string& app_locale =
+      GetApplicationContext()->GetApplicationLocale();
+  profile->SetInfo(fieldType, v, app_locale);
+}
+
+}  // namespace
+
+// Tests the creation of an address from an autofill::AutofillProfile.
+TEST_F(ManualFillAddressFormAutofilliOSTest, CreationWithMiddleName) {
+  NSString* firstName = @"First";
+  NSString* middleName = @"Middle";
+  NSString* lastName = @"Last";
+  NSString* line1 = @"10 Main Street";
+  NSString* line2 = @"Appt 16";
+  NSString* zip = @"12345";
+  NSString* city = @"Springfield";
+  NSString* state = @"State";
+  NSString* country = @"US";
+
+  autofill::CountryNames::SetLocaleString("en-US");
+
+  AutofillProfile* profile = new AutofillProfile();
+  SetProfileFieldTypeValue(profile, autofill::NAME_FIRST, firstName);
+  SetProfileFieldTypeValue(profile, autofill::NAME_MIDDLE, middleName);
+  SetProfileFieldTypeValue(profile, autofill::NAME_LAST, lastName);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_LINE1, line1);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_LINE2, line2);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_ZIP, zip);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_CITY, city);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_STATE, state);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_COUNTRY, country);
+
+  ManualFillAddress* manualFillAddress =
+      [[ManualFillAddress alloc] initWithProfile:*profile];
+
+  EXPECT_TRUE(manualFillAddress);
+  EXPECT_TRUE([firstName isEqualToString:manualFillAddress.firstName]);
+  EXPECT_TRUE(
+      [middleName isEqualToString:manualFillAddress.middleNameOrInitial]);
+  EXPECT_TRUE([lastName isEqualToString:manualFillAddress.lastName]);
+  EXPECT_TRUE([line1 isEqualToString:manualFillAddress.line1]);
+  EXPECT_TRUE([line2 isEqualToString:manualFillAddress.line2]);
+  EXPECT_TRUE([zip isEqualToString:manualFillAddress.zip]);
+  EXPECT_TRUE([city isEqualToString:manualFillAddress.city]);
+  EXPECT_TRUE([state isEqualToString:manualFillAddress.state]);
+  EXPECT_TRUE([@"United States" isEqualToString:manualFillAddress.country]);
+}
+
+// Tests the creation of an address from an autofill::AutofillProfile.
+TEST_F(ManualFillAddressFormAutofilliOSTest, CreationWithMiddleInitial) {
+  NSString* firstName = @"First";
+  NSString* middleInitial = @"M";
+  NSString* lastName = @"Last";
+  NSString* line1 = @"10 Main Street";
+  NSString* line2 = @"Appt 16";
+  NSString* zip = @"12345";
+  NSString* city = @"Springfield";
+  NSString* state = @"State";
+  NSString* country = @"US";
+
+  autofill::CountryNames::SetLocaleString("en-US");
+
+  AutofillProfile* profile = new AutofillProfile();
+  SetProfileFieldTypeValue(profile, autofill::NAME_FIRST, firstName);
+  SetProfileFieldTypeValue(profile, autofill::NAME_MIDDLE_INITIAL,
+                           middleInitial);
+  SetProfileFieldTypeValue(profile, autofill::NAME_LAST, lastName);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_LINE1, line1);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_LINE2, line2);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_ZIP, zip);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_CITY, city);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_STATE, state);
+  SetProfileFieldTypeValue(profile, autofill::ADDRESS_HOME_COUNTRY, country);
+
+  ManualFillAddress* manualFillAddress =
+      [[ManualFillAddress alloc] initWithProfile:*profile];
+
+  EXPECT_TRUE(manualFillAddress);
+  EXPECT_TRUE([firstName isEqualToString:manualFillAddress.firstName]);
+  EXPECT_TRUE(
+      [middleInitial isEqualToString:manualFillAddress.middleNameOrInitial]);
+  EXPECT_TRUE([lastName isEqualToString:manualFillAddress.lastName]);
+  EXPECT_TRUE([line1 isEqualToString:manualFillAddress.line1]);
+  EXPECT_TRUE([line2 isEqualToString:manualFillAddress.line2]);
+  EXPECT_TRUE([zip isEqualToString:manualFillAddress.zip]);
+  EXPECT_TRUE([city isEqualToString:manualFillAddress.city]);
+  EXPECT_TRUE([state isEqualToString:manualFillAddress.state]);
+  EXPECT_TRUE([@"United States" isEqualToString:manualFillAddress.country]);
+}
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_unittest.mm
new file mode 100644
index 0000000..fad4b3b
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_unittest.mm
@@ -0,0 +1,190 @@
+// 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 "ios/chrome/browser/ui/autofill/manual_fill/address.h"
+
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using ManualFillAddressiOSTest = PlatformTest;
+
+// Tests that a credential is correctly created.
+TEST_F(ManualFillAddressiOSTest, Creation) {
+  NSString* firstName = @"First";
+  NSString* middleNameOrInitial = @"M";
+  NSString* lastName = @"Last";
+  NSString* line1 = @"10 Main Street";
+  NSString* line2 = @"Appt 16";
+  NSString* zip = @"12345";
+  NSString* city = @"Springfield";
+  NSString* state = @"State";
+  NSString* country = @"Country";
+  ManualFillAddress* address =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_TRUE(address);
+  EXPECT_TRUE([firstName isEqualToString:address.firstName]);
+  EXPECT_TRUE(
+      [middleNameOrInitial isEqualToString:address.middleNameOrInitial]);
+  EXPECT_TRUE([lastName isEqualToString:address.lastName]);
+  EXPECT_TRUE([line1 isEqualToString:address.line1]);
+  EXPECT_TRUE([line2 isEqualToString:address.line2]);
+  EXPECT_TRUE([zip isEqualToString:address.zip]);
+  EXPECT_TRUE([city isEqualToString:address.city]);
+  EXPECT_TRUE([state isEqualToString:address.state]);
+  EXPECT_TRUE([country isEqualToString:address.country]);
+}
+
+// Test equality between addresses (lexicographically).
+TEST_F(ManualFillAddressiOSTest, Equality) {
+  NSString* firstName = @"First";
+  NSString* middleNameOrInitial = @"M";
+  NSString* lastName = @"Last";
+  NSString* line1 = @"10 Main Street";
+  NSString* line2 = @"Appt 16";
+  NSString* zip = @"12345";
+  NSString* city = @"Springfield";
+  NSString* state = @"State";
+  NSString* country = @"Country";
+  ManualFillAddress* address =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+
+  ManualFillAddress* equalAddress =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_TRUE([address isEqual:equalAddress]);
+
+  ManualFillAddress* differentAddressFirstName =
+      [[ManualFillAddress alloc] initWithFirstName:@"Bilbo"
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressFirstName]);
+
+  ManualFillAddress* differentAddressMiddleNameOrInitial =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:@"R"
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressMiddleNameOrInitial]);
+
+  ManualFillAddress* differentAddressLastName =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:@"Hobbit"
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressLastName]);
+
+  ManualFillAddress* differentAddressLine1 =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:@"A House"
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressLine1]);
+
+  ManualFillAddress* differentAddressLine2 =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:@""
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressLine2]);
+
+  ManualFillAddress* differentAddressZip =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:@"1937"
+                                              city:city
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressZip]);
+
+  ManualFillAddress* differentAddressCity =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:@"Shire"
+                                             state:state
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressCity]);
+
+  ManualFillAddress* differentAddressState =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:@"Eriador"
+                                           country:country];
+  EXPECT_FALSE([address isEqual:differentAddressState]);
+
+  ManualFillAddress* differentAddressCountry =
+      [[ManualFillAddress alloc] initWithFirstName:firstName
+                               middleNameOrInitial:middleNameOrInitial
+                                          lastName:lastName
+                                             line1:line1
+                                             line2:line2
+                                               zip:zip
+                                              city:city
+                                             state:state
+                                           country:@"Arnor"];
+  EXPECT_FALSE([address isEqual:differentAddressCountry]);
+}
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.mm
index 22d5cfbe..b81c476 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.h"
 
 #include "base/ios/ios_util.h"
+#include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/action_cell.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
@@ -37,6 +38,8 @@
 // TODO(crbug.com/845472): look at replacing ManualFillXXXConsumer with
 // ManualFillItemsConsumer.
 - (void)presentAddresses:(NSArray<ManualFillAddressItem*>*)addresses {
+  UMA_HISTOGRAM_COUNTS_100("ManualFallback.PresentedOptions.Profiles",
+                           addresses.count);
   [self presentDataItems:(NSArray<TableViewItem*>*)addresses];
 }
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.mm
index 0f715b0..fcb0f88 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_view_controller.h"
 
 #include "base/ios/ios_util.h"
+#include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/action_cell.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
@@ -35,6 +36,8 @@
 #pragma mark - ManualFillCardConsumer
 
 - (void)presentCards:(NSArray<ManualFillCardItem*>*)cards {
+  UMA_HISTOGRAM_COUNTS_100("ManualFallback.PresentedOptions.CreditCards",
+                           cards.count);
   [self presentDataItems:(NSArray<TableViewItem*>*)cards];
 }
 
diff --git a/ios/chrome/browser/ui/browser_view_controller_egtest.mm b/ios/chrome/browser/ui/browser_view_controller_egtest.mm
index 9967b79..f3caf3b 100644
--- a/ios/chrome/browser/ui/browser_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/browser_view_controller_egtest.mm
@@ -5,6 +5,7 @@
 #include <map>
 
 #import <EarlGrey/EarlGrey.h>
+#import <UIKit/UIKit.h>
 #import <WebKit/WebKit.h>
 #import <XCTest/XCTest.h>
 
@@ -12,6 +13,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -102,4 +104,51 @@
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
 }
 
+#pragma mark - Open URL
+
+// Tests that BVC properly handles open URL. When NTP is visible, the URL
+// should be opened in the same tab (not create a new tab).
+- (void)testOpenURLFromNTP {
+  id<UIApplicationDelegate> appDelegate =
+      [[UIApplication sharedApplication] delegate];
+  [appDelegate application:[UIApplication sharedApplication]
+                   openURL:[NSURL URLWithString:@"https://anything"]
+                   options:[NSDictionary dictionary]];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
+                                          "https://anything")]
+      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForMainTabCount:1];
+}
+
+// Tests that BVC properly handles open URL. When BVC is showing a non-NTP
+// tab, the URL should be opened in a new tab, adding to the tab count.
+- (void)testOpenURLFromTab {
+  [ChromeEarlGrey loadURL:GURL("https://invalid")];
+  id<UIApplicationDelegate> appDelegate =
+      [[UIApplication sharedApplication] delegate];
+  [appDelegate application:[UIApplication sharedApplication]
+                   openURL:[NSURL URLWithString:@"https://anything"]
+                   options:[NSDictionary dictionary]];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
+                                          "https://anything")]
+      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForMainTabCount:2];
+}
+
+// Tests that BVC properly handles open URL. When tab switcher is showing,
+// the URL should be opened in a new tab, and BVC should be shown.
+- (void)testOpenURLFromTabSwitcher {
+  chrome_test_util::CloseCurrentTab();
+  [ChromeEarlGrey waitForMainTabCount:0];
+  id<UIApplicationDelegate> appDelegate =
+      [[UIApplication sharedApplication] delegate];
+  [appDelegate application:[UIApplication sharedApplication]
+                   openURL:[NSURL URLWithString:@"https://anything"]
+                   options:[NSDictionary dictionary]];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
+                                          "https://anything")]
+      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForMainTabCount:1];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
index 7f063bb1..53c6cfc 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -314,7 +314,6 @@
     _fakeLocationBar.userInteractionEnabled = NO;
     _fakeLocationBar.backgroundColor =
         [UIColor colorWithWhite:0 alpha:kAdaptiveLocationBarBackgroundAlpha];
-    _fakeLocationBar.layer.cornerRadius = kAdaptiveLocationBarCornerRadius;
     _fakeLocationBar.translatesAutoresizingMaskIntoConstraints = NO;
   }
   return _fakeLocationBar;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
index 4fd2d092..386eac2 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -192,7 +192,6 @@
 
     _locationButton = [[LocationBarSteadyButton alloc] init];
     _locationButton.translatesAutoresizingMaskIntoConstraints = NO;
-    _locationButton.layer.cornerRadius = kAdaptiveLocationBarCornerRadius;
     [_locationButton addSubview:_trailingButton];
     [_locationButton addSubview:_locationContainerView];
 
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index eb80e31..cbcf878c 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -139,6 +139,7 @@
     "//ios/chrome/browser/ui/context_menu",
     "//ios/chrome/browser/ui/favicon",
     "//ios/chrome/browser/ui/overscroll_actions",
+    "//ios/chrome/browser/ui/toolbar:toolbar_ui",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common",
diff --git a/ios/chrome/browser/ui/ntp/incognito_view.mm b/ios/chrome/browser/ui/ntp/incognito_view.mm
index 27ddabe9..5f65456b 100644
--- a/ios/chrome/browser/ui/ntp/incognito_view.mm
+++ b/ios/chrome/browser/ui/ntp/incognito_view.mm
@@ -8,6 +8,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h"
+#import "ios/chrome/browser/ui/toolbar/toolbar_utils.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
@@ -350,7 +351,9 @@
     } else {
       topInset = StatusBarHeight();
     }
-    _topToolbarMarginHeight.constant = topInset + kAdaptiveToolbarHeight;
+    _topToolbarMarginHeight.constant =
+        topInset + ToolbarExpandedHeight(
+                       self.traitCollection.preferredContentSizeCategory);
   }
 
   if (IsSplitToolbarMode(self)) {
diff --git a/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h b/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h
index 25df0d3..873fd9af 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h
+++ b/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h
@@ -55,7 +55,6 @@
 extern const CGFloat kContractedLocationBarHorizontalMargin;
 
 // Adaptive Location bar constants.
-extern const CGFloat kAdaptiveLocationBarCornerRadius;
 extern const CGFloat kAdaptiveLocationBarBackgroundAlpha;
 extern const CGFloat kAdaptiveLocationBarBackgroundAlphaIncognito;
 extern const CGFloat kAdaptiveLocationBarVerticalMargin;
diff --git a/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.mm b/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.mm
index 0dcb5d5..5ff6cf3 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.mm
+++ b/ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.mm
@@ -37,19 +37,18 @@
 const CGFloat kExpandedLocationBarHorizontalMargin = 8;
 const CGFloat kContractedLocationBarHorizontalMargin = 19;
 
-const CGFloat kAdaptiveLocationBarCornerRadius = 18;
 const CGFloat kAdaptiveLocationBarBackgroundAlpha = 0.09;
 const CGFloat kAdaptiveLocationBarBackgroundAlphaIncognito = 0.12;
 const CGFloat kAdaptiveLocationBarVerticalMargin = 6.0f;
 const CGFloat kAdaptiveLocationBarVerticalMarginFullscreen = 3.0f;
-const CGFloat kLocationBarVerticalMarginDynamicType = 2.0f;
+const CGFloat kLocationBarVerticalMarginDynamicType = -1.0f;
 const CGFloat kAdaptiveLocationBarExtraVerticalMargin = 1.0f;
 
 const CGFloat kTopToolbarUnsplitMargin = 2;
 const CGFloat kAdaptiveToolbarHeight = 48;
 const CGFloat kNonDynamicToolbarHeight = 14;
 const CGFloat kToolbarHeightFullscreen = 20;
-const CGFloat kNonDynamicToolbarHeightFullscreen = 7;
+const CGFloat kNonDynamicToolbarHeightFullscreen = 3;
 
 NSString* const kToolbarToolsMenuButtonIdentifier =
     @"kToolbarToolsMenuButtonIdentifier";
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
index 5a81b53a..b726e650 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
@@ -229,8 +229,6 @@
   self.locationBarContainer.backgroundColor =
       [self.buttonFactory.toolbarConfiguration
           locationBarBackgroundColorWithVisibility:1];
-  self.locationBarContainer.layer.cornerRadius =
-      kAdaptiveLocationBarCornerRadius;
   [self.locationBarContainer
       setContentHuggingPriority:UILayoutPriorityDefaultLow
                         forAxis:UILayoutConstraintAxisHorizontal];
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
index 815850d..0b84aca3 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
@@ -110,6 +110,8 @@
 
   self.view.locationBarHeight.constant =
       [self locationBarHeightForFullscreenProgress:1];
+  self.view.locationBarContainer.layer.cornerRadius =
+      self.view.locationBarHeight.constant / 2;
   self.view.locationBarBottomConstraint.constant =
       [self verticalMarginForLocationBarForFullscreenProgress:1];
 
@@ -149,6 +151,8 @@
       self.traitCollection.preferredContentSizeCategory) {
     self.view.locationBarHeight.constant = [self
         locationBarHeightForFullscreenProgress:self.previousFullscreenProgress];
+    self.view.locationBarContainer.layer.cornerRadius =
+        self.view.locationBarHeight.constant / 2;
   }
 }
 
@@ -183,6 +187,8 @@
   self.view.trailingStackView.alpha = alphaValue;
   self.view.locationBarHeight.constant =
       [self locationBarHeightForFullscreenProgress:progress];
+  self.view.locationBarContainer.layer.cornerRadius =
+      self.view.locationBarHeight.constant / 2;
   self.view.locationBarBottomConstraint.constant =
       [self verticalMarginForLocationBarForFullscreenProgress:progress];
   self.view.locationBarContainer.backgroundColor =
diff --git a/ios/web/public/test/test_web_thread_bundle.h b/ios/web/public/test/test_web_thread_bundle.h
index 11173102..b3be7fd 100644
--- a/ios/web/public/test/test_web_thread_bundle.h
+++ b/ios/web/public/test/test_web_thread_bundle.h
@@ -32,9 +32,7 @@
 #include "base/macros.h"
 
 namespace base {
-namespace test {
-class ScopedTaskEnvironment;
-}  // namespace test
+class MessageLoop;
 }  // namespace base
 
 namespace web {
@@ -60,7 +58,7 @@
  private:
   void Init(int options);
 
-  std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
+  std::unique_ptr<base::MessageLoop> message_loop_;
   std::unique_ptr<TestWebThread> ui_thread_;
   std::unique_ptr<TestWebThread> io_thread_;
 
diff --git a/ios/web/test/test_web_thread_bundle.cc b/ios/web/test/test_web_thread_bundle.cc
index 095c1e0..1e12d6b9 100644
--- a/ios/web/test/test_web_thread_bundle.cc
+++ b/ios/web/test/test_web_thread_bundle.cc
@@ -8,6 +8,8 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/task/task_scheduler/task_scheduler.h"
+#include "base/task/task_scheduler/task_scheduler_impl.h"
 #include "base/test/scoped_task_environment.h"
 #include "ios/web/public/test/test_web_thread.h"
 #include "ios/web/web_thread_impl.h"
@@ -45,7 +47,12 @@
   ui_thread_.reset();
   base::RunLoop().RunUntilIdle();
 
-  scoped_task_environment_.reset();
+  message_loop_.reset();
+
+  base::TaskScheduler::GetInstance()->FlushForTesting();
+  base::TaskScheduler::GetInstance()->Shutdown();
+  base::TaskScheduler::GetInstance()->JoinForTesting();
+  base::TaskScheduler::SetInstance(nullptr);
 
   WebThreadImpl::ResetTaskExecutorForTesting();
 }
@@ -53,26 +60,35 @@
 void TestWebThreadBundle::Init(int options) {
   WebThreadImpl::CreateTaskExecutor();
 
-  scoped_task_environment_ =
-      std::make_unique<base::test::ScopedTaskEnvironment>(
-          options & TestWebThreadBundle::IO_MAINLOOP
-              ? base::test::ScopedTaskEnvironment::MainThreadType::IO
-              : base::test::ScopedTaskEnvironment::MainThreadType::UI);
+  // TODO(crbug.com/903803): Use ScopedTaskEnviroment here instead.
+  message_loop_ = std::make_unique<base::MessageLoop>(
+      options & TestWebThreadBundle::IO_MAINLOOP ? base::MessageLoop::TYPE_IO
+                                                 : base::MessageLoop::TYPE_UI);
 
   // TODO(crbug.com/826465): TestWebThread won't need MessageLoop*
   // once modernized to match its //content equivalent.
-  ui_thread_.reset(new TestWebThread(
-      WebThread::UI,
-      base::MessageLoopCurrent::Get()->ToMessageLoopDeprecated()));
+  ui_thread_.reset(new TestWebThread(WebThread::UI, message_loop_.get()));
 
   if (options & TestWebThreadBundle::REAL_IO_THREAD) {
     io_thread_.reset(new TestWebThread(WebThread::IO));
     io_thread_->StartIOThread();
   } else {
-    io_thread_.reset(new TestWebThread(
-        WebThread::IO,
-        base::MessageLoopCurrent::Get()->ToMessageLoopDeprecated()));
+    io_thread_.reset(new TestWebThread(WebThread::IO, message_loop_.get()));
   }
+
+  // Copied from ScopedTaskEnvironment::ScopedTaskEnvironment.
+  // TODO(crbug.com/821034): Bring ScopedTaskEnvironment back in
+  // TestWebThreadBundle.
+  constexpr int kMaxThreads = 2;
+  const base::TimeDelta kSuggestedReclaimTime = base::TimeDelta::Max();
+  const base::SchedulerWorkerPoolParams worker_pool_params(
+      kMaxThreads, kSuggestedReclaimTime);
+  base::TaskScheduler::SetInstance(
+      std::make_unique<base::internal::TaskSchedulerImpl>(
+          "ScopedTaskEnvironment"));
+  base::TaskScheduler::GetInstance()->Start(
+      {worker_pool_params, worker_pool_params, worker_pool_params,
+       worker_pool_params});
 }
 
 }  // namespace web
diff --git a/media/base/scoped_async_trace.cc b/media/base/scoped_async_trace.cc
index 42f8935..cb525c79 100644
--- a/media/base/scoped_async_trace.cc
+++ b/media/base/scoped_async_trace.cc
@@ -9,23 +9,24 @@
 
 namespace media {
 
+namespace {
+constexpr const char kCategory[] = "media";
+}  // namespace
+
 // static
 std::unique_ptr<ScopedAsyncTrace> ScopedAsyncTrace::CreateIfEnabled(
-    const char* category,
     const char* name) {
   bool enabled = false;
-  TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &enabled);
-  return enabled ? base::WrapUnique(new ScopedAsyncTrace(category, name))
-                 : nullptr;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED(kCategory, &enabled);
+  return enabled ? base::WrapUnique(new ScopedAsyncTrace(name)) : nullptr;
 }
 
-ScopedAsyncTrace::ScopedAsyncTrace(const char* category, const char* name)
-    : category_(category), name_(name) {
-  TRACE_EVENT_ASYNC_BEGIN0(category_, name_, this);
+ScopedAsyncTrace::ScopedAsyncTrace(const char* name) : name_(name) {
+  TRACE_EVENT_ASYNC_BEGIN0(kCategory, name_, this);
 }
 
 ScopedAsyncTrace::~ScopedAsyncTrace() {
-  TRACE_EVENT_ASYNC_END0(category_, name_, this);
+  TRACE_EVENT_ASYNC_END0(kCategory, name_, this);
 }
 
 }  // namespace media
diff --git a/media/base/scoped_async_trace.h b/media/base/scoped_async_trace.h
index affac29c..b82df2e 100644
--- a/media/base/scoped_async_trace.h
+++ b/media/base/scoped_async_trace.h
@@ -20,21 +20,19 @@
 // is destroyed without being run.
 class MEDIA_EXPORT ScopedAsyncTrace {
  public:
-  // Create a ScopedAsyncTrace if tracing for |category| is enabled, else return
+  // Create a ScopedAsyncTrace if tracing for "media" is enabled, else return
   // nullptr.  |name| provided to the trace as the name(!).
   // IMPORTANT: These strings must outlive |this|, since tracing needs it.  In
   // other words, use literal strings only.  See trace_event_common.h .
-  static std::unique_ptr<ScopedAsyncTrace> CreateIfEnabled(const char* category,
-                                                           const char* name);
+  static std::unique_ptr<ScopedAsyncTrace> CreateIfEnabled(const char* name);
 
   ~ScopedAsyncTrace();
 
   // TODO(liberato): Add StepInto / StepPast.
 
  private:
-  ScopedAsyncTrace(const char* category, const char* name);
+  explicit ScopedAsyncTrace(const char* name);
 
-  const char* category_ = nullptr;
   const char* name_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedAsyncTrace);
diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc
index 32a1e41e..213e7c3 100644
--- a/media/blink/video_frame_compositor.cc
+++ b/media/blink/video_frame_compositor.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/time/default_tick_clock.h"
-#include "base/trace_event/auto_open_close_event.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/media_switches.h"
@@ -21,6 +20,9 @@
 // background rendering to keep the Render() callbacks moving.
 const int kBackgroundRenderingTimeoutMs = 250;
 
+// static
+constexpr const char VideoFrameCompositor::kTracingCategory[];
+
 VideoFrameCompositor::VideoFrameCompositor(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     std::unique_ptr<blink::WebVideoFrameSubmitter> submitter)
@@ -111,8 +113,9 @@
   rendering_ = new_state;
 
   if (!auto_open_close_) {
-    auto_open_close_.reset(new base::trace_event::AutoOpenCloseEvent(
-        base::trace_event::AutoOpenCloseEvent::Type::ASYNC, "media,rail",
+    auto_open_close_.reset(new base::trace_event::AutoOpenCloseEvent<
+                           kTracingCategory>(
+        base::trace_event::AutoOpenCloseEvent<kTracingCategory>::Type::ASYNC,
         "VideoPlayback"));
   }
 
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index 5f826ae..287d758 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -16,6 +16,7 @@
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "base/trace_event/auto_open_close_event.h"
 #include "cc/layers/surface_layer.h"
 #include "cc/layers/video_frame_provider.h"
 #include "media/base/video_renderer_sink.h"
@@ -24,12 +25,6 @@
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace base {
-namespace trace_event {
-class AutoOpenCloseEvent;
-}
-}
-
 namespace viz {
 class SurfaceId;
 }
@@ -159,6 +154,9 @@
   }
 
  private:
+  // TracingCategory name for |auto_open_close_|.
+  static constexpr const char kTracingCategory[] = "media,rail";
+
   // Ran on the |task_runner_| to initalize |submitter_|;
   void InitializeSubmitter();
 
@@ -227,7 +225,8 @@
   VideoRendererSink::RenderCallback* callback_ GUARDED_BY(callback_lock_);
 
   // AutoOpenCloseEvent for begin/end events.
-  std::unique_ptr<base::trace_event::AutoOpenCloseEvent> auto_open_close_;
+  std::unique_ptr<base::trace_event::AutoOpenCloseEvent<kTracingCategory>>
+      auto_open_close_;
   std::unique_ptr<blink::WebVideoFrameSubmitter> submitter_;
 
   base::WeakPtrFactory<VideoFrameCompositor> weak_ptr_factory_;
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc
index 2848706..5fd8aaa1 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.cc
+++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -81,7 +81,6 @@
     const cros::mojom::CameraMetadataPtr& static_metadata,
     std::vector<cros::mojom::Camera3StreamPtr> streams) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
-  DCHECK(!stream_context_[StreamType::kPreview]);
 
   // The partial result count metadata is optional; defaults to 1 in case it
   // is not set in the static metadata.
@@ -191,6 +190,7 @@
     base::OnceCallback<void(int32_t)> callback) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
   capturing_ = false;
+  repeating_request_settings_.reset();
   if (callback) {
     capture_interface_->Flush(std::move(callback));
   }
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 5bb56c9..f66da51 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -744,7 +744,7 @@
   gfx::Rect visible_rect(output_buffer->size());
   std::unique_ptr<ScopedAsyncTrace> async_trace =
       ScopedAsyncTrace::CreateIfEnabled(
-          "media", "MediaCodecVideoDecoder::CreateVideoFrame");
+          "MediaCodecVideoDecoder::CreateVideoFrame");
   video_frame_factory_->CreateVideoFrame(
       std::move(output_buffer), presentation_time,
       GetNaturalSize(visible_rect, decoder_config_.GetPixelAspectRatio()),
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
index 9bf54a0..4e19f01 100644
--- a/mojo/public/cpp/bindings/README.md
+++ b/mojo/public/cpp/bindings/README.md
@@ -1175,9 +1175,9 @@
 interface which itself already has a master interface.
 
 If you want to test an associated interface endpoint without first
-associating it, you can use `mojo::MakeIsolatedRequest()`. This will create
-working associated interface endpoints which are not actually associated with
-anything else.
+associating it, you can use `mojo::MakeRequestAssociatedWithDedicatedPipe`. This
+will create working associated interface endpoints which are not actually
+associated with anything else.
 
 ### Read more
 
diff --git a/mojo/public/js/bindings_lite.js b/mojo/public/js/bindings_lite.js
index fe0f56b..d5d2391f 100644
--- a/mojo/public/js/bindings_lite.js
+++ b/mojo/public/js/bindings_lite.js
@@ -255,7 +255,8 @@
     this.pendingResponses_.delete(header.requestId);
     if (!pendingResponse)
       return this.onError_('Received unexpected response message');
-    const decoder = new mojo.internal.Decoder(data, handles, header.headerSize);
+    const decoder = new mojo.internal.Decoder(
+        new DataView(buffer, header.headerSize), handles);
     const responseValue =
         decoder.decodeStructInline(pendingResponse.responseStruct.$.structSpec);
     if (!responseValue)
@@ -353,7 +354,7 @@
   }
 
   /**
-   * @param {...*}
+   * @param {...*} arguments
    * @private
    */
   dispatch_() {
@@ -362,7 +363,7 @@
   }
 
   /**
-   * @param {...*}
+   * @param {...*} arguments
    * @return {?Object}
    * @private
    */
@@ -461,7 +462,8 @@
     const handler = this.messageHandlers_.get(header.ordinal);
     if (!handler)
       throw new Error('Received unknown message');
-    const decoder = new mojo.internal.Decoder(data, handles, header.headerSize);
+    const decoder = new mojo.internal.Decoder(
+        new DataView(buffer, header.headerSize), handles);
     const request =
         decoder.decodeStructInline(handler.paramStruct.$.structSpec);
     if (!request)
@@ -700,6 +702,38 @@
 };
 
 /**
+ * @param {!mojo.internal.StructSpec} structSpec
+ * @param {*} value
+ * @return {number}
+ */
+mojo.internal.computeTotalStructSize = function(structSpec, value) {
+  let size = mojo.internal.kStructHeaderSize + structSpec.packedSize;
+  for (const field of structSpec.fields) {
+    const fieldValue = value[field.name];
+    if (field.type.$.computePayloadSize &&
+        !mojo.internal.isNullOrUndefined(fieldValue)) {
+      size +=
+          mojo.internal.align(field.type.$.computePayloadSize(fieldValue), 8);
+    }
+  }
+  return size;
+};
+
+/**
+ * @param {!mojo.internal.ArraySpec} arraySpec
+ * @param {number} length
+ * @return {number}
+ */
+mojo.internal.computeArraySize = function(arraySpec, length) {
+  if (arraySpec.elementType === mojo.mojom.Bool) {
+    return mojo.internal.kArrayHeaderSize + (length + 7) >> 3;
+  } else {
+    return mojo.internal.kArrayHeaderSize +
+        length * arraySpec.elementType.$.arrayElementSize;
+  }
+};
+
+/**
  * @param {!DataView} dataView
  * @param {number} byteOffset
  * @return {number}
@@ -724,10 +758,12 @@
   /**
    * @param {number} flags
    * @param {number} ordinal
-   * @param {number=} opt_requestId
+   * @param {number} requestId
+   * @param {!mojo.internal.StructSpec} paramStructSpec
+   * @param {*} value
    * @private
    */
-  constructor(flags, ordinal, opt_requestId) {
+  constructor(flags, ordinal, requestId, paramStructSpec, value) {
     let headerSize, version;
     if ((flags &
          (mojo.internal.kMessageFlagExpectsResponse |
@@ -739,282 +775,180 @@
       version = 1;
     }
 
+    const totalMessageSize = headerSize +
+        mojo.internal.computeTotalStructSize(paramStructSpec, value);
+
     /** @public {!ArrayBuffer} */
-    this.buffer;
+    this.buffer = new ArrayBuffer(totalMessageSize);
 
-    /** @public {!DataView} */
-    this.data;
+    /* @public {!Array<MojoHandle>} */
+    this.handles = [];
 
-    /** @private {number} */
-    this.bitOffset_;
-
-    this.resize_(headerSize);
-
-    const header = this.data;
+    const header = new DataView(this.buffer);
     header.setUint32(0, headerSize, mojo.internal.kHostLittleEndian);
     header.setUint32(4, version, mojo.internal.kHostLittleEndian);
     header.setUint32(8, 0);  // Interface ID (only for associated interfaces)
     header.setUint32(12, ordinal, mojo.internal.kHostLittleEndian);
     header.setUint32(16, flags, mojo.internal.kHostLittleEndian);
     header.setUint32(20, 0);  // Padding
-    if (version > 0) {
-      mojo.internal.setUint64(
-          this.data, 24,
-          /** @type {number} */ (opt_requestId));
-    }
-
-    this.handles = [];
+    if (version > 0)
+      mojo.internal.setUint64(header, 24, requestId);
 
     /** @private {number} */
-    this.cursor_ = headerSize;
+    this.nextAllocationOffset_ = headerSize;
 
-    /** @private {!Array<{pointerOffset: number, execute: Function}>} */
-    this.deferredEncodings_ = [];
+    const paramStructData = this.allocate(
+        mojo.internal.kStructHeaderSize + paramStructSpec.packedSize);
+    const encoder = new mojo.internal.Encoder(this, paramStructData);
+    encoder.encodeStructInline(paramStructSpec, value);
   }
 
   /**
-   * @param {number} newByteLength
-   * @private
+   * @param {number} numBytes
+   * @return {!DataView} A view into the allocated message bytes.
    */
-  resize_(newByteLength) {
-    if (this.buffer && newByteLength === this.buffer.byteLength)
-      return;
-    const newBuffer = new ArrayBuffer(newByteLength);
-    if (this.buffer)
-      new Uint8Array(newBuffer).set(new Uint8Array(this.buffer));
-    this.buffer = newBuffer;
-    this.data = new DataView(newBuffer);
-    this.bitOffset_ = 0;
+  allocate(numBytes) {
+    const alignedSize = mojo.internal.align(numBytes, 8);
+    const view =
+        new DataView(this.buffer, this.nextAllocationOffset_, alignedSize);
+    this.nextAllocationOffset_ += alignedSize;
+    return view;
   }
+};
 
+/**
+ * Helps encode outgoing messages. Encoders may be created recursively to encode
+ * parial message fragments indexed by indirect message offsets, as with encoded
+ * arrays and nested structs.
+ */
+mojo.internal.Encoder = class {
   /**
-   * @param {number} additionalByteLength
+   * @param {!Message} message
+   * @param {!DataView} data
    * @private
    */
-  grow_(additionalByteLength) {
-    const offset = this.buffer.byteLength;
-    this.resize_(offset + additionalByteLength);
-    return offset;
+  constructor(message, data) {
+    /** @private {!Message} */
+    this.message_ = message;
+
+    /** @private {!DataView} */
+    this.data_ = data;
   }
 
-  /**
-   * @param {number} alignment
-   * @private
-   */
-  alignCursor_(alignment) {
-    if (this.bitOffset_) {
-      this.cursor_++;
-      this.bitOffset_ = 0;
-    }
-    this.cursor_ = mojo.internal.align(this.cursor_, alignment);
-  }
-
-  /**
-   * @param {number} alignment
-   * @private
-   */
-  alignAndGrowToCursor_(alignment) {
-    this.alignCursor_(alignment);
-    if (this.cursor_ > this.buffer.byteLength)
-      this.resize_(this.cursor_);
-  }
-
-  /**
-   * @param {number} amount
-   * @param {number=} opt_alignment
-   * @private
-   */
-  advanceCursor_(amount, opt_alignment) {
-    // As a general rule, a value of N bytes should be aligned to N bytes. This
-    // is the default behavior.
-    if (!opt_alignment)
-      opt_alignment = amount;
-    this.alignCursor_(opt_alignment);
-
-    const offset = this.cursor_;
-    this.cursor_ += amount;
-    if (this.cursor_ > this.buffer.byteLength)
-      this.resize_(this.cursor_);
-    return offset;
-  }
-
-  /** @private */
-  executeDeferredEncodings_() {
-    let encoding;
-    while (encoding = this.deferredEncodings_.shift()) {
-      this.alignAndGrowToCursor_(8);
-      const relativeOffset = this.cursor_ - encoding.pointerOffset;
-      mojo.internal.setUint64(
-          this.data, encoding.pointerOffset, relativeOffset);
-      encoding.execute();
-    }
-  }
-
-  appendNullOffset() {
-    // New bytes are already zero-initialized. This is a fast-path since
-    // encoding null pointers is much more common than encoding actual 64-bit
-    // integer values.
-    this.advanceCursor_(8);
-  }
-
-  appendBool(value) {
-    if (this.cursor_ === this.buffer.byteLength)
-      this.grow_(1);
-    const oldValue = this.data.getUint8(this.cursor_);
+  encodeBool(byteOffset, bitOffset, value) {
+    const oldValue = this.data_.getUint8(byteOffset);
     if (value)
-      this.data.setUint8(this.cursor_, oldValue | (1 << this.bitOffset_));
+      this.data_.setUint8(byteOffset, oldValue | (1 << bitOffset));
     else
-      this.data.setUint8(this.cursor_, oldValue & ~(1 << this.bitOffset_));
-    this.bitOffset_ += 1;
-    if (this.bitOffset_ == 8) {
-      this.bitOffset_ = 0;
-      this.cursor_++;
-    }
+      this.data_.setUint8(byteOffset, oldValue & ~(1 << bitOffset));
   }
 
-  appendInt8(value) {
-    const offset = this.advanceCursor_(1);
-    this.data.setInt8(offset, value);
+  encodeInt8(offset, value) {
+    this.data_.setInt8(offset, value);
   }
 
-  appendUint8(value) {
-    const offset = this.advanceCursor_(1);
-    this.data.setUint8(offset, value);
+  encodeUint8(offset, value) {
+    this.data_.setUint8(offset, value);
   }
 
-  appendInt16(value) {
-    const offset = this.advanceCursor_(2);
-    this.data.setInt16(offset, value, mojo.internal.kHostLittleEndian);
+  encodeInt16(offset, value) {
+    this.data._setInt16(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendUint16(value) {
-    const offset = this.advanceCursor_(1);
-    this.data.setUint16(offset, value, mojo.internal.kHostLittleEndian);
+  encodeUint16(offset, value) {
+    this.data_.setUint16(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendInt32(value) {
-    const offset = this.advanceCursor_(4);
-    this.data.setInt32(offset, value, mojo.internal.kHostLittleEndian);
+  encodeInt32(offset, value) {
+    this.data_.setInt32(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendUint32(value) {
-    const offset = this.advanceCursor_(4);
-    this.data.setUint32(offset, value, mojo.internal.kHostLittleEndian);
+  encodeUint32(offset, value) {
+    this.data_.setUint32(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendInt64(value) {
-    const offset = this.advanceCursor_(8);
-    mojo.internal.setInt64(this.data, offset, value);
+  encodeInt64(offset, value) {
+    mojo.internal.setInt64(this.data_, offset, value);
   }
 
-  appendUint64(value) {
-    const offset = this.advanceCursor_(8);
-    mojo.internal.setUint64(this.data, offset, value);
+  encodeUint64(offset, value) {
+    mojo.internal.setUint64(this.data_, offset, value);
   }
 
-  appendFloat(value) {
-    const offset = this.advanceCursor_(4);
-    this.data.setFloat32(offset, value, mojo.internal.kHostLittleEndian);
+  encodeFloat(offset, value) {
+    this.data_.setFloat32(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendDouble(value) {
-    const offset = this.advanceCursor_(8);
-    this.data.setFloat64(offset, value, mojo.internal.kHostLittleEndian);
+  encodeDouble(offset, value) {
+    this.data_.setFloat64(offset, value, mojo.internal.kHostLittleEndian);
   }
 
-  appendHandle(value) {
-    this.appendUint32(this.handles.length);
-    this.handles.push(value);
+  encodeHandle(offset, value) {
+    this.encodeUint32(offset, this.message_.handles.length);
+    this.message_.handles.push(value);
   }
 
-  appendString(value) {
+  encodeString(offset, value) {
     if (!mojo.internal.Message.textEncoder)
       mojo.internal.Message.textEncoder = new TextEncoder('utf-8');
-    this.appendArray(
-        {elementType: mojo['mojom']['Uint8']},
+    if (typeof value !== 'string')
+      throw new Error('Unxpected non-string value for string field.');
+    this.encodeArray(
+        {elementType: mojo['mojom']['Uint8']}, offset,
         mojo.internal.Message.textEncoder.encode(value));
   }
 
-  deferEncoding(encoder) {
-    this.deferredEncodings_.push({
-      pointerOffset: this.advanceCursor_(8),
-      execute: encoder,
-    });
+  encodeOffset(offset, absoluteOffset) {
+    this.encodeUint64(offset, absoluteOffset - this.data_.byteOffset - offset);
   }
 
   /**
    * @param {!mojo.internal.ArraySpec} arraySpec
+   * @param {number} offset
    * @param {*} value
    */
-  appendArray(arraySpec, value) {
-    this.deferEncoding(this.appendArrayInline.bind(this, arraySpec, value));
-  }
+  encodeArray(arraySpec, offset, value) {
+    const arraySize = mojo.internal.computeArraySize(arraySpec, value.length);
+    const arrayData = this.message_.allocate(arraySize);
+    const arrayEncoder = new mojo.internal.Encoder(this.message_, arrayData);
+    this.encodeOffset(offset, arrayData.byteOffset);
 
-  /**
-   * @param {!mojo.internal.ArraySpec} arraySpec
-   * @param {*} value
-   */
-  appendArrayInline(arraySpec, value) {
-    let size;
+    arrayEncoder.encodeUint32(0, arraySize);
+    arrayEncoder.encodeUint32(4, value.length);
+
+    let byteOffset = 8;
     if (arraySpec.elementType === mojo.mojom.Bool) {
-      size = mojo.internal.kArrayHeaderSize + (value.length + 7) >> 3;
-    } else {
-      size = mojo.internal.kArrayHeaderSize +
-          value.length * arraySpec.elementType.$.arrayElementSize;
-    }
-
-    const header = this.advanceCursor_(8);
-    this.data.setUint32(header, size, mojo.internal.kHostLittleEndian);
-    this.data.setUint32(
-        header + 4, value.length, mojo.internal.kHostLittleEndian);
-
-    if (arraySpec.elementType === mojo.mojom.Bool) {
-      let bit = 0;
-      let index = header + 8;
-      let byte = 0;
+      let bitOffset = 0;
       for (const e of value) {
-        if (bit == 8) {
-          this.data.setUint8(index, byte);
-          bit = 0;
-          byte = 0;
-          index++;
+        arrayEncoder.encodeBool(byteOffset, bitOffset, e);
+        bitOffset++;
+        if (bitOffset == 8) {
+          bitOffset = 0;
+          byteOffset++;
         }
-        if (e)
-          byte += (1 << bit);
-        bit++;
       }
-      this.data.setUint8(index, byte);
-      this.alignAndGrowToCursor_(8);
-      return;
-    }
-
-    for (const e of value) {
-      if (e === null) {
-        if (!arraySpec.elementNullable) {
-          throw new Error(
-              'Trying to send a null element in an array of ' +
-              'non-nullable elements');
+    } else {
+      for (const e of value) {
+        if (e === null) {
+          if (!arraySpec.elementNullable) {
+            throw new Error(
+                'Trying to send a null element in an array of ' +
+                'non-nullable elements');
+          }
+          arraySpec.elementType.$.encodeNull(arrayEncoder, byteOffset);
         }
-        arraySpec.elementType.$.encodeNull();
+        arraySpec.elementType.$.encode(e, arrayEncoder, byteOffset, 0);
+        byteOffset += arraySpec.elementType.$.arrayElementSize;
       }
-      arraySpec.elementType.$.encode(e, this);
     }
-    this.alignAndGrowToCursor_(8);
   }
 
   /**
    * @param {!mojo.internal.MapSpec} mapSpec
+   * @param {number} offset
    * @param {*} value
    */
-  appendMap(mapSpec, value) {
-    this.deferEncoding(this.appendMapInline.bind(this, mapSpec, value));
-  }
-
-  /**
-   * @param {!mojo.internal.MapSpec} mapSpec
-   * @param {*} value
-   */
-  appendMapInline(mapSpec, value) {
+  encodeMap(mapSpec, offset, value) {
     let keys, values;
     if (value instanceof Map) {
       keys = Array.from(value.keys());
@@ -1024,35 +958,45 @@
       values = keys.map(k => value[k]);
     }
 
-    const header = this.advanceCursor_(mojo.internal.kStructHeaderSize, 8);
-    this.data.setUint32(
-        header, mojo.internal.kMapDataSize, mojo.internal.kHostLittleEndian);
-    this.data.setUint32(header + 4, 0);
-    this.appendArray({elementType: mapSpec.keyType}, keys);
-    this.appendArray(
+    const mapData = this.message_.allocate(mojo.internal.kMapDataSize);
+    const mapEncoder = new mojo.internal.Encoder(this.message_, mapData);
+    this.encodeOffset(offset, mapData.byteOffset);
+
+    mapEncoder.encodeUint32(0, mojo.internal.kMapDataSize);
+    mapEncoder.encodeUint32(4, 0);
+    mapEncoder.encodeArray({elementType: mapSpec.keyType}, 8, keys);
+    mapEncoder.encodeArray(
         {
           elementType: mapSpec.valueType,
           elementNullable: mapSpec.valueNullable
         },
-        values);
+        16, values);
+  }
+
+  /**
+   * @param {!mojo.internal.StructSpec} structSpec
+   * @param {number} offset
+   * @param {!Object} value
+   * @return {number} The offset into the message where the struct begins.
+   */
+  encodeStruct(structSpec, offset, value) {
+    const structData = this.message_.allocate(
+        mojo.internal.kStructHeaderSize + structSpec.packedSize);
+    const structEncoder = new mojo.internal.Encoder(this.message_, structData);
+    this.encodeOffset(offset, structData.byteOffset);
+    structEncoder.encodeStructInline(structSpec, value);
   }
 
   /**
    * @param {!mojo.internal.StructSpec} structSpec
    * @param {!Object} value
    */
-  appendStruct(structSpec, value) {
-    this.deferEncoding(this.appendStructInline.bind(this, structSpec, value));
-  }
-
-  /**
-   * @param {!mojo.internal.StructSpec} structSpec
-   * @param {!Object} value
-   */
-  appendStructInline(structSpec, value) {
-    const header = this.advanceCursor_(8);
-
+  encodeStructInline(structSpec, value) {
+    this.encodeUint32(
+        0, mojo.internal.kStructHeaderSize + structSpec.packedSize);
+    this.encodeUint32(4, 0);  // TODO: Support versioning.
     for (const field of structSpec.fields) {
+      const byteOffset = mojo.internal.kStructHeaderSize + field.packedOffset;
       if (!value || !(value instanceof Object) ||
           mojo.internal.isNullOrUndefined(value[field.name])) {
         if (!field.nullable) {
@@ -1060,17 +1004,13 @@
               structSpec.name + ' missing value for non-nullable ' +
               'field "' + field.name + '"');
         }
-        field.type.$.encodeNull(this);
+        field.type.$.encodeNull(this, byteOffset);
         continue;
       }
 
-      field.type.$.encode(value[field.name], this);
+      field.type.$.encode(
+          value[field.name], this, byteOffset, field.packedBitOffset);
     }
-
-    this.alignAndGrowToCursor_(8);
-    this.data.setUint32(
-        header, this.cursor_ - header, mojo.internal.kHostLittleEndian);
-    this.data.setUint32(header + 4, 0);  // TODO: Support versioning.
   }
 };
 
@@ -1086,112 +1026,62 @@
   /**
    * @param {!DataView} data
    * @param {!Array<MojoHandle>} handles
-   * @param {number} cursor
    * @private
    */
-  constructor(data, handles, cursor) {
+  constructor(data, handles) {
     /** @private {!DataView} */
     this.data_ = data;
 
     /** @private {!Array<MojoHandle>} */
     this.handles_ = handles;
-
-    /** @private {number} */
-    this.cursor_ = cursor;
-
-    /** @private {number} */
-    this.bitOffset_ = 0;
-
-    /** @private {number} */
-    this.lastBoolOffset_ = 0;
   }
 
-  /**
-   * @param {number} alignment
-   * @private
-   */
-  alignCursor_(alignment) {
-    if (this.bitOffset_ > 0) {
-      this.cursor_++;
-      this.bitOffset_ = 0;
-    }
-    this.cursor_ = mojo.internal.align(this.cursor_, alignment);
+  decodeBool(byteOffset, bitOffset) {
+    return !!(this.data_.getUint8(byteOffset) & (1 << bitOffset));
   }
 
-  /**
-   * @param {number} amount
-   * @return {number}
-   * @private
-   */
-  alignAndAdvanceCursor_(amount) {
-    this.alignCursor_(amount);
-    const cursor = this.cursor_;
-    this.cursor_ += amount;
-    return cursor;
+  decodeInt8(offset) {
+    return this.data_.getInt8(offset);
   }
 
-  decodeBool() {
-    if (this.cursor_ != this.lastBoolOffset_)
-      this.bitOffset_ = 0;
-    const offset = this.cursor_;
-    const bit = this.bitOffset_++;
-    this.lastBoolOffset_ = offset;
-    if (this.bitOffset_ == 8) {
-      this.cursor_++;
-      this.bitOffset_ = 0;
-    }
-    return !!(this.data_.getUint8(offset) & (1 << bit));
+  decodeUint8(offset) {
+    return this.data_.getUint8(offset);
   }
 
-  decodeInt8() {
-    return this.data_.getInt8(this.cursor_++);
+  decodeInt16(offset) {
+    return this.data_.getInt16(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeUint8() {
-    return this.data_.getUint8(this.cursor_++);
+  decodeUint16(offset) {
+    return this.data_.getUint16(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeInt16() {
-    return this.data_.getInt16(
-        this.alignAndAdvanceCursor_(2), mojo.internal.kHostLittleEndian);
+  decodeInt32(offset) {
+    return this.data_.getInt32(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeUint16() {
-    return this.data_.getUint16(
-        this.alignAndAdvanceCursor_(2), mojo.internal.kHostLittleEndian);
+  decodeUint32(offset) {
+    return this.data_.getUint32(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeInt32() {
-    return this.data_.getInt32(
-        this.alignAndAdvanceCursor_(4), mojo.internal.kHostLittleEndian);
+  decodeInt64(offset) {
+    return mojo.internal.getInt64(this.data_, offset);
   }
 
-  decodeUint32() {
-    return this.data_.getUint32(
-        this.alignAndAdvanceCursor_(4), mojo.internal.kHostLittleEndian);
+  decodeUint64(offset) {
+    return mojo.internal.getUint64(this.data_, offset);
   }
 
-  decodeInt64() {
-    return mojo.internal.getInt64(this.data_, this.alignAndAdvanceCursor_(8));
+  decodeFloat(offset) {
+    return this.data_.getFloat32(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeUint64() {
-    return mojo.internal.getUint64(this.data_, this.alignAndAdvanceCursor_(8));
+  decodeDouble(offset) {
+    return this.data_.getFloat64(offset, mojo.internal.kHostLittleEndian);
   }
 
-  decodeFloat() {
-    return this.data_.getFloat32(
-        this.alignAndAdvanceCursor_(4), mojo.internal.kHostLittleEndian);
-  }
-
-  decodeDouble() {
-    return this.data_.getFloat64(
-        this.alignAndAdvanceCursor_(4), mojo.internal.kHostLittleEndian);
-  }
-
-  decodeHandle() {
-    const index = this.data_.getUint32(
-        this.alignAndAdvanceCursor_(4), mojo.internal.kHostLittleEndian);
+  decodeHandle(offset) {
+    const index = this.data_.getUint32(offset, mojo.internal.kHostLittleEndian);
     if (index == 0xffffffff)
       return null;
     if (index >= this.handles_.length)
@@ -1199,66 +1089,55 @@
     return this.handles_[index];
   }
 
-  decodeString() {
+  decodeString(offset) {
     if (!mojo.internal.Decoder.textDecoder)
       mojo.internal.Decoder.textDecoder = new TextDecoder('utf-8');
     return mojo.internal.Decoder.textDecoder.decode(
-        new Uint8Array(this.decodeArray({
-          elementType: mojo.mojom.Uint8,
-        })).buffer);
+        new Uint8Array(this.decodeArray(
+                           {
+                             elementType: mojo.mojom.Uint8,
+                           },
+                           offset))
+            .buffer);
   }
 
-  decodeOffset() {
-    this.alignCursor_(8);
-    const cursor = this.cursor_;
-    const offset = this.decodeUint64();
-    if (offset == 0)
+  decodeOffset(offset) {
+    const relativeOffset = this.decodeUint64(offset);
+    if (relativeOffset == 0)
       return 0;
-    return cursor + offset;
+    return this.data_.byteOffset + offset + relativeOffset;
   }
 
   /**
    * @param {!mojo.internal.ArraySpec} arraySpec
    * @return {!Array}
    */
-  decodeArray(arraySpec) {
-    const arrayOffset = this.decodeOffset();
+  decodeArray(arraySpec, offset) {
+    const arrayOffset = this.decodeOffset(offset);
     if (!arrayOffset)
       return null;
 
-    const elementDecoder =
-        new mojo.internal.Decoder(this.data_, this.handles_, arrayOffset);
-    return elementDecoder.decodeArrayInline(arraySpec)
-  }
+    const arrayDecoder = new mojo.internal.Decoder(
+        new DataView(this.data_.buffer, arrayOffset), this.handles_);
 
-  /**
-   * @param {!mojo.internal.ArraySpec} arraySpec
-   * @return {!Array}
-   */
-  decodeArrayInline(arraySpec) {
-    const size = this.decodeUint32();
-    const numElements = this.decodeUint32();
+    const size = arrayDecoder.decodeUint32(0);
+    const numElements = arrayDecoder.decodeUint32(4);
     if (!numElements)
       return [];
 
     const result = [];
     if (arraySpec.elementType === mojo.mojom.Bool) {
-      let bit = 8;
-      let byteValue = this.decodeUint8();
-      for (let i = 0; i < numElements; ++i) {
-        if (bit == 8) {
-          bit = 0;
-          byteValue = this.decodeUint8();
-        }
-        result.push(!!(byteValue & (1 << bit)));
-        ++bit;
-      }
+      for (let i = 0; i < numElements; ++i)
+        result.push(arrayDecoder.decodeBool(8 + (i >> 3), i % 8));
     } else {
+      let byteOffset = 8;
       for (let i = 0; i < numElements; ++i) {
-        const element = arraySpec.elementType.$.decode(this);
+        const element =
+            arraySpec.elementType.$.decode(arrayDecoder, byteOffset, 0);
         if (element === null && !arraySpec.elementNullable)
           throw new Error('Received unexpected array element');
         result.push(element);
+        byteOffset += arraySpec.elementType.$.arrayElementSize;
       }
     }
     return result;
@@ -1268,29 +1147,26 @@
    * @param {!mojo.internal.MapSpec} mapSpec
    * @return {!Object|!Map}
    */
-  decodeMap(mapSpec) {
-    const mapOffset = this.decodeOffset();
+  decodeMap(mapSpec, offset) {
+    const mapOffset = this.decodeOffset(offset);
     if (!mapOffset)
       return null;
 
-    const mapStructSize =
-        this.data_.getUint32(mapOffset, mojo.internal.kHostLittleEndian);
-    const mapStructVersion =
-        this.data_.getUint32(mapOffset + 4, mojo.internal.kHostLittleEndian);
+    const mapDecoder = new mojo.internal.Decoder(
+        new DataView(this.data_.buffer, mapOffset), this.handles_);
+    const mapStructSize = mapDecoder.decodeUint32(0);
+    const mapStructVersion = mapDecoder.decodeUint32(4);
     if (mapStructSize != mojo.internal.kMapDataSize || mapStructVersion != 0)
       throw new Error('Received invalid map data');
 
-    const keysDecoder = new mojo.internal.Decoder(
-        this.data_, this.handles_,
-        mojo.internal.getUint64(this.data_, mapOffset + 8) + mapOffset + 8);
-    const valuesDecoder = new mojo.internal.Decoder(
-        this.data_, this.handles_,
-        mojo.internal.getUint64(this.data_, mapOffset + 16) + mapOffset + 16);
-    const keys = keysDecoder.decodeArray({elementType: mapSpec.keyType});
-    const values = valuesDecoder.decodeArray({
-      elementType: mapSpec.valueType,
-      elementNullable: mapSpec.valueNullable
-    });
+    const keys = mapDecoder.decodeArray({elementType: mapSpec.keyType}, 8);
+    const values = mapDecoder.decodeArray(
+        {
+          elementType: mapSpec.valueType,
+          elementNullable: mapSpec.valueNullable
+        },
+        16);
+
     if (keys.length != values.length)
       throw new Error('Received invalid map data');
     if (!mapSpec.keyType.$.isValidObjectKeyType) {
@@ -1310,13 +1186,13 @@
    * @param {!mojo.internal.StructSpec} structSpec
    * @return {Object}
    */
-  decodeStruct(structSpec) {
-    const structOffset = this.decodeOffset();
+  decodeStruct(structSpec, offset) {
+    const structOffset = this.decodeOffset(offset);
     if (!structOffset)
       return null;
 
-    const decoder =
-        new mojo.internal.Decoder(this.data_, this.handles_, structOffset);
+    const decoder = new mojo.internal.Decoder(
+        new DataView(this.data_.buffer, structOffset), this.handles_);
     return decoder.decodeStructInline(structSpec);
   }
 
@@ -1325,11 +1201,13 @@
    * @return {!Object}
    */
   decodeStructInline(structSpec) {
-    const size = this.decodeUint32();
-    const version = this.decodeUint32();
+    const size = this.decodeUint32(0);
+    const version = this.decodeUint32(4);
     const result = {};
     for (const field of structSpec.fields) {
-      const value = field.type.$.decode(this);
+      const byteOffset = mojo.internal.kStructHeaderSize + field.packedOffset;
+      const value =
+          field.type.$.decode(this, byteOffset, field.packedBitOffset);
       if (value === null && !field.nullable) {
         throw new Error(
             'Received ' + structSpec.name + ' with invalid null field ' +
@@ -1340,16 +1218,16 @@
     return result;
   }
 
-  decodeInterfaceProxy(type) {
-    const handle = this.decodeHandle();
-    const version = this.decodeUint32();  // TODO: support versioning
+  decodeInterfaceProxy(type, offset) {
+    const handle = this.decodeHandle(offset);
+    const version = this.decodeUint32(offset + 4);  // TODO: support versioning
     if (!handle)
       return null;
     return new type(handle);
   }
 
-  decodeInterfaceRequest(type) {
-    const handle = this.decodeHandle();
+  decodeInterfaceRequest(type, offset) {
+    const handle = this.decodeHandle(offset);
     if (!handle)
       return null;
     return new type(handle);
@@ -1369,11 +1247,10 @@
  */
 mojo.internal.serializeAndSendMessage = function(
     handle, ordinal, requestId, flags, paramStruct, value) {
-  const message = new mojo.internal.Message(flags, ordinal, requestId);
-  message.appendStructInline(
+  const message = new mojo.internal.Message(
+      flags, ordinal, requestId,
       /* @type {!mojo.internal.StructSpec} */ (paramStruct.$.structSpec),
       value);
-  message.executeDeferredEncodings_();
   handle.writeMessage(message.buffer, message.handles);
 };
 
@@ -1412,8 +1289,8 @@
 
 /**
  * @typedef {{
- *   encode: function(*, !mojo.internal.Message),
- *   decode: function(!mojo.internal.Decoder):*,
+ *   encode: function(*, !mojo.internal.Encoder, number, number),
+ *   decode: function(!mojo.internal.Decoder, number, number):*,
  *   isValidObjectKeyType: boolean,
  *   arrayElementSize: (number|undefined),
  *   arraySpec: (!mojo.internal.ArraySpec|undefined),
@@ -1450,6 +1327,8 @@
 /**
  * @typedef {{
  *   name: string,
+ *   packedOffset: number,
+ *   packedBitOffset: number,
  *   type: !mojo.internal.MojomType,
  *   defaultValue: *,
  *   nullable: boolean,
@@ -1459,7 +1338,8 @@
 
 /**
  * @typedef {{
- *   type: !mojo.internal.MojomType,
+ *   name: string,
+ *   packedSize: number,
  *   fields: !Array<!mojo.internal.StructFieldSpec>,
  * }}
  */
@@ -1481,10 +1361,12 @@
  */
 mojo.mojom.Bool = {
   $: {
-    encode: function(value, message) {
-      message.appendBool(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeBool(byteOffset, bitOffset, value);
     },
-    decode: decoder => decoder.decodeBool(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeBool(byteOffset, bitOffset);
+    },
     isValidObjectKeyType: true,
   },
 };
@@ -1495,10 +1377,12 @@
  */
 mojo.mojom.Int8 = {
   $: {
-    encode: function(value, message) {
-      message.appendInt8(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeInt8(byteOffset, value);
     },
-    decode: decoder => decoder.decodeInt8(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeInt8(byteOffset);
+    },
     arrayElementSize: 1,
     isValidObjectKeyType: true,
   },
@@ -1510,10 +1394,12 @@
  */
 mojo.mojom.Uint8 = {
   $: {
-    encode: function(value, message) {
-      message.appendUint8(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeUint8(byteOffset, value);
     },
-    decode: decoder => decoder.decodeUint8(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeUint8(byteOffset);
+    },
     arrayElementSize: 1,
     isValidObjectKeyType: true,
   },
@@ -1525,10 +1411,12 @@
  */
 mojo.mojom.Int16 = {
   $: {
-    encode: function(value, message) {
-      message.appendInt16(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeInt16(byteOffset, value);
     },
-    decode: decoder => decoder.decodeInt16(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeInt16(byteOffset);
+    },
     arrayElementSize: 2,
     isValidObjectKeyType: true,
   },
@@ -1540,10 +1428,12 @@
  */
 mojo.mojom.Uint16 = {
   $: {
-    encode: function(value, message) {
-      message.appendUint16(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeUint16(byteOffset, value);
     },
-    decode: decoder => decoder.decodeUint16(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeUint16(byteOffset);
+    },
     arrayElementSize: 2,
     isValidObjectKeyType: true,
   },
@@ -1555,10 +1445,12 @@
  */
 mojo.mojom.Int32 = {
   $: {
-    encode: function(value, message) {
-      message.appendInt32(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeInt32(byteOffset, value);
     },
-    decode: decoder => decoder.decodeInt32(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeInt32(byteOffset);
+    },
     arrayElementSize: 4,
     isValidObjectKeyType: true,
   },
@@ -1570,10 +1462,12 @@
  */
 mojo.mojom.Uint32 = {
   $: {
-    encode: function(value, message) {
-      message.appendUint32(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeUint32(byteOffset, value);
     },
-    decode: decoder => decoder.decodeUint32(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeUint32(byteOffset);
+    },
     arrayElementSize: 4,
     isValidObjectKeyType: true,
   },
@@ -1585,10 +1479,12 @@
  */
 mojo.mojom.Int64 = {
   $: {
-    encode: function(value, message) {
-      message.appendInt64(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeInt64(byteOffset, value);
     },
-    decode: decoder => decoder.decodeInt64(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeInt64(byteOffset);
+    },
     arrayElementSize: 8,
     isValidObjectKeyType: true,
   },
@@ -1600,10 +1496,12 @@
  */
 mojo.mojom.Uint64 = {
   $: {
-    encode: function(value, message) {
-      message.appendUint64(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeUint64(byteOffset, value);
     },
-    decode: decoder => decoder.decodeUint64(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeUint64(byteOffset);
+    },
     arrayElementSize: 8,
     isValidObjectKeyType: true,
   },
@@ -1615,10 +1513,12 @@
  */
 mojo.mojom.Float = {
   $: {
-    encode: function(value, message) {
-      message.appendFloat(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeFloat(byteOffset, value);
     },
-    decode: decoder => decoder.decodeFloat(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeFloat(byteOffset);
+    },
     arrayElementSize: 4,
     isValidObjectKeyType: true,
   },
@@ -1630,10 +1530,12 @@
  */
 mojo.mojom.Double = {
   $: {
-    encode: function(value, message) {
-      message.appendDouble(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeDouble(byteOffset, value);
     },
-    decode: decoder => decoder.decodeDouble(),
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeDouble(byteOffset);
+    },
     arrayElementSize: 8,
     isValidObjectKeyType: true,
   },
@@ -1645,13 +1547,13 @@
  */
 mojo.mojom.Handle = {
   $: {
-    encode: function(value, message) {
-      message.appendHandle(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeHandle(byteOffset, value);
     },
-    encodeNull: function(message) {
-      message.appendUint32(0);
+    encodeNull: function(encoder, byteOffset) {},
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeHandle(byteOffset);
     },
-    decode: decoder => decoder.decodeHandle(),
     arrayElementSize: 4,
     isValidObjectKeyType: false,
   },
@@ -1663,13 +1565,17 @@
  */
 mojo.mojom.String = {
   $: {
-    encode: function(value, message) {
-      message.appendString(value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeString(byteOffset, value);
     },
-    encodeNull: function(message) {
-      message.appendNullOffset();
+    encodeNull: function(encoder, byteOffset) {},
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeString(byteOffset);
     },
-    decode: decoder => decoder.decodeString(),
+    computePayloadSize: function(value) {
+      return mojo.internal.computeArraySize(
+          {elementType: mojo.mojom.Uint8}, value.length);
+    },
     arrayElementSize: 8,
     isValidObjectKeyType: true,
   }
@@ -1690,13 +1596,16 @@
   return {
     $: {
       arraySpec: arraySpec,
-      encode: function(value, message) {
-        message.appendArray(arraySpec, value);
+      encode: function(value, encoder, byteOffset, bitOffset) {
+        encoder.encodeArray(arraySpec, byteOffset, value);
       },
-      encodeNull: function(message) {
-        message.appendNullOffset();
+      encodeNull: function(encoder, byteOffset) {},
+      decode: function(decoder, byteOffset, bitOffset) {
+        return decoder.decodeArray(arraySpec, byteOffset);
       },
-      decode: decoder => decoder.decodeArray(arraySpec),
+      computePayloadSize: function(value) {
+        return mojo.internal.computeArraySize(arraySpec, value.length);
+      },
       arrayElementSize: 8,
       isValidObjectKeyType: false,
     },
@@ -1720,13 +1629,25 @@
   return {
     $: {
       mapSpec: mapSpec,
-      encode: function(value, message) {
-        message.appendMap(mapSpec, value);
+      encode: function(value, encoder, byteOffset, bitOffset) {
+        encoder.encodeMap(mapSpec, byteOffset, value);
       },
-      encodeNull: function(message) {
-        message.appendNullOffset();
+      encodeNull: function(encoder, byteOffset) {},
+      decode: function(decoder, byteOffset, bitOffset) {
+        return decoder.decodeMap(mapSpec, byteOffset);
       },
-      decode: decoder => decoder.decodeMap(mapSpec),
+      computePayloadSize: function(value) {
+        const numEntries =
+            (value instanceof Map) ? value.size : Object.keys(value).length;
+        return mojo.internal.kMapDataSize +
+            mojo.internal.computeArraySize({elementType: keyType}, numEntries) +
+            mojo.internal.computeArraySize(
+                {
+                  elementType: valueType,
+                  elementNullable: valueNullable,
+                },
+                numEntries);
+      },
       arrayElementSize: 8,
       isValidObjectKeyType: false,
     },
@@ -1741,12 +1662,12 @@
 mojo.mojom.Enum = function(properties) {
   return {
     $: {
-      encode: function(value, message) {
+      encode: function(value, encoder, byteOffset, bitOffset) {
         // TODO: Do some sender-side error checking on the input value.
-        message.appendUint32(value);
+        encoder.encodeUint32(byteOffset, value);
       },
-      decode: decoder => {
-        const value = decoder.decodeInt32();
+      decode: function(decoder, byteOffset, bitOffset) {
+        const value = decoder.decodeInt32(byteOffset);
         // TODO: validate
         return value;
       },
@@ -1758,15 +1679,20 @@
 
 /**
  * @param {string} name
+ * @param {number} packedOffset
+ * @param {number} packedBitOffset
  * @param {!mojo.internal.MojomType} type
  * @param {*} defaultValue
  * @param {boolean} nullable
  * @return {!mojo.mojom.StructFieldSpec}
  * @export
  */
-mojo.mojom.StructField = function(name, type, defaultValue, nullable) {
+mojo.mojom.StructField = function(
+    name, packedOffset, packedBitOffset, type, defaultValue, nullable) {
   return {
     name: name,
+    packedOffset: packedOffset,
+    packedBitOffset: packedBitOffset,
     type: type,
     defaultValue: defaultValue,
     nullable: nullable,
@@ -1776,24 +1702,29 @@
 /**
  * @param {!Object} objectToBlessAsType
  * @param {string} name
+ * @param {number} packedSize
  * @param {!Array<!mojo.internal.StructFieldSpec>} fields
  * @export
  */
-mojo.mojom.Struct = function(objectToBlessAsType, name, fields) {
+mojo.mojom.Struct = function(objectToBlessAsType, name, packedSize, fields) {
   /** @type {!mojo.internal.StructSpec} */
   const structSpec = {
     name: name,
+    packedSize: packedSize,
     fields: fields,
   };
   objectToBlessAsType.$ = {
     structSpec: structSpec,
-    encode: function(value, message) {
-      message.appendStruct(structSpec, value);
+    encode: function(value, encoder, byteOffset, bitOffset) {
+      encoder.encodeStruct(structSpec, byteOffset, value);
     },
-    encodeNull: function(message) {
-      message.appendNullOffset();
+    encodeNull: function(encoder, byteOffset) {},
+    decode: function(decoder, byteOffset, bitOffset) {
+      return decoder.decodeStruct(structSpec, byteOffset);
     },
-    decode: decoder => decoder.decodeStruct(structSpec),
+    computePayloadSize: function(value) {
+      return mojo.internal.computeTotalStructSize(structSpec, value);
+    },
     arrayElementSize: 8,
     isValidObjectKeyType: false,
   };
@@ -1808,23 +1739,26 @@
     $: {
       /**
        * @param {!{proxy: mojo.internal.InterfaceProxyBase}} value
-       * @param {!mojo.internal.Message} message
+       * @param {!mojo.internal.Encoder} encoder
+       * @param {number} byteOffset
+       * @param {number} bitOffset
        */
-      encode: function(value, message) {
+      encode: function(value, encoder, byteOffset, bitOffset) {
         if (!(value instanceof type))
           throw new Error('Invalid proxy type. Expected ' + type.name);
         if (!value.proxy.handle)
           throw new Error('Unexpected null ' + type.name);
 
-        message.appendHandle(value.proxy.handle);
-        message.appendUint32(0);  // TODO: Support versioning
+        encoder.encodeHandle(byteOffset, value.proxy.handle);
+        encoder.encodeUint32(byteOffset + 4, 0);  // TODO: Support versioning
         value.proxy.unbind();
       },
-      encodeNull: function(message) {
-        message.appendUint32(0xffffffff);
-        message.appendUint32(0);
+      encodeNull: function(encoder, byteOffset) {
+        encoder.encodeUint32(byteOffset, 0xffffffff);
       },
-      decode: decoder => decoder.decodeInterfaceProxy(type),
+      decode: function(decoder, byteOffset, bitOffset) {
+        return decoder.decodeInterfaceProxy(type, byteOffset);
+      },
       arrayElementSize: 8,
       isValidObjectKeyType: false,
     },
@@ -1838,18 +1772,19 @@
 mojo.mojom.InterfaceRequest = function(type) {
   return {
     $: {
-      encode: function(value, message) {
+      encode: function(value, encoder, byteOffset, bitOffset) {
         if (!(value instanceof type))
           throw new Error('Invalid request type. Expected ' + type.name);
         if (!value.handle)
           throw new Error('Unexpected null ' + type.name);
-
-        message.appendHandle(value.handle);
+        encoder.encodeHandle(byteOffset, value.handle);
       },
-      encodeNull: function(message) {
-        message.appendUint32(0xffffffff);
+      encodeNull: function(encoder, byteOffset) {
+        encoder.encodeUint32(byteOffset, 0xffffffff);
       },
-      decode: decoder => decoder.decodeInterfaceRequest(type),
+      decode: function(decoder, byteOffset, bitOffset) {
+        return decoder.decodeInterfaceRequest(type, byteOffset);
+      },
       arrayElementSize: 4,
       isValidObjectKeyType: false,
     },
@@ -1864,10 +1799,10 @@
   return {
     $: {
       type: type,
-      encode: function(value, message) {
+      encode: function(value, encoder, byteOffset, bitOffset) {
         throw new Error('Associated interfaces not supported yet.');
       },
-      decode: decoder => {
+      decode: function(decoder, byteOffset, bitOffset) {
         throw new Error('Associated interfaces not supported yet.');
       },
     },
@@ -1882,10 +1817,10 @@
   return {
     $: {
       type: type,
-      encode: function(value, message) {
+      encode: function(value, encoder, byteOffset, bitOffset) {
         throw new Error('Associated interfaces not supported yet.');
       },
-      decode: decoder => {
+      decode: function(decoder, byteOffset, bitOffset) {
         throw new Error('Associated interfaces not supported yet.');
       },
     },
diff --git a/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl
index b45a250..71bfd2a 100644
--- a/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/lite/struct_definition.tmpl
@@ -2,10 +2,13 @@
 mojo.mojom.Struct(
     {{struct.name}},
     '{{struct.name}}',
+    {{struct.packed|payload_size}},
     [
-{%- for packed_field in struct.packed.packed_fields %}
+{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
       mojo.mojom.StructField(
-        '{{packed_field.field.name}}',
+        '{{packed_field.field.name}}', {{packed_field.offset}},
+        {% if packed_field.field.kind|is_bool_kind %}{{packed_field.bit}}
+        {%- else %}0{% endif %},
         {{packed_field.field.kind|lite_js_type}},
         {{packed_field.field|lite_default_value}},
 {%-   if packed_field.field.kind.is_nullable %}
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 486c984e..8e9aa787 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -103,7 +103,6 @@
 
 source_set("constants") {
   sources = [
-    "base/trace_constants.cc",
     "base/trace_constants.h",
   ]
   deps = [
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
index 34f4fc1..5de91d2 100644
--- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
+++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -4,10 +4,12 @@
 
 package org.chromium.net;
 
+import android.Manifest;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -19,6 +21,7 @@
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.security.NetworkSecurityPolicy;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -65,6 +68,8 @@
     private static Method sIsPrivateDnsActiveMethod;
     // Cached Method for LinkProperties.getPrivateDnsServerName().
     private static Method sGetPrivateDnsServerNameMethod;
+    // Cached value indicating if app has ACCESS_NETWORK_STATE permission.
+    private static Boolean sHaveAccessNetworkState;
 
     // Set of public DNS servers supporting DNS-over-HTTPS.
     private static final Set<InetAddress> sAutoDohServers = new HashSet<>();
@@ -374,6 +379,19 @@
         return null;
     }
 
+    private static boolean haveAccessNetworkState() {
+        // This could be racy if called on multiple threads, but races will
+        // end in the same result so it's not a problem.
+        if (sHaveAccessNetworkState == null) {
+            sHaveAccessNetworkState =
+                    Boolean.valueOf(ContextUtils.getApplicationContext().checkPermission(
+                                            Manifest.permission.ACCESS_NETWORK_STATE,
+                                            Process.myPid(), Process.myUid())
+                            == PackageManager.PERMISSION_GRANTED);
+        }
+        return sHaveAccessNetworkState;
+    }
+
     /**
      * Returns list of IP addresses of DNS servers.
      * If private DNS is active, then returns a 1x1 array.
@@ -381,6 +399,9 @@
     @TargetApi(Build.VERSION_CODES.M)
     @CalledByNative
     private static byte[][] getDnsServers() {
+        if (!haveAccessNetworkState()) {
+            return new byte[0][0];
+        }
         ConnectivityManager connectivityManager =
                 (ConnectivityManager) ContextUtils.getApplicationContext().getSystemService(
                         Context.CONNECTIVITY_SERVICE);
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index ef812bb..5f8f4bc7 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -23,7 +23,7 @@
 int NetworkDelegate::NotifyBeforeURLRequest(URLRequest* request,
                                             CompletionOnceCallback callback,
                                             GURL* new_url) {
-  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyBeforeURLRequest");
+  TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::NotifyBeforeURLRequest");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(request);
   DCHECK(!callback.is_null());
@@ -37,7 +37,7 @@
     URLRequest* request,
     CompletionOnceCallback callback,
     HttpRequestHeaders* headers) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "NetworkDelegate::NotifyBeforeStartTransation");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(headers);
@@ -58,7 +58,7 @@
 void NetworkDelegate::NotifyStartTransaction(
     URLRequest* request,
     const HttpRequestHeaders& headers) {
-  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyStartTransaction");
+  TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::NotifyStartTransaction");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   OnStartTransaction(request, headers);
 }
@@ -69,7 +69,7 @@
     const HttpResponseHeaders* original_response_headers,
     scoped_refptr<HttpResponseHeaders>* override_response_headers,
     GURL* allowed_unsafe_redirect_url) {
-  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyHeadersReceived");
+  TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::NotifyHeadersReceived");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(original_response_headers);
   DCHECK(!callback.is_null());
@@ -88,7 +88,7 @@
 
 void NetworkDelegate::NotifyNetworkBytesReceived(URLRequest* request,
                                                  int64_t bytes_received) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "NetworkDelegate::NotifyNetworkBytesReceived");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_GT(bytes_received, 0);
@@ -112,14 +112,14 @@
 void NetworkDelegate::NotifyCompleted(URLRequest* request,
                                       bool started,
                                       int net_error) {
-  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyCompleted");
+  TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::NotifyCompleted");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(request);
   OnCompleted(request, started, net_error);
 }
 
 void NetworkDelegate::NotifyURLRequestDestroyed(URLRequest* request) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "NetworkDelegate::NotifyURLRequestDestroyed");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(request);
@@ -167,7 +167,7 @@
 
 bool NetworkDelegate::CanEnablePrivacyMode(const GURL& url,
                                            const GURL& site_for_cookies) const {
-  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::CanEnablePrivacyMode");
+  TRACE_EVENT0(NetTracingCategory(), "NetworkDelegate::CanEnablePrivacyMode");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return OnCanEnablePrivacyMode(url, site_for_cookies);
 }
diff --git a/net/base/trace_constants.cc b/net/base/trace_constants.cc
deleted file mode 100644
index ceade2c..0000000
--- a/net/base/trace_constants.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/trace_constants.h"
-
-#include "base/trace_event/common/trace_event_common.h"
-
-namespace net {
-
-const char kNetTracingCategory[] = TRACE_DISABLED_BY_DEFAULT("net");
-
-}  // namespace net
diff --git a/net/base/trace_constants.h b/net/base/trace_constants.h
index dc5d84aa..e57b561 100644
--- a/net/base/trace_constants.h
+++ b/net/base/trace_constants.h
@@ -5,10 +5,16 @@
 #ifndef NET_BASE_TRACE_CONSTANTS_H_
 #define NET_BASE_TRACE_CONSTANTS_H_
 
+#include "base/trace_event/common/trace_event_common.h"
+
 namespace net {
 
 // Net Category used in Tracing.
-extern const char kNetTracingCategory[];
+constexpr const char* NetTracingCategory() {
+  // Declared as a constexpr function to have an external linkage and to be
+  // known at compile-time.
+  return TRACE_DISABLED_BY_DEFAULT("net");
+}
 
 }  // namespace net
 
diff --git a/net/cert/crl_set.cc b/net/cert/crl_set.cc
index aa07edc1..785f6ab1 100644
--- a/net/cert/crl_set.cc
+++ b/net/cert/crl_set.cc
@@ -191,7 +191,7 @@
 
 // static
 bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
-  TRACE_EVENT0(kNetTracingCategory, "CRLSet::Parse");
+  TRACE_EVENT0(NetTracingCategory(), "CRLSet::Parse");
 // Other parts of Chrome assume that we're little endian, so we don't lose
 // anything by doing this.
 #if defined(__BYTE_ORDER)
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 7154358..48a7861 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -202,7 +202,7 @@
     int flags,
     const scoped_refptr<CRLSet>& crl_set,
     const CertificateList& additional_trust_anchors) {
-  TRACE_EVENT0(kNetTracingCategory, "DoVerifyOnWorkerThread");
+  TRACE_EVENT0(NetTracingCategory(), "DoVerifyOnWorkerThread");
   auto verify_result = std::make_unique<ResultHelper>();
   MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
       allow_base_sync_primitives;
@@ -312,7 +312,7 @@
 
   void OnJobCompleted(uint32_t config_id,
                       std::unique_ptr<ResultHelper> verify_result) {
-    TRACE_EVENT0(kNetTracingCategory, "CertVerifierJob::OnJobCompleted");
+    TRACE_EVENT0(NetTracingCategory(), "CertVerifierJob::OnJobCompleted");
     std::unique_ptr<CertVerifierJob> keep_alive =
         cert_verifier_->RemoveJob(this);
 
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index e377c616..6807320 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -215,7 +215,7 @@
                     const Entry& entry,
                     base::TimeTicks now,
                     base::TimeDelta ttl) {
-  TRACE_EVENT0(kNetTracingCategory, "HostCache::Set");
+  TRACE_EVENT0(NetTracingCategory(), "HostCache::Set");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (caching_is_disabled())
     return;
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 201378766..818c55f 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -903,7 +903,7 @@
                                       const AddressList& results,
                                       int error,
                                       const int os_error) {
-    TRACE_EVENT0(kNetTracingCategory, "ProcTask::OnLookupComplete");
+    TRACE_EVENT0(NetTracingCategory(), "ProcTask::OnLookupComplete");
 
     // If results are empty, we should return an error.
     bool empty_list_on_ok = (error == OK && results.empty());
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 170f026..5a4331d 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -1527,7 +1527,7 @@
 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetchComplete(
     int result) {
   TRACE_EVENT0(
-      kNetTracingCategory,
+      NetTracingCategory(),
       "HttpCacheTransaction::DoCacheToggleUnusedSincePrefetchComplete");
   // Restore the original value for this transaction.
   response_.unused_since_prefetch = !response_.unused_since_prefetch;
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index b3a4bb79..f703740a 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -562,12 +562,12 @@
 }
 
 void HttpStreamFactory::Job::OnIOComplete(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "HttpStreamFactory::Job::OnIOComplete");
+  TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::OnIOComplete");
   RunLoop(result);
 }
 
 void HttpStreamFactory::Job::RunLoop(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "HttpStreamFactory::Job::RunLoop");
+  TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::RunLoop");
   result = DoLoop(result);
 
   if (result == ERR_IO_PENDING)
diff --git a/net/log/trace_net_log_observer.cc b/net/log/trace_net_log_observer.cc
index ed3286f..5129deb5 100644
--- a/net/log/trace_net_log_observer.cc
+++ b/net/log/trace_net_log_observer.cc
@@ -22,7 +22,7 @@
 namespace {
 
 // TraceLog category for NetLog events.
-const char kNetLogTracingCategory[] = "netlog";
+constexpr const char kNetLogTracingCategory[] = "netlog";
 
 class TracedValue : public base::trace_event::ConvertableToTraceFormat {
  public:
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index eac99e2..9b28d1e 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -440,7 +440,7 @@
 }
 
 void NetworkQualityEstimator::NotifyHeadersReceived(const URLRequest& request) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "NetworkQualityEstimator::NotifyHeadersReceived");
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -578,7 +578,7 @@
 
 void NetworkQualityEstimator::NotifyRequestCompleted(const URLRequest& request,
                                                      int net_error) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "NetworkQualityEstimator::NotifyRequestCompleted");
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/net/proxy_resolution/proxy_resolver_v8_tracing.cc b/net/proxy_resolution/proxy_resolver_v8_tracing.cc
index 844f3c5..eb1b6a8d 100644
--- a/net/proxy_resolution/proxy_resolver_v8_tracing.cc
+++ b/net/proxy_resolution/proxy_resolver_v8_tracing.cc
@@ -560,7 +560,7 @@
 }
 
 int Job::ExecuteProxyResolver() {
-  TRACE_EVENT0(kNetTracingCategory, "Job::ExecuteProxyResolver");
+  TRACE_EVENT0(NetTracingCategory(), "Job::ExecuteProxyResolver");
   int result = ERR_UNEXPECTED;  // Initialized to silence warnings.
 
   switch (operation_) {
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index cb190cd..f720bee 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -501,7 +501,7 @@
 }
 
 int QuicStreamFactory::Job::DoLoop(int rv) {
-  TRACE_EVENT0(kNetTracingCategory, "QuicStreamFactory::Job::DoLoop");
+  TRACE_EVENT0(NetTracingCategory(), "QuicStreamFactory::Job::DoLoop");
 
   do {
     IoState state = io_state_;
@@ -1664,7 +1664,7 @@
     const NetLogWithSource& net_log,
     QuicChromiumClientSession** session,
     NetworkChangeNotifier::NetworkHandle* network) {
-  TRACE_EVENT0(kNetTracingCategory, "QuicStreamFactory::CreateSession");
+  TRACE_EVENT0(NetTracingCategory(), "QuicStreamFactory::CreateSession");
   IPEndPoint addr = *address_list.begin();
   const quic::QuicServerId& server_id = key.server_id();
   std::unique_ptr<DatagramClientSocket> socket(
diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc
index 281ab3b..0186008 100644
--- a/net/socket/client_socket_handle.cc
+++ b/net/socket/client_socket_handle.cc
@@ -159,7 +159,7 @@
 }
 
 void ClientSocketHandle::OnIOComplete(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "ClientSocketHandle::OnIOComplete");
+  TRACE_EVENT0(NetTracingCategory(), "ClientSocketHandle::OnIOComplete");
   CompletionOnceCallback callback = std::move(callback_);
   callback_.Reset();
   HandleInitCompletion(result);
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index 3774cb5..61036c6 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -95,7 +95,7 @@
 }
 
 void ConnectJob::NotifyDelegateOfCompletion(int rv) {
-  TRACE_EVENT0(kNetTracingCategory, "ConnectJob::NotifyDelegateOfCompletion");
+  TRACE_EVENT0(NetTracingCategory(), "ConnectJob::NotifyDelegateOfCompletion");
   // The delegate will own |this|.
   Delegate* delegate = delegate_;
   delegate_ = NULL;
diff --git a/net/socket/socket_posix.cc b/net/socket/socket_posix.cc
index b4241bd..a6f0b38 100644
--- a/net/socket/socket_posix.cc
+++ b/net/socket/socket_posix.cc
@@ -455,7 +455,7 @@
 }
 
 void SocketPosix::OnFileCanReadWithoutBlocking(int fd) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "SocketPosix::OnFileCanReadWithoutBlocking");
   if (!accept_callback_.is_null()) {
     AcceptCompleted();
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 37ce6b3..fe71b688 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1219,7 +1219,7 @@
 }
 
 int SSLClientSocketImpl::DoHandshakeLoop(int last_io_result) {
-  TRACE_EVENT0(kNetTracingCategory, "SSLClientSocketImpl::DoHandshakeLoop");
+  TRACE_EVENT0(NetTracingCategory(), "SSLClientSocketImpl::DoHandshakeLoop");
   int rv = last_io_result;
   do {
     // Default to STATE_NONE for next state.
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 83d78bc6..c8d37b4 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -179,7 +179,7 @@
 }
 
 int SSLConnectJob::DoLoop(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "SSLConnectJob::DoLoop");
+  TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoLoop");
   DCHECK_NE(next_state_, STATE_NONE);
 
   int rv = result;
@@ -300,7 +300,7 @@
 }
 
 int SSLConnectJob::DoSSLConnect() {
-  TRACE_EVENT0(kNetTracingCategory, "SSLConnectJob::DoSSLConnect");
+  TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoSSLConnect");
   next_state_ = STATE_SSL_CONNECT_COMPLETE;
 
   // Reset the timeout to just the time allowed for the SSL handshake.
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index f5689ab..e2cc51f 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -256,7 +256,7 @@
 }
 
 int TransportConnectJob::DoResolveHostComplete(int result) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "TransportConnectJob::DoResolveHostComplete");
   connect_timing_.dns_end = base::TimeTicks::Now();
   // Overwrite connection start time, since for connections that do not go
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index b1a5fdb..730fc18 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -694,7 +694,7 @@
 }
 
 void UDPSocketPosix::ReadWatcher::OnFileCanReadWithoutBlocking(int) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "UDPSocketPosix::ReadWatcher::OnFileCanReadWithoutBlocking");
   if (!socket_->read_callback_.is_null())
     socket_->DidCompleteRead();
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
index ea80b8e1..5615da0 100644
--- a/net/socket/websocket_transport_client_socket_pool.cc
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -128,7 +128,7 @@
 }
 
 int WebSocketTransportConnectJob::DoResolveHostComplete(int result) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "WebSocketTransportConnectJob::DoResolveHostComplete");
   connect_timing_.dns_end = base::TimeTicks::Now();
   // Overwrite connection start time, since for connections that do not go
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 5fb90a2..1b83355 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -103,7 +103,7 @@
     bool is_trusted_proxy,
     std::unique_ptr<ClientSocketHandle> connection,
     const NetLogWithSource& net_log) {
-  TRACE_EVENT0(kNetTracingCategory,
+  TRACE_EVENT0(NetTracingCategory(),
                "SpdySessionPool::CreateAvailableSessionFromSocket");
 
   UMA_HISTOGRAM_ENUMERATION(
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 064bb6b..72c8532 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -848,7 +848,7 @@
 }
 
 void URLRequestHttpJob::OnStartCompleted(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "URLRequestHttpJob::OnStartCompleted");
+  TRACE_EVENT0(NetTracingCategory(), "URLRequestHttpJob::OnStartCompleted");
   RecordTimer();
 
   // If the job is done (due to cancellation), can just ignore this
@@ -942,7 +942,7 @@
 }
 
 void URLRequestHttpJob::OnReadCompleted(int result) {
-  TRACE_EVENT0(kNetTracingCategory, "URLRequestHttpJob::OnReadCompleted");
+  TRACE_EVENT0(NetTracingCategory(), "URLRequestHttpJob::OnReadCompleted");
   read_in_progress_ = false;
 
   DCHECK_NE(ERR_IO_PENDING, result);
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index eccebb6f..91d1c8dd 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -33,6 +33,7 @@
   jumbo_static_library("pdf") {
     configs += [ ":pdf_common_config" ]
     deps = [
+      ":features",
       "//base",
       "//gin",
       "//net",
@@ -112,6 +113,22 @@
     }
   }
 
+  source_set("features") {
+    configs += [ ":pdf_common_config" ]
+    deps = [
+      "//base",
+    ]
+
+    public = [
+      "pdf_features.h",
+    ]
+
+    sources = [
+      "pdf_features.cc",
+      "pdf_features.h",
+    ]
+  }
+
   source_set("pdf_test_utils") {
     testonly = true
     sources = [
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index d0397bc8..eae8acc 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -22,6 +22,7 @@
 #include "chrome/common/content_restriction.h"
 #include "net/base/escape.h"
 #include "pdf/pdf.h"
+#include "pdf/pdf_features.h"
 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/pp_rect.h"
@@ -50,9 +51,6 @@
 
 namespace {
 
-const base::Feature kSaveEditedPDFFormExperiment{
-    "SaveEditedPDFForm", base::FEATURE_DISABLED_BY_DEFAULT};
-
 constexpr char kChromePrint[] = "chrome://print/";
 constexpr char kChromeExtension[] =
     "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai";
@@ -1501,7 +1499,7 @@
 void OutOfProcessInstance::Save(const std::string& token) {
   engine_->KillFormFocus();
 
-  if (!base::FeatureList::IsEnabled(kSaveEditedPDFFormExperiment) ||
+  if (!base::FeatureList::IsEnabled(features::kSaveEditedPDFForm) ||
       !edit_mode_) {
     ConsumeSaveToken(token);
     pp::PDF::SaveAs(this);
diff --git a/pdf/pdf_features.cc b/pdf/pdf_features.cc
new file mode 100644
index 0000000..91454c4
--- /dev/null
+++ b/pdf/pdf_features.cc
@@ -0,0 +1,14 @@
+// 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 "pdf/pdf_features.h"
+
+namespace chrome_pdf {
+namespace features {
+
+const base::Feature kSaveEditedPDFForm{"SaveEditedPDFForm",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+}  // namespace chrome_pdf
diff --git a/pdf/pdf_features.h b/pdf/pdf_features.h
new file mode 100644
index 0000000..56019e5
--- /dev/null
+++ b/pdf/pdf_features.h
@@ -0,0 +1,21 @@
+// 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.
+
+// This file defines all the public base::FeatureList features for the pdf
+// module.
+
+#ifndef PDF_PDF_FEATURES_H_
+#define PDF_PDF_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace chrome_pdf {
+namespace features {
+
+extern const base::Feature kSaveEditedPDFForm;
+
+}  // namespace features
+}  // namespace chrome_pdf
+
+#endif  // PDF_PDF_FEATURES_H_
diff --git a/remoting/host/installer/mac/uninstaller/remoting_uninstaller.icns b/remoting/host/installer/mac/uninstaller/remoting_uninstaller.icns
index cbc1490f..13ccb74 100644
--- a/remoting/host/installer/mac/uninstaller/remoting_uninstaller.icns
+++ b/remoting/host/installer/mac/uninstaller/remoting_uninstaller.icns
Binary files differ
diff --git a/remoting/host/mac/remoting_me2me_host.icns b/remoting/host/mac/remoting_me2me_host.icns
index cbc1490f..13ccb74 100644
--- a/remoting/host/mac/remoting_me2me_host.icns
+++ b/remoting/host/mac/remoting_me2me_host.icns
Binary files differ
diff --git a/services/tracing/public/cpp/perfetto/producer_client.cc b/services/tracing/public/cpp/perfetto/producer_client.cc
index a02d8b91..1a098d6 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.cc
+++ b/services/tracing/public/cpp/perfetto/producer_client.cc
@@ -47,6 +47,12 @@
   StartTracing(producer_client, data_source_config);
 }
 
+// static
+ProducerClient* ProducerClient::Get() {
+  static base::NoDestructor<ProducerClient> producer_client;
+  return producer_client.get();
+}
+
 ProducerClient::ProducerClient() : weak_ptr_factory_(this) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
diff --git a/services/tracing/public/cpp/perfetto/producer_client.h b/services/tracing/public/cpp/perfetto/producer_client.h
index 7de4408..393ecdbf 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.h
+++ b/services/tracing/public/cpp/perfetto/producer_client.h
@@ -70,7 +70,9 @@
     std::string name_;
   };
 
-  ProducerClient();
+  // Returns the process-wide instance of the ProducerClient.
+  static ProducerClient* Get();
+
   ~ProducerClient() override;
 
   static void DeleteSoonForTesting(std::unique_ptr<ProducerClient>);
@@ -125,7 +127,13 @@
 
   static void ResetTaskRunnerForTesting();
 
+ protected:
+  // protected for testing.
+  ProducerClient();
+
  private:
+  friend class base::NoDestructor<ProducerClient>;
+
   void CommitDataOnSequence(mojom::CommitDataRequestPtr request);
   void AddDataSourceOnSequence(DataSourceBase*);
   void RegisterDataSourceWithHost(DataSourceBase* data_source);
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index fb034b9..bea01e4f 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -28,7 +28,7 @@
 
 namespace {
 
-const char kCategoryGroup[] = "foo";
+constexpr const char kCategoryGroup[] = "foo";
 
 class MockProducerClient : public ProducerClient {
  public:
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc
index 4ea4c91..14a4047 100644
--- a/services/tracing/public/cpp/trace_event_agent.cc
+++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -46,16 +46,12 @@
 
 namespace {
 
-ProducerClient* GetProducerClient() {
-  static base::NoDestructor<ProducerClient> producer_client;
-  return producer_client.get();
-}
-
 void InitializeProducerClient(service_manager::Connector* connector) {
   mojom::PerfettoServicePtr perfetto_service;
   connector->BindInterface(mojom::kServiceName, &perfetto_service);
 
-  GetProducerClient()->CreateMojoMessagepipes(base::BindOnce(
+  ProducerClient* client = ProducerClient::Get();
+  client->CreateMojoMessagepipes(base::BindOnce(
       [](mojom::PerfettoServicePtr perfetto_service,
          mojom::ProducerClientPtr producer_client_pipe,
          mojom::ProducerHostRequest producer_host_pipe) {
@@ -64,7 +60,7 @@
       },
       std::move(perfetto_service)));
 
-  GetProducerClient()->AddDataSource(TraceEventDataSource::GetInstance());
+  client->AddDataSource(TraceEventDataSource::GetInstance());
 }
 
 void AddPerfettoMetadataGeneratorFunction(
@@ -73,7 +69,7 @@
   // call.
   static TraceEventMetadataSource* metadata_source = []() {
     static base::NoDestructor<TraceEventMetadataSource> instance;
-    GetProducerClient()->AddDataSource(instance.get());
+    ProducerClient::Get()->AddDataSource(instance.get());
     return instance.get();
   }();
 
diff --git a/services/tracing/public/cpp/trace_event_agent_unittest.cc b/services/tracing/public/cpp/trace_event_agent_unittest.cc
index a55c310..d385fd67 100644
--- a/services/tracing/public/cpp/trace_event_agent_unittest.cc
+++ b/services/tracing/public/cpp/trace_event_agent_unittest.cc
@@ -23,7 +23,7 @@
 namespace tracing {
 
 namespace {
-const char kTestCategory[] = "TraceEventAgentTestCategory";
+constexpr const char kTestCategory[] = "TraceEventAgentTestCategory";
 const char kTestMetadataKey[] = "TraceEventAgentTestMetadata";
 }  // namespace
 
diff --git a/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.cc b/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.cc
index 68056867..70d4124 100644
--- a/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.cc
+++ b/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.cc
@@ -16,6 +16,7 @@
     return false;
   }
   out->flags = data.flags();
+  out->async_hit_test_reasons = data.async_hit_test_reasons();
   out->child_count = data.child_count();
   return true;
 }
diff --git a/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.h b/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.h
index bc93c1fb..05824a6 100644
--- a/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.h
+++ b/services/viz/public/cpp/hit_test/aggregated_hit_test_region_struct_traits.h
@@ -25,6 +25,11 @@
     return region.flags;
   }
 
+  static uint32_t async_hit_test_reasons(
+      const viz::AggregatedHitTestRegion& region) {
+    return region.async_hit_test_reasons;
+  }
+
   static const gfx::Rect& rect(const viz::AggregatedHitTestRegion& region) {
     return region.rect;
   }
diff --git a/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.cc b/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.cc
index e5f606d7..9739bd7 100644
--- a/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.cc
+++ b/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.cc
@@ -17,6 +17,7 @@
   if (!data.ReadTransform(&out->transform))
     return false;
   out->flags = data.flags();
+  out->async_hit_test_reasons = data.async_hit_test_reasons();
   return true;
 }
 
@@ -32,6 +33,7 @@
   if (!data.ReadTransform(&out->transform))
     return false;
   out->flags = data.flags();
+  out->async_hit_test_reasons = data.async_hit_test_reasons();
   return true;
 }
 
diff --git a/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.h b/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.h
index 581fcad..3ed39f4 100644
--- a/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.h
+++ b/services/viz/public/cpp/hit_test/hit_test_region_list_struct_traits.h
@@ -22,6 +22,9 @@
   static uint32_t flags(const viz::HitTestRegion& region) {
     return region.flags;
   }
+  static uint32_t async_hit_test_reasons(const viz::HitTestRegion& region) {
+    return region.async_hit_test_reasons;
+  }
   static const gfx::Rect& rect(const viz::HitTestRegion& region) {
     return region.rect;
   }
@@ -39,6 +42,9 @@
   static uint32_t flags(const viz::HitTestRegionList& list) {
     return list.flags;
   }
+  static uint32_t async_hit_test_reasons(const viz::HitTestRegionList& list) {
+    return list.async_hit_test_reasons;
+  }
   static const gfx::Rect& bounds(const viz::HitTestRegionList& list) {
     return list.bounds;
   }
diff --git a/services/viz/public/cpp/hit_test/struct_traits_unittest.cc b/services/viz/public/cpp/hit_test/struct_traits_unittest.cc
index f7e55ef..21114d8 100644
--- a/services/viz/public/cpp/hit_test/struct_traits_unittest.cc
+++ b/services/viz/public/cpp/hit_test/struct_traits_unittest.cc
@@ -18,17 +18,20 @@
 TEST(StructTraitsTest, AggregatedHitTestRegion) {
   constexpr FrameSinkId frame_sink_id(1337, 1234);
   constexpr uint32_t flags = HitTestRegionFlags::kHitTestAsk;
+  constexpr uint32_t async_hit_test_reasons =
+      AsyncHitTestReasons::kOverlappedRegion;
   constexpr gfx::Rect rect(1024, 768);
   gfx::Transform transform;
   transform.Scale(.5f, .7f);
   constexpr int32_t child_count = 5;
   AggregatedHitTestRegion input(frame_sink_id, flags, rect, transform,
-                                child_count);
+                                child_count, async_hit_test_reasons);
   AggregatedHitTestRegion output;
   mojo::test::SerializeAndDeserialize<mojom::AggregatedHitTestRegion>(&input,
                                                                       &output);
   EXPECT_EQ(input.frame_sink_id, output.frame_sink_id);
   EXPECT_EQ(input.flags, output.flags);
+  EXPECT_EQ(input.async_hit_test_reasons, output.async_hit_test_reasons);
   EXPECT_EQ(input.rect, output.rect);
   EXPECT_EQ(input.transform(), output.transform());
   EXPECT_EQ(input.child_count, output.child_count);
@@ -37,11 +40,13 @@
 TEST(StructTraitsTest, HitTestRegionList) {
   base::Optional<HitTestRegionList> input(base::in_place);
   input->flags = HitTestRegionFlags::kHitTestAsk;
+  input->async_hit_test_reasons = AsyncHitTestReasons::kOverlappedRegion;
   input->bounds = gfx::Rect(1, 2, 3, 4);
   input->transform.Scale(0.5f, 0.7f);
 
   HitTestRegion input_region1;
   input_region1.flags = HitTestRegionFlags::kHitTestIgnore;
+  input_region1.async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
   input_region1.frame_sink_id = FrameSinkId(12, 13);
   input_region1.rect = gfx::Rect(4, 5, 6, 7);
   input_region1.transform.Scale(1.2f, 1.3f);
@@ -52,10 +57,13 @@
                                                                 &output);
   EXPECT_TRUE(output);
   EXPECT_EQ(input->flags, output->flags);
+  EXPECT_EQ(input->async_hit_test_reasons, output->async_hit_test_reasons);
   EXPECT_EQ(input->bounds, output->bounds);
   EXPECT_EQ(input->transform, output->transform);
   EXPECT_EQ(input->regions.size(), output->regions.size());
   EXPECT_EQ(input->regions[0].flags, output->regions[0].flags);
+  EXPECT_EQ(input->regions[0].async_hit_test_reasons,
+            output->regions[0].async_hit_test_reasons);
   EXPECT_EQ(input->regions[0].frame_sink_id, output->regions[0].frame_sink_id);
   EXPECT_EQ(input->regions[0].rect, output->regions[0].rect);
   EXPECT_EQ(input->regions[0].transform, output->regions[0].transform);
diff --git a/services/viz/public/interfaces/hit_test/aggregated_hit_test_region.mojom b/services/viz/public/interfaces/hit_test/aggregated_hit_test_region.mojom
index 21c088d8..84d7f858 100644
--- a/services/viz/public/interfaces/hit_test/aggregated_hit_test_region.mojom
+++ b/services/viz/public/interfaces/hit_test/aggregated_hit_test_region.mojom
@@ -14,6 +14,8 @@
 
   uint32 flags;
 
+  uint32 async_hit_test_reasons;
+
   gfx.mojom.Rect rect;
 
   uint32 child_count;
diff --git a/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom b/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
index 8df79ee1..9280bb8c 100644
--- a/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
+++ b/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
@@ -12,6 +12,7 @@
 // See src/components/viz/common/hit_test/hit_test_region_list.h.
 struct HitTestRegion {
   uint32 flags;
+  uint32 async_hit_test_reasons;
   FrameSinkId frame_sink_id;
   gfx.mojom.Rect rect;
   gfx.mojom.Transform transform;
@@ -20,6 +21,7 @@
 // See src/components/viz/common/hit_test/hit_test_region_list.h.
 struct HitTestRegionList {
   uint32 flags;
+  uint32 async_hit_test_reasons;
   gfx.mojom.Rect bounds;
   gfx.mojom.Transform transform;
   array<HitTestRegion> regions;
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index b6446bf..5d93162b 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -151,10 +151,6 @@
 #define SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
 #endif
 
-#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
-#define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
-#endif
-
 // Max. verb count for paths rendered by the edge-AA tessellating path renderer.
 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 100
 
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 169e040..5fc62782 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -23138,7 +23138,6 @@
           "--browser=android-chromium",
           "--device=android"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_perf_unittests",
         "name": "telemetry_perf_unittests",
         "swarming": {
diff --git a/testing/buildbot/filters/webui_polymer2_browser_tests.filter b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
index 0885aaf8..df76d2d 100644
--- a/testing/buildbot/filters/webui_polymer2_browser_tests.filter
+++ b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
@@ -68,7 +68,6 @@
 CrElementsToastTest.*
 CrElementsToggleTest.*
 CrElementsToolbarSearchFieldTest.*
-CrExtensionsActivityLogTest.*
 CrExtensionsA11yTest.*
 CrExtensionsA11yTestWithMultipleExensions.*
 CrExtensionsCodeSectionTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index aa9ff9f8..9b75cfd 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1020,7 +1020,6 @@
           ],
           'shards': 15,
         },
-        'experiment_percentage': 100, # https://crbug.com/903849
       },
       # chromium.android.fyi
       'x86 Cloud Tester': {
diff --git a/testing/libfuzzer/README.md b/testing/libfuzzer/README.md
index 09d16bc..f8907f3 100644
--- a/testing/libfuzzer/README.md
+++ b/testing/libfuzzer/README.md
@@ -5,7 +5,7 @@
 *** aside
 [Getting Started](getting_started.md)
 | [Buildbot](https://build.chromium.org/p/chromium.fyi/buildslaves/slave43-c1)
-| [ClusterFuzz Status](https://clusterfuzz.com/v2/fuzzer-stats)
+| [ClusterFuzz Status](https://clusterfuzz.com/fuzzer-stats)
 | [Cover Bug]
 ***
 
diff --git a/testing/libfuzzer/clusterfuzz.md b/testing/libfuzzer/clusterfuzz.md
index c46c4df..fa08db3 100644
--- a/testing/libfuzzer/clusterfuzz.md
+++ b/testing/libfuzzer/clusterfuzz.md
@@ -47,7 +47,7 @@
 
 [Buildbot]: https://build.chromium.org/p/chromium.fyi/buildslaves/slave43-c1
 [chromium_libfuzzer.py]: https://code.google.com/p/chromium/codesearch#chromium/build/scripts/slave/recipes/chromium_libfuzzer.py
-[ClusterFuzz Fuzzer Status]: https://clusterfuzz.com/v2/fuzzer-stats
+[ClusterFuzz Fuzzer Status]: https://clusterfuzz.com/fuzzer-stats
 [ClusterFuzz libFuzzer Logs]: https://console.cloud.google.com/storage/browser/clusterfuzz-libfuzzer-logs
 [Corpus GCS Bucket]: https://console.cloud.google.com/storage/clusterfuzz-corpus/libfuzzer
 [fuzzer_test.gni]: https://code.google.com/p/chromium/codesearch#chromium/src/testing/libfuzzer/fuzzer_test.gni
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index c5e6bc6..e9cd1634 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1795,9 +1795,7 @@
 crbug.com/867834 virtual/outofblink-cors/http/tests/security/isolatedWorld/cross-origin-xhr.html [ Crash ]
 
 # Timeout tests in dictionary order.
-# Using Skip because this is listed as a Slow test, so if Timeout is used it
-# will waste 30 or 90 seconds per run.
-crbug.com/870173 [ Mac ] virtual/outofblink-cors/external/wpt/service-workers/service-worker/clients-matchall-order.https.html [ Skip ]
+crbug.com/870173 [ Mac ] virtual/outofblink-cors/external/wpt/service-workers/service-worker/clients-matchall-order.https.html [ Timeout Pass ]
 
 # Failing tests in dictionary order.
 crbug.com/870173 virtual/outofblink-cors/http/tests/security/img-redirect-to-crossorigin-credentials.html [ Failure ]
@@ -2852,6 +2850,7 @@
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/multiDevice.html [ Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Win7 ] external/wpt/web-animations/animation-model/animation-types/accumulation-per-property.html [ Failure Timeout ]
 crbug.com/626703 [ Win7 ] external/wpt/IndexedDB/interleaved-cursors-large.html [ Crash ]
 crbug.com/626703 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Timeout ]
 crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/reach-and-scroll-overflow-div-without-mouse.html b/third_party/WebKit/LayoutTests/accessibility/reach-and-scroll-overflow-div-without-mouse.html
new file mode 100644
index 0000000..a6ed5ad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/reach-and-scroll-overflow-div-without-mouse.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<style>
+  div#testcontent  {
+    display: flex;
+    justify-content: space-between;
+    flex-direction: row;
+  }
+  div#scroller {
+    height: 200px;
+    width: 200px;
+    overflow: scroll;
+    background-color: #82E0D9;
+  }
+</style>
+
+<div id="testcontent">
+  <button id="b1">b1</button>
+  <div id="scroller">
+    <p style="margin-top: 100%">clipped text</p>
+  </div>
+  <button id="b2">b2</button>
+</div>
+
+<p>By using the keyboard, a user can, without a mouse, read the div's clipped text.</p>
+
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../fast/spatial-navigation/resources/snav-testharness.js"></script>
+<script>
+  test(function(t) {
+      assert_true(internals.runtimeFlags.keyboardFocusableScrollersEnabled);
+  }, "Make sure KeyboardFocusableScrollers is set.");
+
+  let scroller = document.getElementById('scroller')
+  test(() => {
+    assert_equals(scroller.scrollTop, 0);
+  }, "Scroller starts at its top.");
+
+  test(() => {
+    assert_equals(scroller.tabIndex, 0);
+  }, "tabIndex reflects reality (the scroller can be focused).");
+
+  async_test(t => {
+    scroller.addEventListener("scroll", t.step_func_done(function() {
+      assert_greater_than(scroller.scrollTop, 0);
+    }));
+  }, "Down-key scrolled the focused div.");  // assertFocusMoves triggers a scroll.
+
+  document.getElementById("b1").focus();  // Start outside the scroller.
+  snav.assertFocusMoves([
+    ["Forward", "scroller"],   // Focus the scroller using sequential navigation (forward).
+    ["Forward", "b2"],         // Blur the scroller using sequential navigation.
+    ["Backward", "scroller"],  // Focus the scroller using sequential navigation (backward).
+    ["Down", "scroller"],      // Verify arrow-key-scrolling.
+  ], false);
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
index 11a2211..72f366e 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -140248,6 +140248,11 @@
      {}
     ]
    ],
+   "css/css-transforms/parsing/rotate-parsing-valid-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-transforms/parsing/scale-parsing-valid-expected.txt": [
     [
      {}
@@ -350299,8 +350304,12 @@
    "9eef999716da1025b1c8595f60e52ff2cdb7715e",
    "testharness"
   ],
+  "css/css-transforms/parsing/rotate-parsing-valid-expected.txt": [
+   "07ec45ce897b42272b6fa172c3cf8c19aaa3e042",
+   "support"
+  ],
   "css/css-transforms/parsing/rotate-parsing-valid.html": [
-   "c82f6be8c5d75e4c323c0f2701cfa9d0a0540b4b",
+   "63243b5e9856f0223cefc02ec41f53666b95b918",
    "testharness"
   ],
   "css/css-transforms/parsing/scale-parsing-invalid.html": [
@@ -387320,7 +387329,7 @@
    "support"
   ],
   "html/dom/elements-obsolete.js": [
-   "e37e34280f69aec0e37d6b9f277f36d53d7ff330",
+   "7f673cb7f0ee9bbeae746448163bbc11e31a1220",
    "support"
   ],
   "html/dom/elements-sections.js": [
@@ -388224,7 +388233,7 @@
    "testharness"
   ],
   "html/dom/reflection-obsolete-expected.txt": [
-   "718597f9b80fb1d871d1ecf7ede30412e95a3fa0",
+   "27bd13b69096cdfdd3781133f436bd1ff51c3676",
    "support"
   ],
   "html/dom/reflection-obsolete.html": [
@@ -435636,7 +435645,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [
-   "2ccb7b120832b804eca3f116460858f2fe4a1d4a",
+   "9e547a2d7b2dd7abf2db695290659b0a9d9ba9ec",
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
@@ -435644,7 +435653,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [
-   "0791c79b3b5540632cf31ad9599663ee95ceb9fa",
+   "89a39f66b4941789f803773eda400e21300bcbab",
    "support"
   ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
@@ -435656,7 +435665,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property-expected.txt": [
-   "60a6e87386b61837484f05ecc16b2f6d7835c60a",
+   "68e34175b91db5a2545a6f1e05e589342fa75d9d",
    "support"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property.html": [
@@ -435668,7 +435677,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/property-types.js": [
-   "dd20b85aa1e9a647fb0301f5cc9120b95954fc23",
+   "519e38ae8aea2a6e5d762af56069ebddbb231838",
    "support"
   ],
   "web-animations/animation-model/animation-types/visibility.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/var-reference-registered-properties-cycles.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/var-reference-registered-properties-cycles.html
index 58d6c846..65d11697 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/var-reference-registered-properties-cycles.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/var-reference-registered-properties-cycles.html
@@ -25,14 +25,13 @@
     CSS.registerProperty({name: '--registered-1-d', syntax: '<length>', initialValue: '4px', inherits: false});
 
     computedStyle = getComputedStyle(test1);
-    assert_equals(computedStyle.getPropertyValue('--registered-1-a'), '1px');
-    assert_equals(computedStyle.getPropertyValue('--registered-1-b'), '2px');
-
-    assert_equals(computedStyle.getPropertyValue('--registered-1-c'), '2px');
-    assert_equals(computedStyle.getPropertyValue('--registered-1-d'), '2px');
-    assert_equals(computedStyle.getPropertyValue('--unregistered-1-a'), '1px');
-    assert_equals(computedStyle.left, '1px');
-    assert_equals(computedStyle.top, '2px');
+    assert_equals(computedStyle.getPropertyValue('--registered-1-a'), '');
+    assert_equals(computedStyle.getPropertyValue('--registered-1-b'), '');
+    assert_equals(computedStyle.getPropertyValue('--registered-1-c'), '30px');
+    assert_equals(computedStyle.getPropertyValue('--registered-1-d'), '4px');
+    assert_equals(computedStyle.getPropertyValue('--unregistered-1-a'), '');
+    assert_equals(computedStyle.left, '50px');
+    assert_equals(computedStyle.top, '60px');
 }, "A var() cycle between two registered properties is handled correctly.");
 </script>
 
@@ -63,18 +62,18 @@
     CSS.registerProperty({name: '--registered-2-e', syntax: '<length>', initialValue: '5px', inherits: false});
 
     computedStyle = getComputedStyle(test2);
-    assert_equals(computedStyle.getPropertyValue('--registered-2-a'), '1px');
+    assert_equals(computedStyle.getPropertyValue('--registered-2-a'), '');
     assert_equals(computedStyle.getPropertyValue('--unregistered-2-a'), '');
 
-    assert_equals(computedStyle.getPropertyValue('--registered-2-b'), '1px');
-    assert_equals(computedStyle.getPropertyValue('--registered-2-c'), '1px');
+    assert_equals(computedStyle.getPropertyValue('--registered-2-b'), '30px');
+    assert_equals(computedStyle.getPropertyValue('--registered-2-c'), '3px');
     assert_equals(computedStyle.getPropertyValue('--registered-2-d'), '40px');
     assert_equals(computedStyle.getPropertyValue('--registered-2-e'), '5px');
-    assert_equals(computedStyle.getPropertyValue('--unregistered-2-b'), '1px');
-    assert_equals(computedStyle.getPropertyValue('--unregistered-2-c'), '1px');
+    assert_equals(computedStyle.getPropertyValue('--unregistered-2-b'), '50px');
+    assert_equals(computedStyle.getPropertyValue('--unregistered-2-c'), '');
     assert_equals(computedStyle.getPropertyValue('--unregistered-2-d'), '60px');
     assert_equals(computedStyle.getPropertyValue('--unregistered-2-e'), '');
-    assert_equals(computedStyle.left, '1px');
+    assert_equals(computedStyle.left, '70px');
     assert_equals(computedStyle.top, '80px');
 }, "A var() cycle between a registered properties and an unregistered property is handled correctly.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid-expected.txt
new file mode 100644
index 0000000..07ec45c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS e.style['rotate'] = "none" should set the property value
+PASS e.style['rotate'] = "0deg" should set the property value
+PASS e.style['rotate'] = "100 200 300 400grad" should set the property value
+PASS e.style['rotate'] = "400grad 100 200 300" should set the property value
+FAIL e.style['rotate'] = "x 400grad" should set the property value assert_equals: serialization should be canonical expected "x 400grad" but got "1 0 0 400grad"
+FAIL e.style['rotate'] = "400grad x" should set the property value assert_equals: serialization should be canonical expected "x 400grad" but got "1 0 0 400grad"
+FAIL e.style['rotate'] = "1 0 0 400grad" should set the property value assert_equals: serialization should be canonical expected "x 400grad" but got "1 0 0 400grad"
+FAIL e.style['rotate'] = "y 400grad" should set the property value assert_equals: serialization should be canonical expected "y 400grad" but got "0 1 0 400grad"
+FAIL e.style['rotate'] = "400grad y" should set the property value assert_equals: serialization should be canonical expected "y 400grad" but got "0 1 0 400grad"
+FAIL e.style['rotate'] = "0 1 0 400grad" should set the property value assert_equals: serialization should be canonical expected "y 400grad" but got "0 1 0 400grad"
+FAIL e.style['rotate'] = "z 400grad" should set the property value assert_equals: serialization should be canonical expected "z 400grad" but got "0 0 1 400grad"
+FAIL e.style['rotate'] = "400grad z" should set the property value assert_equals: serialization should be canonical expected "z 400grad" but got "0 0 1 400grad"
+FAIL e.style['rotate'] = "0 0 1 400grad" should set the property value assert_equals: serialization should be canonical expected "z 400grad" but got "0 0 1 400grad"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid.html
index c82f6be..63243b5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-transforms/parsing/rotate-parsing-valid.html
@@ -19,14 +19,17 @@
 test_valid_value("rotate", "100 200 300 400grad");
 test_valid_value("rotate", "400grad 100 200 300", "100 200 300 400grad");
 
-test_valid_value("rotate", "x 400grad", "1 0 0 400grad");
-test_valid_value("rotate", "400grad x", "1 0 0 400grad");
+test_valid_value("rotate", "x 400grad");
+test_valid_value("rotate", "400grad x", "x 400grad");
+test_valid_value("rotate", "1 0 0 400grad", "x 400grad");
 
-test_valid_value("rotate", "y 400grad", "0 1 0 400grad");
-test_valid_value("rotate", "400grad y", "0 1 0 400grad");
+test_valid_value("rotate", "y 400grad");
+test_valid_value("rotate", "400grad y", "y 400grad");
+test_valid_value("rotate", "0 1 0 400grad", "y 400grad");
 
-test_valid_value("rotate", "z 400grad", "0 0 1 400grad");
-test_valid_value("rotate", "400grad z", "0 0 1 400grad");
+test_valid_value("rotate", "z 400grad");
+test_valid_value("rotate", "400grad z", "z 400grad");
+test_valid_value("rotate", "0 0 1 400grad", "z 400grad");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html
new file mode 100644
index 0000000..2648868
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "camera");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await test_driver.bless('Activate document for user media');
+  await navigator.mediaDevices.getUserMedia({video: true});
+  check_report_format(await report);
+}, "Camera report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html.headers
new file mode 100644
index 0000000..46b8481
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: camera-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-reporting.https.html
index 14b2ed1d..1d08700d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/camera-reporting.https.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "camera");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html
new file mode 100644
index 0000000..ab0bb827
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "document-write");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  document.write("This should be written into the document");
+  check_report_format(await report);
+}, "Document-write report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers
new file mode 100644
index 0000000..63e43f1d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers
@@ -0,0 +1 @@
+Feature-Policy: document-write-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-reporting.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-reporting.html
index cb08b8d..d113f2e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-reporting.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/document-write-reporting.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "document-write");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html
new file mode 100644
index 0000000..20e44b2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "encrypted-media");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await navigator.requestMediaKeySystemAccess("org.w3.clearkey",
+      [{
+        initDataTypes: ["webm"],
+        videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}],
+      }]);
+  check_report_format(await report);
+}, "Encrypted Media report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html.headers
new file mode 100644
index 0000000..a2c8fbb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: encrypted-media-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html
index d309d53..c3b6393 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html
@@ -11,6 +11,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "encrypted-media");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html
new file mode 100644
index 0000000..a6b3d5ad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <div id='fs'></div>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "fullscreen");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await test_driver.bless('Activate document for fullscreen');
+  await document.getElementById('fs').requestFullscreen();
+  check_report_format(await report);
+  document.exitFullscreen();
+}, "Fullscreen report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html.headers
new file mode 100644
index 0000000..33defa88
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-report-only.html.headers
@@ -0,0 +1 @@
+Feature-Policy: fullscreen-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-reporting.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-reporting.html
index 83d97c9..9a7fb878 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-reporting.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/fullscreen-reporting.html
@@ -12,6 +12,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "fullscreen");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html
new file mode 100644
index 0000000..deb6ade
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var sensor_features_verified = {
+  "accelerometer": false,
+  "ambient-light-sensor": false,
+  "magnetometer": false,
+  "gyroscope": false
+};
+
+var check_report_format = function(reports, observer) {
+  // Check each report in this batch. This observer callback may be called
+  // multiple times before all reports have been processed.
+  for (const report of reports) {
+
+    // Validate that the reported feature is one of the sensor features, and that
+    // we have not seen a report for this feature before.
+    assert_true(sensor_features_verified.hasOwnProperty(report.body.feature));
+    assert_false(sensor_features_verified[report.body.feature]);
+
+    // Validate the remainder of the report
+    assert_equals(report.type, "feature-policy");
+    assert_equals(report.url, document.location.href);
+    assert_equals(report.body.disposition, "report");
+    assert_equals(report.body.sourceFile, document.location.href);
+    assert_equals(typeof report.body.message, "string");
+    assert_equals(typeof report.body.lineNumber, "number");
+    assert_equals(typeof report.body.columnNumber, "number");
+
+    sensor_features_verified[report.body.feature] = true;
+  }
+
+  // Test is only done when reports for all features have been seen
+  for (let result of Object.values(sensor_features_verified)) {
+    if (!result)
+      return;
+  }
+  this.done();
+};
+
+async_test(t => {
+  new ReportingObserver(t.step_func(check_report_format),
+                        {types: ['feature-policy']}).observe();
+  new Accelerometer();
+  new AmbientLightSensor();
+  new Gyroscope();
+  new Magnetometer();
+}, "Generic Sensor report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html.headers
new file mode 100644
index 0000000..26605eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: ambient-light-sensor-report-only 'none'; accelerometer-report-only 'none'; gyroscope-report-only 'none'; magnetometer-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html
index c60e3e8..517c7f6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html
@@ -26,6 +26,7 @@
     // Validate the remainder of the report
     assert_equals(report.type, "feature-policy");
     assert_equals(report.url, document.location.href);
+    assert_equals(report.body.disposition, "enforce");
     assert_equals(report.body.sourceFile, document.location.href);
     assert_equals(typeof report.body.message, "string");
     assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https-expected.txt
new file mode 100644
index 0000000..9322061
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Geolocation report only mode promise_test: Unhandled rejection with value: object "[object PositionError]"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html
new file mode 100644
index 0000000..cf2a75b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "geolocation");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  try {
+    await new Promise((resolve, reject) => {
+      navigator.geolocation.getCurrentPosition(resolve, reject);
+    });
+    check_report_format(await report);
+  } catch (err) {
+    // In case the getCurrentPosition call was rejected due to user permissions,
+    // the report should be generated anyway. Wait for it and check the format
+    // before failing this test.
+    check_report_format(await report);
+    throw err;
+  }
+}, "Geolocation report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html.headers
new file mode 100644
index 0000000..fc985900
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: geolocation-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-reporting.https.html
index 22e2585..ce06902 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/geolocation-reporting.https.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "geolocation");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html
new file mode 100644
index 0000000..2d7b4d96
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "microphone");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await test_driver.bless('Activate document for user media');
+  await navigator.mediaDevices.getUserMedia({audio: true});
+  check_report_format(await report);
+}, "Microphone report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html.headers
new file mode 100644
index 0000000..7673d05b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: microphone-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-reporting.https.html
index 7347a23..4a0e0b5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/microphone-reporting.https.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "microphone");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only-expected.txt
new file mode 100644
index 0000000..bba8e2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL MIDI report only mode promise_test: Unhandled rejection with value: object "SecurityError: An attempt was made to break through the security policy of the user agent."
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html
new file mode 100644
index 0000000..e466ce0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "midi");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  try {
+    await navigator.requestMIDIAccess();
+    check_report_format(await report);
+  } catch (err) {
+    // In case the requestMIDIAccess call was rejected due to user permissions,
+    // the report should be generated anyway. Wait for it and check the format
+    // before failing this test.
+    check_report_format(await report);
+    throw err;
+  }
+}, "MIDI report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html.headers
new file mode 100644
index 0000000..3c6a2d4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-report-only.html.headers
@@ -0,0 +1 @@
+Feature-Policy: midi-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-reporting.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-reporting.html
index 8303b7a..fc6d8b1a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-reporting.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/midi-reporting.html
@@ -11,6 +11,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "midi");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https-expected.txt
new file mode 100644
index 0000000..19f3d28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL PaymentRequest report only mode promise_test: Unhandled rejection with value: object "UnknownError: Request failed"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html
new file mode 100644
index 0000000..6a7678b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "payment");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  try {
+    const request = new PaymentRequest(
+      [{ supportedMethods: 'basic-card' }],
+      { total: { label: 'Total', amount: { currency: 'USD', value: 0 }}},
+      {});
+    await request.show()
+    check_report_format(await report);
+  } catch (err) {
+    // In case the show call was rejected, the report should be generated
+    // anyway. Wait for it and check the format before failing this test.
+    check_report_format(await report);
+    throw err;
+  }
+}, "PaymentRequest report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html.headers
new file mode 100644
index 0000000..6411478e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: payment-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-reporting.https.html
index 03eaebe..2c1189d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/payment-reporting.https.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "payment");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html
new file mode 100644
index 0000000..157670f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/common/media.js'></script>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+    <script src='../resources/picture-in-picture.js'></script>
+  </head>
+  <body>
+    <script>
+const check_report_format = ([reports, observer]) => {
+  const report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "picture-in-picture");
+  assert_equals(report.body.disposition, "report");
+};
+
+const loadVideo = () => new Promise(resolve => {
+    const video = document.createElement('video');
+    video.src = getVideoURI('/media/movie_5');
+    video.addEventListener('loadedmetadata', () => {
+      resolve(video);
+    }, { once: true });
+});
+
+promise_pip_test(async (t) => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  const videoElement = await loadVideo();
+  await test_driver.bless('picture-in-picture');
+  await videoElement.requestPictureInPicture();
+  check_report_format(await report);
+}, "Picture-in-Picture report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html.headers
new file mode 100644
index 0000000..0df90a3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-report-only.html.headers
@@ -0,0 +1 @@
+Feature-Policy: picture-in-picture-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-reporting.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-reporting.html
index e3cbf10..f15f47c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-reporting.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/picture-in-picture-reporting.html
@@ -15,6 +15,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "picture-in-picture");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html
new file mode 100644
index 0000000..f841f63
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+const check_report_format = ([reports, observer]) => {
+  const report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "sync-xhr");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  const xhr = new XMLHttpRequest();
+  xhr.open("GET", document.location.href, false);
+  xhr.send();
+  check_report_format(await report);
+}, "Sync-xhr report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html.headers
new file mode 100644
index 0000000..79a82cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-report-only.html.headers
@@ -0,0 +1 @@
+Feature-Policy: sync-xhr-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
index 2c76390..f16e335d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "sync-xhr");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html
new file mode 100644
index 0000000..e44c6c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <div id='fs'></div>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.body.feature, "usb");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await test_driver.bless('Activate document for USB');
+  await navigator.usb.getDevices();
+  check_report_format(await report);
+}, "USB report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html.headers
new file mode 100644
index 0000000..bd2e3c6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: usb-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-reporting.https.html
index f90c602e..bc4bffd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/usb-reporting.https.html
@@ -13,6 +13,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "usb");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html
new file mode 100644
index 0000000..91016d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+const check_report_format = ([reports, observer]) => {
+  const report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.url, document.location.href);
+  assert_equals(report.body.feature, "vr");
+  assert_equals(report.body.disposition, "report");
+  assert_equals(report.body.sourceFile, document.location.href);
+  assert_equals(typeof report.body.message, "string");
+  assert_equals(typeof report.body.lineNumber, "number");
+  assert_equals(typeof report.body.columnNumber, "number");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  await navigator.getVRDisplays();
+  check_report_format(await report);
+}, "VR report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers
new file mode 100644
index 0000000..b54cad2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-reporting.https.html
index 12cae05..9278519 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/vr-reporting.https.html
@@ -11,6 +11,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "vr");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html
new file mode 100644
index 0000000..5d4fb06
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+const check_report_format = ([reports, observer]) => {
+  const report = reports[0];
+  assert_equals(report.type, "feature-policy");
+  assert_equals(report.url, document.location.href);
+  assert_equals(report.body.feature, "vr");
+  assert_equals(report.body.disposition, "report");
+  assert_equals(report.body.sourceFile, document.location.href);
+  assert_equals(typeof report.body.message, "string");
+  assert_equals(typeof report.body.lineNumber, "number");
+  assert_equals(typeof report.body.columnNumber, "number");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy']}).observe();
+  });
+  try {
+    await navigator.xr.requestDevice();
+  } catch (err) {
+    // If no XR devices are available, requestDevice() will throw NotFoundError,
+    // but the report should be generated anyway.
+    assert_equals(err.name, 'NotFoundError');
+  }
+  check_report_format(await report);
+}, "XR report only mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html.headers
new file mode 100644
index 0000000..b54cad2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-report-only.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr-report-only 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-reporting.https.html
index a7a1222..0c793d5b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/xr-reporting.https.html
@@ -11,6 +11,7 @@
   assert_equals(report.type, "feature-policy");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.feature, "vr");
+  assert_equals(report.body.disposition, "enforce");
   assert_equals(report.body.sourceFile, document.location.href);
   assert_equals(typeof report.body.message, "string");
   assert_equals(typeof report.body.lineNumber, "number");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements-obsolete.js b/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements-obsolete.js
index e37e3428..7f673cb7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements-obsolete.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements-obsolete.js
@@ -13,9 +13,21 @@
     width: "string",
   },
   marquee: {
-    behavior: "string",
+    behavior: {
+      type: {
+        type: "enum",
+        keywords: ["scroll", "slide", "alternate"],
+        defaultVal: "scroll"
+      },
+    },
     bgColor: "string",
-    direction: "string",
+    direction: {
+      type: {
+        type: "enum",
+        keywords: ["up", "right", "down", "left"],
+        defaultVal: "left"
+      },
+    },
     height: "string",
     hspace: "unsigned long",
     scrollAmount: {type: "unsigned long", defaultVal: 6},
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-obsolete-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-obsolete-expected.txt
index 718597f9..27bd13b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-obsolete-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-obsolete-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 2809 tests; 2385 PASS, 424 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 2745 tests; 2321 PASS, 424 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS applet.title: 32 tests
 PASS applet.lang: 32 tests
 PASS applet.dir: 62 tests
@@ -438,9 +438,7 @@
 PASS marquee.hidden: 33 tests
 PASS marquee.accessKey: 32 tests
 PASS marquee.tabIndex: 24 tests
-PASS marquee.behavior: 32 tests
 PASS marquee.bgColor: 32 tests
-PASS marquee.direction: 32 tests
 PASS marquee.height: 32 tests
 PASS marquee.hspace: 62 tests
 PASS marquee.scrollAmount: 62 tests
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
index 2ccb7b1..9e547a2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
@@ -533,7 +533,7 @@
 PASS transform-style: "flat" onto "preserve-3d"
 PASS rotate (type: rotateList) has testAccumulation function
 FAIL rotate without rotation axes assert_equals: The value should be -45deg at 0ms expected "-45deg" but got "-90deg"
-FAIL rotate with underlying transform assert_equals: The value should be 1 0 0 45deg at 0ms expected "1 0 0 45deg" but got "1 0 0 90deg"
+FAIL rotate with underlying transform assert_equals: The value should be x 45deg at 0ms expected "x 45deg" but got "1 0 0 90deg"
 FAIL rotate with different rotation axes assert_approx_equals: expected 0.707107 0 0.707107 90deg but got 1 0 1 90deg: The value should be 0.707107 0 0.707107 90deg at 500ms but got 1 0 1 90deg expected 0.707107 +/- 0.0001 but got 1
 PASS translate (type: translateList) has testAccumulation function
 FAIL translate assert_equals: The value should be -100px at 0ms expected "-100px" but got "-200px"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
index 0791c79b..89a39f6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 562 tests; 529 PASS, 33 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 562 tests; 528 PASS, 34 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAddition function
 PASS align-content: "flex-end" onto "flex-start"
@@ -528,7 +528,7 @@
 PASS transform-style: "flat" onto "preserve-3d"
 PASS rotate (type: rotateList) has testAddition function
 PASS rotate without rotation axes
-PASS rotate with underlying transform
+FAIL rotate with underlying transform assert_equals: The value should be y 45deg at 0ms expected "y 45deg" but got "0 1 0 45deg"
 FAIL rotate with different rotation axes assert_approx_equals: expected 0.57735 0.57735 0.57735 135deg but got 1 1 1 135deg: The value should be 0.57735 0.57735 0.57735 135deg at 500ms but got 1 1 1 135deg expected 0.57735 +/- 0.0001 but got 1
 PASS translate (type: translateList) has testAddition function
 PASS translate
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
index 60a6e87..68e3417 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 701 tests; 642 PASS, 59 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 701 tests; 640 PASS, 61 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testInterpolation function
 PASS align-content uses discrete animation when animating between "flex-start" and "flex-end" with linear easing
@@ -663,8 +663,8 @@
 PASS transform-style uses discrete animation when animating between "flat" and "preserve-3d" with keyframe easing
 PASS rotate (type: rotateList) has testInterpolation function
 PASS rotate without rotation axes
-PASS rotate with rotation axes
-PASS rotate with rotation axes and range over 360 degrees
+FAIL rotate with rotation axes assert_equals: The value should be y 45deg at 500ms expected "y 45deg" but got "0 1 0 45deg"
+FAIL rotate with rotation axes and range over 360 degrees assert_equals: The value should be y 180deg at 250ms expected "y 180deg" but got "0 1 0 180deg"
 FAIL rotate with different rotation axes assert_approx_equals: expected 0 0.707107 0.707107 45deg but got 0 1 1 45deg: The value should be 0 0.707107 0.707107 45deg at 500ms but got 0 1 1 45deg expected 0.707107 +/- 0.0001 but got 1
 PASS translate (type: translateList) has testInterpolation function
 PASS translate with two unspecified values
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/property-types.js b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/property-types.js
index dd20b85a..519e38ae 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/property-types.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/animation-model/animation-types/property-types.js
@@ -1720,7 +1720,7 @@
                        1000);
 
       testAnimationSamples(animation, idlName,
-        [{ time: 500, expected: '0 1 0 45deg' }]);
+        [{ time: 500, expected: 'y 45deg' }]);
     }, `${property} with rotation axes`);
 
     test(t => {
@@ -1733,7 +1733,7 @@
                        1000);
 
       testAnimationSamples(animation, idlName,
-        [{ time: 250, expected: '0 1 0 180deg' }]);
+        [{ time: 250, expected: 'y 180deg' }]);
     }, `${property} with rotation axes and range over 360 degrees`);
 
     test(t => {
@@ -1768,15 +1768,15 @@
       // Rotation specified in transform property should not affect the computed
       // value of |property|.
       target.style.transform = 'rotate(20deg)';
-      target.style[idlName] = '0 1 0 -45deg';
+      target.style[idlName] = 'y -45deg';
       const animation =
         target.animate({ [idlName]: ['0 1 0 90deg',
                                      '0 1 0 180deg'] },
                        { duration: 1000, fill: 'both', composite: 'add' });
 
       testAnimationSamples(animation, idlName,
-        [{ time: 0,    expected: '0 1 0 45deg' },
-         { time: 1000, expected: '0 1 0 135deg' }]);
+        [{ time: 0,    expected: 'y 45deg' },
+         { time: 1000, expected: 'y 135deg' }]);
     }, `${property} with underlying transform`);
 
     test(t => {
@@ -1816,8 +1816,8 @@
                        { duration: 1000, fill: 'both', composite: 'accumulate' });
 
       testAnimationSamples(animation, idlName,
-        [{ time: 0,    expected: '1 0 0 45deg' },
-         { time: 1000, expected: '1 0 0 135deg' }]);
+        [{ time: 0,    expected: 'x 45deg' },
+         { time: 1000, expected: 'x 135deg' }]);
     }, `${property} with underlying transform`);
 
     test(t => {
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual-automation.js
index d1a201f..d45280c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual-automation.js
@@ -6,6 +6,6 @@
   }).then(function() {
     return touchScrollInTarget('#target0', 'down');
   }).then(function() {
-    return touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual-automation.js
index 8eb08e6..c3687c6d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual-automation.js
@@ -6,6 +6,6 @@
   }).then(function() {
     return touchScrollInTarget('#target0', 'up');
   }).then(function() {
-    return touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-none-css_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-none-css_touch-manual-automation.js
index f2e9074..77e7e61 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-none-css_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-none-css_touch-manual-automation.js
@@ -4,7 +4,7 @@
   return touchScrollInTarget('#target0', 'down').then(function() {
     return touchScrollInTarget('#target0', 'right');
   }).then(function() {
-    return touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual-automation.js
index f2e9074..77e7e61 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual-automation.js
@@ -4,7 +4,7 @@
   return touchScrollInTarget('#target0', 'down').then(function() {
     return touchScrollInTarget('#target0', 'right');
   }).then(function() {
-    return touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual-automation.js
index 48ddb2a1e..85d90a3 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual-automation.js
@@ -4,6 +4,6 @@
   return touchScrollInTarget('#target0', 'down').then(function() {
     return touchScrollInTarget('#target0', 'right');
   }).then(function() {
-    return touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-span-test_touch-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-span-test_touch-manual-automation.js
index 5e4f4c3..4734ff3 100644
--- a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-span-test_touch-manual-automation.js
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_touch-action-span-test_touch-manual-automation.js
@@ -10,6 +10,6 @@
   }).then(function() {
     return touchScrollInTarget('#target0 > span', 'right');
   }).then(function() {
-    touchTapInTarget('#btnComplete');
+    btnComplete.click();
   });
 }
diff --git a/third_party/WebKit/LayoutTests/fast/dom/constructed-objects-prototypes-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/constructed-objects-prototypes-expected.txt
index 624aaabd..863dddd7 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/constructed-objects-prototypes-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/constructed-objects-prototypes-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 33: 'webkitURL' is deprecated. Please use 'URL' instead.
 CONSOLE WARNING: line 33: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Make sure prototypes are set up using the window a property came from, instead of the lexical global object.
 
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/check-instanceof-domurl-functions-expected.txt b/third_party/WebKit/LayoutTests/fast/domurl/check-instanceof-domurl-functions-expected.txt
index 98d48a3..fdd574e 100644
--- a/third_party/WebKit/LayoutTests/fast/domurl/check-instanceof-domurl-functions-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/domurl/check-instanceof-domurl-functions-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: 'webkitURL' is deprecated. Please use 'URL' instead.
 Test instanceof functions and properties of URL and webkitURL.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon-expected.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon-expected.html
index cae8a8e2..09393465 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon-expected.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon-expected.html
@@ -14,6 +14,7 @@
     overflow: auto;
     border: solid 3px #cc0000;
     font-size: 80px;
+    outline: none;
 }
 </style>
 </head>
diff --git a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon.html b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon.html
index f7eb658..cdc7ff4 100644
--- a/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon.html
+++ b/third_party/WebKit/LayoutTests/fast/events/middleClickAutoscroll-panIcon.html
@@ -14,6 +14,7 @@
     overflow: auto;
     border: solid 3px #cc0000;
     font-size: 80px;
+    outline: none;
 }
 </style>
 <script>
diff --git a/third_party/WebKit/LayoutTests/fast/js/global-constructors-expected.txt b/third_party/WebKit/LayoutTests/fast/js/global-constructors-expected.txt
index 3aa352c..dffee2d 100644
--- a/third_party/WebKit/LayoutTests/fast/js/global-constructors-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/js/global-constructors-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 8: 'webkitURL' is deprecated. Please use 'URL' instead.
 CONSOLE WARNING: line 8: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 This test documents our set of global constructors we expose on the window object (FF and Opera don't expose them on the window, btw). This also checks to make sure than any constructor attribute we expose has the expected constructor type.
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/resources/snav-testharness.js b/third_party/WebKit/LayoutTests/fast/spatial-navigation/resources/snav-testharness.js
index 4840282..b9b02fd4 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/resources/snav-testharness.js
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/resources/snav-testharness.js
@@ -20,6 +20,12 @@
       case 'Left':
         eventSender.keyDown('ArrowLeft');
         break;
+      case 'Forward':
+        eventSender.keyDown('Tab');
+        break;
+      case 'Backward':
+        eventSender.keyDown('Tab', ['shiftKey']);
+        break;
     }
   }
 
@@ -89,8 +95,9 @@
       }, 'window.testRunner is present.');
     },
 
-    assertFocusMoves: function(expectedMoves) {
-      snav.assertSnavEnabledAndTestable();
+    assertFocusMoves: function(expectedMoves, enableSpatnav=true) {
+      if (enableSpatnav)
+        snav.assertSnavEnabledAndTestable();
       gAsyncTest = async_test("Focus movements:\n" +
           JSON.stringify(expectedMoves).replace(/],/g, ']\n') + '\n');
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-clipped-overflowed-content.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-clipped-overflowed-content.html
index 4459731..a0075440 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-clipped-overflowed-content.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-clipped-overflowed-content.html
@@ -27,6 +27,13 @@
 <script src="../../resources/testharnessreport.js"></script>
 <script src="resources/snav-testharness.js"></script>
 <script>
+  test(function(t) {
+      assert_true(internals.runtimeFlags.keyboardFocusableScrollersEnabled);
+  }, "Make sure KeyboardFocusableScrollers is set.");
+
+  // These expectations will change once crbug.com/801162 lands:
+  // We want spatnav, to focus the scroller before scrolling it
+  // or searching inside of it, just like seqnav does.
   var resultMap = [
     ["Down", "1"],
     ["Down", "1"],
@@ -40,6 +47,7 @@
     ["Right", "3"],
     ["Right", "4"],
     ["Down", "5"],
+    ["Up", "4"],
     ["Up", "2"],
     ["Up", "2"],
     ["Up", "2"],
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
index 57f6fc1..c93c587 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
@@ -10,11 +10,11 @@
 </style>
 
 <div><a id="start" href="a"><img src="resources/green.png" width=30 height=30></a></div>
-<div class="scroll">
+<div class="scroll" id="scrollerA">
   <img src="resources/green.png" width=240 height=300>
 </div>
 
-<div class="scroll">
+<div class="scroll" id="scrollerB">
   <img src="resources/green.png" width=240 height=300>
 </div>
 <div><a id="end" href="a"><img src="resources/green.png" width=30 height=30></a></div>
@@ -24,20 +24,26 @@
 <script src="../../resources/testharnessreport.js"></script>
 <script src="resources/snav-testharness.js"></script>
 <script>
+  test(function(t) {
+      assert_true(internals.runtimeFlags.keyboardFocusableScrollersEnabled);
+  }, "Make sure KeyboardFocusableScrollers is set.");
+
+  // These expectations will change once crbug.com/801162 lands:
+  // We want spatnav, to focus the scroller before scrolling it
+  // or searching inside of it, just like seqnav does.
   var resultMap = [
     ["Down", "start"],
     ["Down", "start"],
     ["Down", "start"],
-    ["Down", "start"],
-    ["Down", "start"],
-    ["Down", "start"],
-    ["Down", "end"],
-    ["Up"  , "end"],
-    ["Up"  , "end"],
-    ["Up"  , "end"],
-    ["Up"  , "end"],
-    ["Up"  , "end"],
-    ["Up"  , "end"],
+    ["Down", "scrollerA"],
+    ["Down", "scrollerA"],
+    ["Down", "scrollerA"],
+    ["Down", "scrollerA"],
+    ["Down", "scrollerB"],
+    ["Up"  , "scrollerB"],
+    ["Up"  , "scrollerB"],
+    ["Up"  , "scrollerB"],
+    ["Up"  , "scrollerA"],
     ["Up"  , "start"],
   ];
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-search-outside-of-focused-scroller.html b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-search-outside-of-focused-scroller.html
index 96acb78..269f9694 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-search-outside-of-focused-scroller.html
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-search-outside-of-focused-scroller.html
@@ -13,7 +13,7 @@
 <a id="end" href="#">end</a>
 <br><br>
 <a id="1" href="#">1</a>
-<div id="start" tabindex="0">
+<div id="start">
   <br>
   <p>text</p>
 </div>
@@ -23,7 +23,12 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="resources/snav-testharness.js"></script>
+
 <script>
+  test(function(t) {
+      assert_true(internals.runtimeFlags.keyboardFocusableScrollersEnabled);
+  }, "Make sure KeyboardFocusableScrollers is set.");
+
   var resultMap = [
     ["Up", "end"],
   ];
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/harness-tests/special-characters-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/harness-tests/special-characters-expected.txt
new file mode 100644
index 0000000..8c60ce2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/harness-tests/special-characters-expected.txt
@@ -0,0 +1,143 @@
+This is a testharness.js-based test.
+PASS Unescaped \ in test name.
+PASS Escaped "\\" in test name.
+FAIL Unescaped "\\" in test message. Expected Error: \ character.
+FAIL Escaped "\\" in test message. Expected Error: "\\" character.
+PASS Unescaped \r
+ in test name.
+PASS Escaped "\r\n" in test name.
+FAIL Unescaped "\r\n" in test message. Expected Error: \r
+ character.
+FAIL Escaped "\r\n" in test message. Expected Error: "\r\n" character.
+PASS Unescaped \0 in test name.
+PASS Escaped "\0" in test name.
+FAIL Unescaped "\0" in test message. Expected Error: \0 character.
+FAIL Escaped "\0" in test message. Expected Error: "\0" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x01" in test name.
+FAIL Unescaped "\x01" in test message. Expected Error:  character.
+FAIL Escaped "\x01" in test message. Expected Error: "\x01" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x02" in test name.
+FAIL Unescaped "\x02" in test message. Expected Error:  character.
+FAIL Escaped "\x02" in test message. Expected Error: "\x02" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x03" in test name.
+FAIL Unescaped "\x03" in test message. Expected Error:  character.
+FAIL Escaped "\x03" in test message. Expected Error: "\x03" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x04" in test name.
+FAIL Unescaped "\x04" in test message. Expected Error:  character.
+FAIL Escaped "\x04" in test message. Expected Error: "\x04" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x05" in test name.
+FAIL Unescaped "\x05" in test message. Expected Error:  character.
+FAIL Escaped "\x05" in test message. Expected Error: "\x05" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x06" in test name.
+FAIL Unescaped "\x06" in test message. Expected Error:  character.
+FAIL Escaped "\x06" in test message. Expected Error: "\x06" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x07" in test name.
+FAIL Unescaped "\x07" in test message. Expected Error:  character.
+FAIL Escaped "\x07" in test message. Expected Error: "\x07" character.
+PASS Unescaped  in test name.
+PASS Escaped "\b" in test name.
+FAIL Unescaped "\b" in test message. Expected Error:  character.
+FAIL Escaped "\b" in test message. Expected Error: "\b" character.
+PASS Unescaped 	 in test name.
+PASS Escaped "\t" in test name.
+FAIL Unescaped "\t" in test message. Expected Error: 	 character.
+FAIL Escaped "\t" in test message. Expected Error: "\t" character.
+PASS Unescaped 
+ in test name.
+PASS Escaped "\n" in test name.
+FAIL Unescaped "\n" in test message. Expected Error: 
+ character.
+FAIL Escaped "\n" in test message. Expected Error: "\n" character.
+PASS Unescaped  in test name.
+PASS Escaped "\v" in test name.
+FAIL Unescaped "\v" in test message. Expected Error:  character.
+FAIL Escaped "\v" in test message. Expected Error: "\v" character.
+PASS Unescaped  in test name.
+PASS Escaped "\f" in test name.
+FAIL Unescaped "\f" in test message. Expected Error:  character.
+FAIL Escaped "\f" in test message. Expected Error: "\f" character.
+PASS Unescaped \r in test name.
+PASS Escaped "\r" in test name.
+FAIL Unescaped "\r" in test message. Expected Error: \r character.
+FAIL Escaped "\r" in test message. Expected Error: "\r" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x0e" in test name.
+FAIL Unescaped "\x0e" in test message. Expected Error:  character.
+FAIL Escaped "\x0e" in test message. Expected Error: "\x0e" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x0f" in test name.
+FAIL Unescaped "\x0f" in test message. Expected Error:  character.
+FAIL Escaped "\x0f" in test message. Expected Error: "\x0f" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x10" in test name.
+FAIL Unescaped "\x10" in test message. Expected Error:  character.
+FAIL Escaped "\x10" in test message. Expected Error: "\x10" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x11" in test name.
+FAIL Unescaped "\x11" in test message. Expected Error:  character.
+FAIL Escaped "\x11" in test message. Expected Error: "\x11" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x12" in test name.
+FAIL Unescaped "\x12" in test message. Expected Error:  character.
+FAIL Escaped "\x12" in test message. Expected Error: "\x12" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x13" in test name.
+FAIL Unescaped "\x13" in test message. Expected Error:  character.
+FAIL Escaped "\x13" in test message. Expected Error: "\x13" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x14" in test name.
+FAIL Unescaped "\x14" in test message. Expected Error:  character.
+FAIL Escaped "\x14" in test message. Expected Error: "\x14" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x15" in test name.
+FAIL Unescaped "\x15" in test message. Expected Error:  character.
+FAIL Escaped "\x15" in test message. Expected Error: "\x15" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x16" in test name.
+FAIL Unescaped "\x16" in test message. Expected Error:  character.
+FAIL Escaped "\x16" in test message. Expected Error: "\x16" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x17" in test name.
+FAIL Unescaped "\x17" in test message. Expected Error:  character.
+FAIL Escaped "\x17" in test message. Expected Error: "\x17" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x18" in test name.
+FAIL Unescaped "\x18" in test message. Expected Error:  character.
+FAIL Escaped "\x18" in test message. Expected Error: "\x18" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x19" in test name.
+FAIL Unescaped "\x19" in test message. Expected Error:  character.
+FAIL Escaped "\x19" in test message. Expected Error: "\x19" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1a" in test name.
+FAIL Unescaped "\x1a" in test message. Expected Error:  character.
+FAIL Escaped "\x1a" in test message. Expected Error: "\x1a" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1b" in test name.
+FAIL Unescaped "\x1b" in test message. Expected Error:  character.
+FAIL Escaped "\x1b" in test message. Expected Error: "\x1b" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1c" in test name.
+FAIL Unescaped "\x1c" in test message. Expected Error:  character.
+FAIL Escaped "\x1c" in test message. Expected Error: "\x1c" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1d" in test name.
+FAIL Unescaped "\x1d" in test message. Expected Error:  character.
+FAIL Escaped "\x1d" in test message. Expected Error: "\x1d" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1e" in test name.
+FAIL Unescaped "\x1e" in test message. Expected Error:  character.
+FAIL Escaped "\x1e" in test message. Expected Error: "\x1e" character.
+PASS Unescaped  in test name.
+PASS Escaped "\x1f" in test name.
+FAIL Unescaped "\x1f" in test message. Expected Error:  character.
+FAIL Escaped "\x1f" in test message. Expected Error: "\x1f" character.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/form-method-dialog-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/form-method-dialog-expected.txt
deleted file mode 100644
index d669470..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/form-method-dialog-expected.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-CONSOLE WARNING: line 177: Element.createShadowRoot is deprecated and will be removed in M73, around March 2019. Please use Element.attachShadow instead. See https://www.chromestatus.com/features/4507242028072960 for more details.
-Tests form submission with method=dialog
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Clicking outer-submit-empty-string
-PASS outer; dialog.open is false
-PASS outer; dialog.returnValue is ""
-PASS inner; dialog.open is true
-PASS inner; dialog.returnValue is "init"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking outer-submit-no-value
-PASS outer; dialog.open is false
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is true
-PASS inner; dialog.returnValue is "init"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking inner-submit-yes
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is false
-PASS inner; dialog.returnValue is "Yes"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking inner-submit-no
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is false
-PASS inner; dialog.returnValue is "No"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking no-dialog-ancestor-1
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is true
-PASS inner; dialog.returnValue is "init"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking no-dialog-ancestor-2
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is true
-PASS inner; dialog.returnValue is "init"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking host-submit-yes
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is true
-PASS inner; dialog.returnValue is "init"
-PASS host; dialog.open is false
-PASS host; dialog.returnValue is "Yes"
-
-Submitting a form without submit button (this should not crash)
-
-Clicking a button in a closed dialog
-PASS dialog.open is false
-PASS dialog.returnValue is "init"
-
-Activating an image button by click()
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is false
-PASS inner; dialog.returnValue is "0,0"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Activating an image button by keyboard
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is false
-PASS inner; dialog.returnValue is "0,0"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-
-Clicking an image button
-PASS outer; dialog.open is true
-PASS outer; dialog.returnValue is "init"
-PASS inner; dialog.open is false
-PASS inner; dialog.returnValue is "10,5"
-PASS host; dialog.open is true
-PASS host; dialog.returnValue is "init"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
- 
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-label-focus-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-label-focus-expected.txt
deleted file mode 100644
index b18fe40..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-label-focus-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Tests focusing of an inert label for a non-inert target. label.focus() should send focus to the target, but clicking the label should be the same as clicking on the document body.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Testing that calling focus() on label sends focus to its target.
-PASS document.activeElement is document.querySelector('#submit')
-Testing that clicking on the label sends focus to document.body.
-PASS document.activeElement is document.body
-PASS successfullyParsed is true
-
-TEST COMPLETE
-Label for Submit 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-node-is-unselectable-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-node-is-unselectable-expected.txt
deleted file mode 100644
index 1df1516..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/dialog/inert-node-is-unselectable-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS window.getSelection().toString() is "I'm selectable."
-PASS successfullyParsed is true
-
-TEST COMPLETE
-Here is a text node you can't select.I'm selectable.
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/tabular_data/table_border_invalid-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/tabular_data/table_border_invalid-expected.txt
deleted file mode 100644
index f79df87..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/html/tabular_data/table_border_invalid-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-https://bugs.webkit.org/show_bug.cgi?id=102112: There should be a black box below.
-PASS getComputedStyle(document.querySelector("table")).borderTopWidth is "1px"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-expected.txt
deleted file mode 100644
index bac9c71..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-   
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-png-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-png-expected.txt
deleted file mode 100644
index bac9c71..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-background-image-cross-fade-png-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-   
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-filter-all-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-filter-all-expected.txt
deleted file mode 100644
index 1ada1b7..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-filter-all-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-
-GRAYSCALE
-
- 
-NONE
-
- 
-BRIGHTNESS
-
- 
-SATURATION
-
- 
-SEPIA
-
- 
-BLUR
-
- 
-OPACITY
-
- 
-BLUR+HUE
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-pseudo-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-pseudo-content-expected.txt
deleted file mode 100644
index a0a2ef28..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-image-pseudo-content-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
- pseudo-content image replacement
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-adobe-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-adobe-to-srgb-expected.txt
deleted file mode 100644
index 14fa349..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-adobe-to-srgb-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-
-Color         Actual        Expected      dE
---------------------------------------------
-Dark Skin     114,80,64     115,80,64     1
-Light Skin    195,150,130   195,151,130   1
-Blue Sky      94,123,156    94,123,156    0
-Foliage       88,108,65     88,108,65     0
-Blue Flower   130,129,177   130,129,177   0
-Bluish Green  100,190,171   100,190,171   0
---------------------------------------------
-Orange        216,121,37    217,122,37    1
-Purplish Blue 72,90,166     72,91,165     1
-Moderate Red  194,84,97     194,84,98     1
-Purple        90,60,106     91,59,107     2
-Yellow Green  160,188,60    160,188,60    0
-Orange Yellow 231,163,42    230,163,42    1
---------------------------------------------
-Blue          47,60,153     46,60,153     1
-Green         70,149,69     71,150,69     1
-Red           177,45,56     177,44,56     1
-Yellow        239,200,27    238,200,27    1
-Magenta       187,82,147    187,82,148    1
-Cyan (*)      0,135,165     0,135,166     1
---------------------------------------------
-White         243,242,236   243,242,237   1
-Neutral 8     201,202,200   201,201,201   1
-Neutral 6.5   160,161,160   161,161,161   1
-Neutral 5     123,121,120   122,122,121   2
-Neutral 3.5   83,83,83      83,83,83      0
-Black         50,50,50      50,49,50      1
---------------------------------------------
-
-Result: total RMS color error: 1.00
-* Munsell Cyan is outside 255 sRGB gamut
-
-  
-
-
-
-
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-srgb-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-srgb-to-srgb-expected.txt
deleted file mode 100644
index 74fab75..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/color-profile-munsell-srgb-to-srgb-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-
-Color         Actual        Expected      dE
---------------------------------------------
-Dark Skin     115,80,64     115,80,64     0
-Light Skin    195,151,130   195,151,130   0
-Blue Sky      94,123,156    94,123,156    0
-Foliage       88,108,65     88,108,65     0
-Blue Flower   130,129,177   130,129,177   0
-Bluish Green  100,190,171   100,190,171   0
---------------------------------------------
-Orange        217,122,37    217,122,37    0
-Purplish Blue 72,91,165     72,91,165     0
-Moderate Red  194,84,98     194,84,98     0
-Purple        91,59,107     91,59,107     0
-Yellow Green  160,188,60    160,188,60    0
-Orange Yellow 230,163,42    230,163,42    0
---------------------------------------------
-Blue          46,60,153     46,60,153     0
-Green         71,150,69     71,150,69     0
-Red           177,44,56     177,44,56     0
-Yellow        238,200,27    238,200,27    0
-Magenta       187,82,148    187,82,148    0
-Cyan (*)      0,135,166     0,135,166     0
---------------------------------------------
-White         243,242,237   243,242,237   0
-Neutral 8     201,201,201   201,201,201   0
-Neutral 6.5   161,161,161   161,161,161   0
-Neutral 5     122,122,121   122,122,121   0
-Neutral 3.5   83,83,83      83,83,83      0
-Black         50,49,50      50,49,50      0
---------------------------------------------
-
-Result: total RMS color error: 0.00
-* Munsell Cyan is outside 255 sRGB gamut
-
-  
-
-
-
-
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-css-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-css-expected.txt
deleted file mode 100644
index 55dce31..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-css-expected.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-None of the images should be rotated. This test is only valid when run with testRunner (or with WebKitShouldRespectImageOrientation manually set to true).
-
-
-Normal 
-Flipped horizontally 
-Rotated 180° 
-Flipped vertically
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Undefined (invalid value)
-img1 size = 100px by 50px
-img2 size = 100px by 50px
-img3 size = 100px by 50px
-img4 size = 100px by 50px
-img5 size = 100px by 50px
-img6 size = 100px by 50px
-img7 size = 100px by 50px
-img8 size = 100px by 50px
-img9 size = 100px by 100px
-img10 size = 100px by 100px
-img11 size = 100px by 100px
-img12 size = 100px by 100px
-img13 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-expected.txt
deleted file mode 100644
index e9b281fd2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-The images should be rotated respecting their EXIF orientation. This test can only be run with testRunner (or by manually setting WebKitShouldRespectImageOrientation to true).
-
-
-Normal 
-Flipped horizontally 
-Rotated 180° 
-Flipped vertically
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Undefined (invalid value)
-img1 size = 100px by 50px
-img2 size = 100px by 50px
-img3 size = 100px by 50px
-img4 size = 100px by 50px
-img5 size = 50px by 100px
-img6 size = 50px by 100px
-img7 size = 50px by 100px
-img8 size = 50px by 100px
-img9 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-image-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-image-document-expected.txt
deleted file mode 100644
index 625076d3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/exif-orientation-image-document-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-The images should be rotated respecting their EXIF orientation. In image documents, this happens independent of WebKitShouldRespectImageOrientation.
-
-   
-   
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-expected.txt
deleted file mode 100644
index 262e51b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-This tests image map behavior when there are multiple maps with the same name.
-1: PASS: Hit the first map in the document.
-2: PASS: Hit the second map after the first was renamed.
-3: PASS: Hit the first map after it was renamed back.
-4: PASS: Hit the second map after the first was removed.
-5: PASS: Hit the first map after it was added back.
-6: PASS: Hit the first map after the second was removed.
-7: PASS: Hit the first map after the second was re-added.
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-xhtml-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-xhtml-expected.txt
deleted file mode 100644
index 262e51b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-multiple-xhtml-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-This tests image map behavior when there are multiple maps with the same name.
-1: PASS: Hit the first map in the document.
-2: PASS: Hit the second map after the first was renamed.
-3: PASS: Hit the first map after it was renamed back.
-4: PASS: Hit the second map after the first was removed.
-5: PASS: Hit the first map after it was added back.
-6: PASS: Hit the first map after the second was removed.
-7: PASS: Hit the first map after the second was re-added.
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-alt-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-alt-content-expected.txt
deleted file mode 100644
index e7cfca9f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-alt-content-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-This tests that client side image maps still work when zooming is in effect.
-
-SUCCESS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-expected.txt
deleted file mode 100644
index e7cfca9f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/image-map-zoom-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-This tests that client side image maps still work when zooming is in effect.
-
-SUCCESS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/imagemap-nested-area-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/imagemap-nested-area-expected.txt
deleted file mode 100644
index 772223c..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/imagemap-nested-area-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This tests that pressing Tab focuses areas in an image map even if they're not direct children.
-Test Passed
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/png-extra-row-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/png-extra-row-crash-expected.txt
deleted file mode 100644
index b75a3ecc..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/png-extra-row-crash-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-The following PNG will attempt to render a bad row. If the test succeeds this should not crash. 
-PASS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/inspector-protocol/page/capture-snapshot-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/inspector-protocol/page/capture-snapshot-expected.txt
new file mode 100644
index 0000000..67e795f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/inspector-protocol/page/capture-snapshot-expected.txt
@@ -0,0 +1,15 @@
+Tests capturing MHTML snapshots.
+
+Capturing without specified format:
+From: <Saved by Blink><Snapshot-Content-Location: >Subject: <Date: ><MIME-Version: >Content-Type: multipart/related;	type="text/html";<boundary="----MultipartBoundary--><------MultipartBoundary-->Content-Type: text/html<Content-ID: ><Content-Transfer-Encoding: ><Content-Location: ><html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset==3Dwindows-1252"></head><body><div id=3D"x" class=3D"container">      <p style=3D"color: red">Text</p>     =20    </div>    </body></html><------MultipartBoundary-->
+
+Capturing with format: foo
+{
+    error : {
+        code : -32000
+        message : Unsupported snapshot format
+    }
+    id : <number>
+    sessionId : <string>
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-controls-visible-audio-only-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-controls-visible-audio-only-expected.txt
deleted file mode 100644
index a78bd29..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-controls-visible-audio-only-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Test video element control visibility when mouse is not over element.
-
-This test only runs in DRT!
-
-mouse parks here, am I blue?
-
-
-
-TEST(video.paused) OK
-TEST(!video.paused) OK
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.txt
deleted file mode 100644
index a0a2ef28..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
- pseudo-content image replacement
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/crash-bad-cast-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/crash-bad-cast-expected.txt
deleted file mode 100644
index 8d1c8b6..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/crash-bad-cast-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
- 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-css-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-css-expected.txt
deleted file mode 100644
index 55dce31..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-css-expected.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-None of the images should be rotated. This test is only valid when run with testRunner (or with WebKitShouldRespectImageOrientation manually set to true).
-
-
-Normal 
-Flipped horizontally 
-Rotated 180° 
-Flipped vertically
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Undefined (invalid value)
-img1 size = 100px by 50px
-img2 size = 100px by 50px
-img3 size = 100px by 50px
-img4 size = 100px by 50px
-img5 size = 100px by 50px
-img6 size = 100px by 50px
-img7 size = 100px by 50px
-img8 size = 100px by 50px
-img9 size = 100px by 100px
-img10 size = 100px by 100px
-img11 size = 100px by 100px
-img12 size = 100px by 100px
-img13 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-expected.txt
deleted file mode 100644
index e9b281fd2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-The images should be rotated respecting their EXIF orientation. This test can only be run with testRunner (or by manually setting WebKitShouldRespectImageOrientation to true).
-
-
-Normal 
-Flipped horizontally 
-Rotated 180° 
-Flipped vertically
-
-Rotated 90° CCW and flipped vertically 
-Rotated 90° CCW 
-Rotated 90° CW and flipped vertically 
-Rotated 90° CW
-
-Undefined (invalid value)
-img1 size = 100px by 50px
-img2 size = 100px by 50px
-img3 size = 100px by 50px
-img4 size = 100px by 50px
-img5 size = 50px by 100px
-img6 size = 50px by 100px
-img7 size = 50px by 100px
-img8 size = 50px by 100px
-img9 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-image-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-image-document-expected.txt
deleted file mode 100644
index 625076d3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/exif-orientation-image-document-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-The images should be rotated respecting their EXIF orientation. In image documents, this happens independent of WebKitShouldRespectImageOrientation.
-
-   
-   
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-expected.txt
deleted file mode 100644
index 262e51b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-This tests image map behavior when there are multiple maps with the same name.
-1: PASS: Hit the first map in the document.
-2: PASS: Hit the second map after the first was renamed.
-3: PASS: Hit the first map after it was renamed back.
-4: PASS: Hit the second map after the first was removed.
-5: PASS: Hit the first map after it was added back.
-6: PASS: Hit the first map after the second was removed.
-7: PASS: Hit the first map after the second was re-added.
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-xhtml-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-xhtml-expected.txt
deleted file mode 100644
index 262e51b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-multiple-xhtml-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-This tests image map behavior when there are multiple maps with the same name.
-1: PASS: Hit the first map in the document.
-2: PASS: Hit the second map after the first was renamed.
-3: PASS: Hit the first map after it was renamed back.
-4: PASS: Hit the second map after the first was removed.
-5: PASS: Hit the first map after it was added back.
-6: PASS: Hit the first map after the second was removed.
-7: PASS: Hit the first map after the second was re-added.
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-alt-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-alt-content-expected.txt
deleted file mode 100644
index e7cfca9f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-alt-content-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-This tests that client side image maps still work when zooming is in effect.
-
-SUCCESS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-expected.txt
deleted file mode 100644
index e7cfca9f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/image-map-zoom-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
-This tests that client side image maps still work when zooming is in effect.
-
-SUCCESS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/png-extra-row-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/png-extra-row-crash-expected.txt
deleted file mode 100644
index b75a3ecc..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/png-extra-row-crash-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-The following PNG will attempt to render a bad row. If the test succeeds this should not crash. 
-PASS
diff --git a/third_party/WebKit/LayoutTests/hittesting/text-overflow-inline-image-expected.txt b/third_party/WebKit/LayoutTests/hittesting/text-overflow-inline-image-expected.txt
index 6f2c420..b1f318bb 100644
--- a/third_party/WebKit/LayoutTests/hittesting/text-overflow-inline-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/hittesting/text-overflow-inline-image-expected.txt
@@ -1,4 +1,3 @@
-
 this is a link
 clicked
 
diff --git a/third_party/WebKit/LayoutTests/html/details_summary/details-children-merge-crash-expected.txt b/third_party/WebKit/LayoutTests/html/details_summary/details-children-merge-crash-expected.txt
index 1042c767..0383162 100644
--- a/third_party/WebKit/LayoutTests/html/details_summary/details-children-merge-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/details_summary/details-children-merge-crash-expected.txt
@@ -1,2 +1 @@
 Test passes if it does not crash.
-
diff --git a/third_party/WebKit/LayoutTests/html/details_summary/details-clone-expected.txt b/third_party/WebKit/LayoutTests/html/details_summary/details-clone-expected.txt
index 0d32574..a99cde2 100644
--- a/third_party/WebKit/LayoutTests/html/details_summary/details-clone-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/details_summary/details-clone-expected.txt
@@ -3,5 +3,5 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
-PASS targetMarkerPseudoId is clonedMarkerPseudoId
 
+PASS targetMarkerPseudoId is clonedMarkerPseudoId
diff --git a/third_party/WebKit/LayoutTests/html/details_summary/details-keyboard-show-hide-expected.txt b/third_party/WebKit/LayoutTests/html/details_summary/details-keyboard-show-hide-expected.txt
index 0baed54..5b442b90 100644
--- a/third_party/WebKit/LayoutTests/html/details_summary/details-keyboard-show-hide-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/details_summary/details-keyboard-show-hide-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS openAttribute("details") is false
 Toggle <display> using Enter key:
 PASS openAttribute("details") is true
diff --git a/third_party/WebKit/LayoutTests/html/details_summary/details-open-toggle-event-expected.txt b/third_party/WebKit/LayoutTests/html/details_summary/details-open-toggle-event-expected.txt
index ca440f1..19f798bd 100644
--- a/third_party/WebKit/LayoutTests/html/details_summary/details-open-toggle-event-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/details_summary/details-open-toggle-event-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS details.ontoggle.__proto__ is Function.prototype
 PASS details.open is false
 PASS toggleEventCount is 0
@@ -26,4 +25,5 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 details
diff --git a/third_party/WebKit/LayoutTests/html/dialog/closed-dialog-does-not-block-mouse-events-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/closed-dialog-does-not-block-mouse-events-expected.txt
index 749a905..fd6617bc 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/closed-dialog-does-not-block-mouse-events-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/closed-dialog-does-not-block-mouse-events-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS div.firedOn is true
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/dialog-canceling-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/dialog-canceling-expected.txt
index 6074e22..851d8d5 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/dialog-canceling-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/dialog-canceling-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Top dialog event listener should prevent closing.
 PASS topDialog.open is true
 PASS bottomDialog.open is true
diff --git a/third_party/WebKit/LayoutTests/html/dialog/dialog-close-event-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/dialog-close-event-expected.txt
index aa11b0e..9f711cb 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/dialog-close-event-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/dialog-close-event-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS dialog.open is false
 PASS closedCount is 0
 PASS selfDialog is dialog
diff --git a/third_party/WebKit/LayoutTests/html/dialog/dialog-open-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/dialog-open-expected.txt
index f0f9679..f6ed04d 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/dialog-open-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/dialog-open-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS computedStyle.getPropertyValue('display') is 'none'
 PASS computedStyle.getPropertyValue('display') is 'block'
 PASS computedStyle.getPropertyValue('display') is 'none'
diff --git a/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-expected.txt
index 1656400..0e142eb6 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS computedStyle.getPropertyValue('display') is "none"
 PASS computedStyle.getPropertyValue('display') is "block"
 "If dialog already has an open attribute, then throw an InvalidStateError exception."
diff --git a/third_party/WebKit/LayoutTests/html/dialog/fixpos-dialog-layout-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/fixpos-dialog-layout-expected.txt
index 0c8ae9f4e..a75e851 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/fixpos-dialog-layout-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/fixpos-dialog-layout-expected.txt
@@ -3,7 +3,6 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-
 showModal() should center in the viewport.
 PASS dialog.getBoundingClientRect().top is centeredTop
 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/form-method-dialog-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/form-method-dialog-expected.txt
index a61e927..23100ef 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/form-method-dialog-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/form-method-dialog-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Clicking outer-submit-empty-string
 PASS outer; dialog.open is false
 PASS outer; dialog.returnValue is ""
@@ -12,6 +11,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking outer-submit-no-value
 PASS outer; dialog.open is false
 PASS outer; dialog.returnValue is "init"
@@ -20,6 +20,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking inner-submit-yes
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -28,6 +29,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking inner-submit-no
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -36,6 +38,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking no-dialog-ancestor-1
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -44,6 +47,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking no-dialog-ancestor-2
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -52,6 +56,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking host-submit-yes
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -60,12 +65,15 @@
 PASS host; dialog.open is false
 PASS host; dialog.returnValue is "Yes"
 
+
 Submitting a form without submit button (this should not crash)
 
+
 Clicking a button in a closed dialog
 PASS dialog.open is false
 PASS dialog.returnValue is "init"
 
+
 Activating an image button by click()
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -74,6 +82,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Activating an image button by keyboard
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -82,6 +91,7 @@
 PASS host; dialog.open is true
 PASS host; dialog.returnValue is "init"
 
+
 Clicking an image button
 PASS outer; dialog.open is true
 PASS outer; dialog.returnValue is "init"
@@ -94,5 +104,5 @@
 TEST COMPLETE
 
 
-  
 
+ 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/inert-focus-in-frames-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/inert-focus-in-frames-expected.txt
index 5d27317..528b246 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/inert-focus-in-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/inert-focus-in-frames-expected.txt
@@ -2,10 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Opening a modal dialog in frame1. It blocks other nodes in its document.
 PASS "frame1-input"; focusedElement === theElement is false
 PASS "iframe-input"; focusedElement === theElement is false
diff --git a/third_party/WebKit/LayoutTests/html/dialog/inert-inlines-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/inert-inlines-expected.txt
index 96d17739..867d0ae 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/inert-inlines-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/inert-inlines-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 clicking on a
 PASS inertElementFiredOn is false
 PASS expectedTarget.firedOn is true
@@ -18,6 +17,7 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Click me Click me
 Click meeee
 Click me
diff --git a/third_party/WebKit/LayoutTests/html/dialog/inert-label-focus-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/inert-label-focus-expected.txt
index eb935a9..9f360b66 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/inert-label-focus-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/inert-label-focus-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Testing that calling focus() on label sends focus to its target.
 PASS document.activeElement is document.querySelector('#submit')
 Testing that clicking on the label sends focus to document.body.
@@ -10,4 +9,6 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
-Label for Submit   
+
+Label for Submit
+ 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-uneditable-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-uneditable-expected.txt
index 1221410..5f825bbc 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-uneditable-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-uneditable-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS notEditable.textContent is oldValue
 PASS editable.textContent is not oldValue
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-unselectable-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-unselectable-expected.txt
index a7120747..cb2f35916 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-unselectable-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/inert-node-is-unselectable-expected.txt
@@ -2,9 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS window.getSelection().toString() is "I'm selectable."
 PASS successfullyParsed is true
 
 TEST COMPLETE
-Here is a text node you can't select. I'm selectable.
+
+Here is a text node you can't select.
+I'm selectable.
diff --git a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt
index 212dbda21..1245060 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Clicking on ancestor
 PASS handledEvent.document is true
 PASS handledEvent.body is false
diff --git a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt
index 23785d3..64f6953 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Clicking on inert box
 PASS inertDivHandledEvent is false
 PASS Object.keys(handledEvents.document).length is expectedEventCountForDocument
diff --git a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-distributed-child-is-not-inert-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-distributed-child-is-not-inert-expected.txt
index b670497..82edeaa 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-distributed-child-is-not-inert-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/modal-dialog-distributed-child-is-not-inert-expected.txt
@@ -3,9 +3,9 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS button was clicked
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Click me
diff --git a/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-does-not-block-mouse-events-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-does-not-block-mouse-events-expected.txt
index 76ea6b0..0b89874 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-does-not-block-mouse-events-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-does-not-block-mouse-events-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS div.firedOn is true
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-layout-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-layout-expected.txt
index 69354e6..038be80 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-layout-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/non-modal-dialog-layout-expected.txt
@@ -3,7 +3,6 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-
 Test absolute position
 PASS dialog.getBoundingClientRect().top is relativeContainer.getBoundingClientRect().top + offset
 PASS dialog.getBoundingClientRect().left is relativeContainer.getBoundingClientRect().left + offset
diff --git a/third_party/WebKit/LayoutTests/html/dialog/scrollable-after-close-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/scrollable-after-close-expected.txt
index be0b87d..0d1512f 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/scrollable-after-close-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/scrollable-after-close-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS internals.numberOfScrollableAreas(document) is 1
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/html/dialog/simulated-click-inert-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/simulated-click-inert-expected.txt
index fde1d56..6b754e7 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/simulated-click-inert-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/simulated-click-inert-expected.txt
@@ -1,4 +1,5 @@
 Ensure that simulated click is still dispatched to an inert node. To test manually, click the CLICK ME label and verify it does change the value of the checkbox.
 
 PASS
+
 CLICK ME
diff --git a/third_party/WebKit/LayoutTests/html/dialog/submit-dialog-close-event-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/submit-dialog-close-event-expected.txt
index 117290d..5dd1d56 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/submit-dialog-close-event-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/submit-dialog-close-event-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS dialog.open is false
 PASS dialog.returnValue is "Goodbye"
 PASS dialog.open is false
diff --git a/third_party/WebKit/LayoutTests/html/dialog/synthetic-click-inert-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/synthetic-click-inert-expected.txt
index ad115cf..53e326e5 100644
--- a/third_party/WebKit/LayoutTests/html/dialog/synthetic-click-inert-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/dialog/synthetic-click-inert-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Calling click() on BUTTON
 PASS clicked is expectedElement
 Calling dispatchEvent() on BUTTON
@@ -14,6 +13,6 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Click me
 Click me too
-
diff --git a/third_party/WebKit/LayoutTests/html/document_metadata/base-expected.txt b/third_party/WebKit/LayoutTests/html/document_metadata/base-expected.txt
index 4f4c2eb..bfb05b3 100644
--- a/third_party/WebKit/LayoutTests/html/document_metadata/base-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/document_metadata/base-expected.txt
@@ -9,3 +9,4 @@
 
 TEST COMPLETE
 
+
diff --git a/third_party/WebKit/LayoutTests/html/document_metadata/base-multiple-expected.txt b/third_party/WebKit/LayoutTests/html/document_metadata/base-multiple-expected.txt
index 8afcc8f..7649053 100644
--- a/third_party/WebKit/LayoutTests/html/document_metadata/base-multiple-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/document_metadata/base-multiple-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS clean(anchor.href) is 'http://originalbase.com/file'
 PASS document.head.appendChild(base), clean(anchor.href) is 'http://domain.com/base/file'
 PASS base.href = 'http://domain.com/base-changed/', clean(anchor.href) is 'http://domain.com/base-changed/file'
diff --git a/third_party/WebKit/LayoutTests/html/document_metadata/head-check-expected.txt b/third_party/WebKit/LayoutTests/html/document_metadata/head-check-expected.txt
index 652da48..c3471c6 100644
--- a/third_party/WebKit/LayoutTests/html/document_metadata/head-check-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/document_metadata/head-check-expected.txt
@@ -3,20 +3,25 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Testing: head-check-1.html
 PASS iframe.contentWindow.document.getElementsByTagName('head').length is 1
 PASS iframe.contentWindow.document.firstChild.firstChild.nodeName.toLowerCase() is 'head'
 
+
 Testing: head-check-2.html
 PASS iframe.contentWindow.document.getElementsByTagName('head').length is 1
 PASS iframe.contentWindow.document.firstChild.firstChild.nodeName.toLowerCase() is 'head'
 
+
 Testing: head-check-3.html
 PASS iframe.contentWindow.document.getElementsByTagName('head').length is 1
 PASS iframe.contentWindow.document.firstChild.firstChild.nodeName.toLowerCase() is 'head'
 
+
 Testing: head-check-4.html
 PASS iframe.contentWindow.document.getElementsByTagName('head').length is 1
 PASS iframe.contentWindow.document.firstChild.firstChild.nodeName.toLowerCase() is 'head'
 
+
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/html/document_metadata/meta-attributes-expected.txt b/third_party/WebKit/LayoutTests/html/document_metadata/meta-attributes-expected.txt
index 77e76d1..b468e555 100644
--- a/third_party/WebKit/LayoutTests/html/document_metadata/meta-attributes-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/document_metadata/meta-attributes-expected.txt
@@ -1,4 +1,5 @@
 You should see 5 lines with "SUCCESS" below:
+
 SUCCESS (value: "[object HTMLMetaElement]")
 
 SUCCESS (value: "foo")
diff --git a/third_party/WebKit/LayoutTests/html/document_metadata/style-disabled-expected.txt b/third_party/WebKit/LayoutTests/html/document_metadata/style-disabled-expected.txt
index a982b0a..e23ae9a5 100644
--- a/third_party/WebKit/LayoutTests/html/document_metadata/style-disabled-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/document_metadata/style-disabled-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS styleElement.disabled is false
 PASS window.getComputedStyle(testElement).color is "rgb(0, 128, 0)"
 PASS styleElement.disabled is true
diff --git a/third_party/WebKit/LayoutTests/html/grouping_content/figcaption-element-expected.txt b/third_party/WebKit/LayoutTests/html/grouping_content/figcaption-element-expected.txt
index 9402231..46375a7 100644
--- a/third_party/WebKit/LayoutTests/html/grouping_content/figcaption-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/grouping_content/figcaption-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <figcaption> default styling:
 PASS getStyleValue("figcaption0","display") is "block"
 <figcaption> closes <p>:
diff --git a/third_party/WebKit/LayoutTests/html/grouping_content/figure-element-expected.txt b/third_party/WebKit/LayoutTests/html/grouping_content/figure-element-expected.txt
index 0a60a79..3b674aa 100644
--- a/third_party/WebKit/LayoutTests/html/grouping_content/figure-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/grouping_content/figure-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <figure> default styling:
 PASS getStyleValue("figure0","display") is "block"
 PASS getStyleValue("figure0","margin-top") is emSize
diff --git a/third_party/WebKit/LayoutTests/html/grouping_content/main-element-expected.txt b/third_party/WebKit/LayoutTests/html/grouping_content/main-element-expected.txt
index 44e917a..5c72e48 100644
--- a/third_party/WebKit/LayoutTests/html/grouping_content/main-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/grouping_content/main-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <main> closes <p>:
 PASS main1.parentNode.nodeName == "p" is false
 <p> does not close <main>:
diff --git a/third_party/WebKit/LayoutTests/html/marquee/marquee-direction-attribute-should-be-case-insensitive-expected.txt b/third_party/WebKit/LayoutTests/html/marquee/marquee-direction-attribute-should-be-case-insensitive-expected.txt
index 7179a5a9..1671e70a 100644
--- a/third_party/WebKit/LayoutTests/html/marquee/marquee-direction-attribute-should-be-case-insensitive-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/marquee/marquee-direction-attribute-should-be-case-insensitive-expected.txt
@@ -5,4 +5,5 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 The text should move down. The text should move up. The text should move right. The text should move left.
diff --git a/third_party/WebKit/LayoutTests/html/marquee/marquee-element-expected.txt b/third_party/WebKit/LayoutTests/html/marquee/marquee-element-expected.txt
index 9e8318a..a0a4801 100644
--- a/third_party/WebKit/LayoutTests/html/marquee/marquee-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/marquee/marquee-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 "behavior" IDL attribute reflects content attribute:
 PASS marquee.behavior is "slide"
 "behavior" IDL attribute can be modified:
diff --git a/third_party/WebKit/LayoutTests/html/marquee/marquee-should-not-wrap-expected.txt b/third_party/WebKit/LayoutTests/html/marquee/marquee-should-not-wrap-expected.txt
index 88f2f6c..a3214d3 100644
--- a/third_party/WebKit/LayoutTests/html/marquee/marquee-should-not-wrap-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/marquee/marquee-should-not-wrap-expected.txt
@@ -1,4 +1,5 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 This text should not wrap.
diff --git a/third_party/WebKit/LayoutTests/html/marquee/marquee-without-frame-no-crash-expected.txt b/third_party/WebKit/LayoutTests/html/marquee/marquee-without-frame-no-crash-expected.txt
index dea7c8c..3439534 100644
--- a/third_party/WebKit/LayoutTests/html/marquee/marquee-without-frame-no-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/marquee/marquee-without-frame-no-crash-expected.txt
@@ -2,4 +2,5 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 foo 
diff --git a/third_party/WebKit/LayoutTests/html/sections/article-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/article-element-expected.txt
index 6a6ba99..3ebff53 100644
--- a/third_party/WebKit/LayoutTests/html/sections/article-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/article-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <article> closes <p>:
 PASS article1.parentNode.nodeName == "p" is false
 <p> does not close <article>:
diff --git a/third_party/WebKit/LayoutTests/html/sections/aside-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/aside-element-expected.txt
index af08fa6..a552f04 100644
--- a/third_party/WebKit/LayoutTests/html/sections/aside-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/aside-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <aside> closes <p>:
 PASS aside1.parentNode.nodeName == "p" is false
 <p> does not close <aside>:
diff --git a/third_party/WebKit/LayoutTests/html/sections/body-clone-link-decl-parent-crash-expected.txt b/third_party/WebKit/LayoutTests/html/sections/body-clone-link-decl-parent-crash-expected.txt
index 48bdc14..69154eb2 100644
--- a/third_party/WebKit/LayoutTests/html/sections/body-clone-link-decl-parent-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/body-clone-link-decl-parent-crash-expected.txt
@@ -3,3 +3,4 @@
 
 TEST COMPLETE
 
+
diff --git a/third_party/WebKit/LayoutTests/html/sections/body-legacy-colors-expected.txt b/third_party/WebKit/LayoutTests/html/sections/body-legacy-colors-expected.txt
index 2820b0a..f8f0960 100644
--- a/third_party/WebKit/LayoutTests/html/sections/body-legacy-colors-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/body-legacy-colors-expected.txt
@@ -1,6 +1,7 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 PASS getComputedStyle(window.document.body).backgroundColor is white
 PASS internals.computedStyleIncludingVisitedInfo(vlink).color is green
 PASS internals.computedStyleIncludingVisitedInfo(alink).color is yellow
diff --git a/third_party/WebKit/LayoutTests/html/sections/body-quirk-client-size-expected.txt b/third_party/WebKit/LayoutTests/html/sections/body-quirk-client-size-expected.txt
index c520c9c..f51c3a6 100644
--- a/third_party/WebKit/LayoutTests/html/sections/body-quirk-client-size-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/body-quirk-client-size-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS [object Internals] is defined.
 PASS internals.updateStyleAndReturnAffectedElementCount() is 1
 PASS 800 is 800
diff --git a/third_party/WebKit/LayoutTests/html/sections/footer-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/footer-element-expected.txt
index e0ec8177..1b4b9ded 100644
--- a/third_party/WebKit/LayoutTests/html/sections/footer-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/footer-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <footer> closes <p>:
 PASS footer1.parentNode.nodeName == "p" is false
 <p> does not close <footer>:
diff --git a/third_party/WebKit/LayoutTests/html/sections/header-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/header-element-expected.txt
index 692f3ef..477d8e24 100644
--- a/third_party/WebKit/LayoutTests/html/sections/header-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/header-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <header> closes <p>:
 PASS header1.parentNode.nodeName == "p" is false
 <p> does not close <header>:
diff --git a/third_party/WebKit/LayoutTests/html/sections/hgroup-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/hgroup-element-expected.txt
index 1c7abe4..cec0535 100644
--- a/third_party/WebKit/LayoutTests/html/sections/hgroup-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/hgroup-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <hgroup> closes <p>:
 PASS hgroup1.parentNode.nodeName == "p" is false
 <p> does not close <hgroup>:
diff --git a/third_party/WebKit/LayoutTests/html/sections/nav-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/nav-element-expected.txt
index eeda2d8..0b7a4cf 100644
--- a/third_party/WebKit/LayoutTests/html/sections/nav-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/nav-element-expected.txt
@@ -1,17 +1,25 @@
 Test content
+
 Test that nav closes p. This paragraph should be surrounded by a thin green border, instead of a thick red one. Also tests that nav lays out as a block. There should be only a single border box with width of the content area (minus margins).
 
+
+
 Test that p does not close nav. This paragraph should have a double green border.
 
 
+
 Test that nav can nest inside itself. This paragraph should have a double green border.
 
+
 Test of residual style. This text should be bold.
 This should be bold too.
 
 
+
+
 Test of FormatBlock behavior. This text should have a green border.
 
+
 DOM for the above (so this test can dump as text)
 
 <p></p><nav>Test that <code>nav</code> closes <code>p</code>. This paragraph should be surrounded by a thin green border, instead of a thick red one. Also tests that nav lays out as a block. There should be only a single border box with width of the content area (minus margins).</nav>
diff --git a/third_party/WebKit/LayoutTests/html/sections/numbered-header-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/numbered-header-element-expected.txt
index 450b59d..e19f3c5 100644
--- a/third_party/WebKit/LayoutTests/html/sections/numbered-header-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/numbered-header-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <h1> closes <h1>:
 PASS test2.parentNode.id == "test1" is false
 PASS test2.parentNode.id == "test0" is true
diff --git a/third_party/WebKit/LayoutTests/html/sections/section-element-expected.txt b/third_party/WebKit/LayoutTests/html/sections/section-element-expected.txt
index 3d719d8..cca7ec21 100644
--- a/third_party/WebKit/LayoutTests/html/sections/section-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/sections/section-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <section> closes <p>:
 PASS section1.parentNode.nodeName == "p" is false
 <p> does not close <section>:
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/col_span-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/col_span-expected.txt
index 404df76..10318006 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/col_span-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/col_span-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS document.createElement("col").span is 1
 PASS spanAttributeEffect("") is 1
 PASS spanAttributeEffect("1") is 1
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-65-excerpt-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-65-excerpt-expected.txt
index df9c958..4a281bd 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-65-excerpt-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-65-excerpt-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS table.tBodies[0].rows[0].rowIndex is 0
 PASS table.tBodies[0].rows[0].sectionRowIndex is 0
 PASS table.childNodes.length is 3
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-66-excerpt-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-66-excerpt-expected.txt
index 559a432..3075149 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-66-excerpt-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/early-acid3-66-excerpt-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS table.rows.length is 6
 PASS table.getElementsByTagName('tr').length is 6
 PASS table.childNodes.length is 3
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_border_invalid-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_border_invalid-expected.txt
index 608602e..e22a340 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_border_invalid-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_border_invalid-expected.txt
@@ -1,4 +1,6 @@
-https://bugs.webkit.org/show_bug.cgi?id=102112: There should be a black box below. 
+https://bugs.webkit.org/show_bug.cgi?id=102112: There should be a black box below.
+
+
 PASS getComputedStyle(document.querySelector("table")).borderTopWidth is "1px"
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_cellpadding-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_cellpadding-expected.txt
index 5ec394d..e914944 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_cellpadding-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_cellpadding-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS cellPaddingAttributeEffect("") is "1px"
 PASS cellPaddingAttributeEffect("1") is "1px"
 PASS cellPaddingAttributeEffect("2") is "2px"
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_exceptions-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_exceptions-expected.txt
index a82d9a1..57ee3d4 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_exceptions-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_exceptions-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS t.caption = document.body threw exception TypeError: Failed to set the 'caption' property on 'HTMLTableElement': The provided value is not of type 'HTMLTableCaptionElement'..
 PASS t.tHead = document.body threw exception TypeError: Failed to set the 'tHead' property on 'HTMLTableElement': The provided value is not of type 'HTMLTableSectionElement'..
 PASS t.createTFoot() is t.tFoot
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow-expected.txt
index ece74e5..1f58bc7d 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow-expected.txt
@@ -1,5 +1,6 @@
 HTMLTableElement's insertRow() method
 The first three test whether HTMLTableElement's insertRow() method can add an implicit tbody into a table without one, to hold the inserted row.
+
 The first tests an empty table
 
 The second tests a table with text contents.
@@ -7,6 +8,7 @@
 The third tests a table with only a form element inside.
 
 The next four test the method on typical cases.
+
 The first tests a table with only a thead
 
 The second tests a table with only a tbody
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow_default_argument-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow_default_argument-expected.txt
index 74ba243..f9af1f0 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow_default_argument-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_insertrow_default_argument-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS table.__proto__ is HTMLTableElement.prototype
 PASS rows.length is 4
 PASS rows[0].innerHTML is "0"
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_rows-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_rows-expected.txt
index 04946c5..19f7c458 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_rows-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_rows-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS checkRowNesting("col") is 0
 PASS checkRowNesting("colgroup") is 0
 PASS checkRowNesting("div") is 0
@@ -12,12 +11,15 @@
 PASS checkRowNesting("td") is 0
 PASS checkRowNesting("th") is 0
 
+
 PASS checkRowNesting("tbody") is 0
 PASS checkRowNesting("tfoot") is 0
 PASS checkRowNesting("thead") is 0
 
+
 PASS checkRowNesting("tr") is 1
 
+
 PASS checkNoBodyRowNesting("col") is 0
 PASS checkNoBodyRowNesting("colgroup") is 0
 PASS checkNoBodyRowNesting("div") is 0
@@ -27,12 +29,15 @@
 PASS checkNoBodyRowNesting("td") is 0
 PASS checkNoBodyRowNesting("th") is 0
 
+
 PASS checkNoBodyRowNesting("tbody") is 1
 PASS checkNoBodyRowNesting("tfoot") is 1
 PASS checkNoBodyRowNesting("thead") is 1
 
+
 PASS checkNoBodyRowNesting("tr") is 1
 
+
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/table_tbodies-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/table_tbodies-expected.txt
index b3fe3360..6fc59c00 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/table_tbodies-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/table_tbodies-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS checkTBodyNesting("col") is 0
 PASS checkTBodyNesting("colgroup") is 0
 PASS checkTBodyNesting("div") is 0
@@ -15,8 +14,10 @@
 PASS checkTBodyNesting("thead") is 0
 PASS checkTBodyNesting("tr") is 0
 
+
 PASS checkTBodyNesting("tbody") is 1
 
+
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_default_argument-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_default_argument-expected.txt
index 6470120c..7b28642 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_default_argument-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_default_argument-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS tb.__proto__ is HTMLTableSectionElement.prototype
 PASS rows.length is 4
 PASS rows[0].innerHTML is "0"
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_skips_non_tr-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_skips_non_tr-expected.txt
index 8349274b..4833723 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_skips_non_tr-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_insertrow_skips_non_tr-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS tb.__proto__ is HTMLTableSectionElement.prototype
 PASS childNodes.length is 4
 PASS childNodes[0].nodeValue is "TEXT"
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_rows-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_rows-expected.txt
index 41ce7ab..666858b5 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tbody_rows-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tbody_rows-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS checkRowNesting("col") is 0
 PASS checkRowNesting("colgroup") is 0
 PASS checkRowNesting("div") is 0
@@ -12,12 +11,15 @@
 PASS checkRowNesting("td") is 0
 PASS checkRowNesting("th") is 0
 
+
 PASS checkRowNesting("tbody") is 0
 PASS checkRowNesting("tfoot") is 0
 PASS checkRowNesting("thead") is 0
 
+
 PASS checkRowNesting("tr") is 1
 
+
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/td_colspan-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/td_colspan-expected.txt
index 6041750..4b33cd7 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/td_colspan-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/td_colspan-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS document.createElement("td").colSpan is 1
 PASS colspanAttributeEffect("") is 1
 PASS colspanAttributeEffect("1") is 1
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/td_rowspan-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/td_rowspan-expected.txt
index 1ff4ece..9952a39 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/td_rowspan-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/td_rowspan-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS document.createElement("td").rowSpan is 1
 PASS rowspanAttributeEffect("") is 1
 PASS rowspanAttributeEffect("1") is 1
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tr_cells-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tr_cells-expected.txt
index 162ed69..d76c44d3 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tr_cells-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tr_cells-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS checkCellNesting("col") is 0
 PASS checkCellNesting("colgroup") is 0
 PASS checkCellNesting("div") is 0
@@ -14,9 +13,11 @@
 PASS checkCellNesting("thead") is 0
 PASS checkCellNesting("tr") is 0
 
+
 PASS checkCellNesting("td") is 1
 PASS checkCellNesting("th") is 1
 
+
 PASS checkHeaderCellNesting("col") is 0
 PASS checkHeaderCellNesting("colgroup") is 0
 PASS checkHeaderCellNesting("div") is 0
@@ -28,9 +29,11 @@
 PASS checkHeaderCellNesting("thead") is 0
 PASS checkHeaderCellNesting("tr") is 0
 
+
 PASS checkHeaderCellNesting("td") is 1
 PASS checkHeaderCellNesting("th") is 1
 
+
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tr_exceptions-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tr_exceptions-expected.txt
index dc479bb..9eb3791 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tr_exceptions-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tr_exceptions-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS tr.insertCell(-2) threw exception IndexSizeError: Failed to execute 'insertCell' on 'HTMLTableRowElement': The value provided (-2) is outside the range [-1, 0]..
 PASS tr.insertCell(1) threw exception IndexSizeError: Failed to execute 'insertCell' on 'HTMLTableRowElement': The value provided (1) is outside the range [-1, 0]..
 PASS tr.deleteCell(-2) threw exception IndexSizeError: Failed to execute 'deleteCell' on 'HTMLTableRowElement': The value provided (-2) is outside the range [0, 1)..
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_default_argument-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_default_argument-expected.txt
index 98cf52e..381a5d3 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_default_argument-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_default_argument-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS tr.__proto__ is HTMLTableRowElement.prototype
 PASS cells.length is 4
 PASS cells[0].innerHTML is "0"
diff --git a/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_skips_non_td_th-expected.txt b/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_skips_non_td_th-expected.txt
index 165e94c..9beaac4 100644
--- a/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_skips_non_td_th-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/tabular_data/tr_insertcell_skips_non_td_th-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS tr.__proto__ is HTMLTableRowElement.prototype
 PASS childNodes.length is 4
 PASS childNodes[0].nodeValue is "TEXT"
diff --git a/third_party/WebKit/LayoutTests/html/text_level_semantics/mark-element-expected.txt b/third_party/WebKit/LayoutTests/html/text_level_semantics/mark-element-expected.txt
index 18d2705..e55dc1a3 100644
--- a/third_party/WebKit/LayoutTests/html/text_level_semantics/mark-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/html/text_level_semantics/mark-element-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 <p> closes <mark>:
 PASS paragraph1.parentNode.nodeName == "mark" is false
 <b> does not close <mark>:
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
index 39012c4c..b0379cc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
@@ -10,7 +10,7 @@
 DIV:console-key-navigation.js:20 Message #99
 
 Shift+Tab:
-BUTTON:Console settings
+DIV#console-messages.monospace
 
 Tab:
 DIV:console-key-navigation.js:20 Message #99
@@ -29,7 +29,7 @@
 DIV.console-group.console-group-messages
 
 Shift+Tab:
-BUTTON:Console settings
+DIV#console-messages.monospace
 
 Tab:
 DIV.console-group.console-group-messages
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-data-blob-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-data-blob-expected.txt
index 3ce7937..4c3fcc0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-data-blob-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-data-blob-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 * BlobBuilder.append(string)
 PASS Expected response data received: OK
 PASS Expected response data received: OK
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-hybrid-blob-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-hybrid-blob-expected.txt
index 5796dfda..e8ac92f5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-hybrid-blob-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-hybrid-blob-expected.txt
@@ -1,10 +1,8 @@
 CONSOLE WARNING: line 17: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
-
 Test for building blobs with multiple files combined by BlobBuilder and sending them via XMLHttpRequest. (This test requires eventSender.beginDragWithFiles)
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 * BlobBuilder.append(file) - single file
 PASS Expected response data received: OK
 PASS Expected response data received: OK
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-sliced-data-blob-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-sliced-data-blob-expected.txt
index f3df3380..f7c39a3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/blob/send-sliced-data-blob-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/blob/send-sliced-data-blob-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 * BlobBuilder.append(data) and then apply Blob.slice()
 PASS Expected response data received: OK
 PASS Expected response data received: OK
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/drag-over-remote-content-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/drag-over-remote-content-expected.txt
index d880172de..1a565bd1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/drag-over-remote-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/drag-over-remote-content-expected.txt
@@ -1,6 +1,7 @@
 This page just embeds drag-over-remote-content-iframe.html in an iframe. See its results below:
 
 
+
 This page tests the URL the DOM sees while the user drags a file. If the test passes, you'll see a PASS message.
 
 https://bugs.webkit.org/show_bug.cgi?id=25907 | rdar://problem/6910832.
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt
index bf8fc369..d29d7d3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS event.dataTransfer contains a File object on drop.
 PASS lastModifiedDate is not null
 PASS lastModifiedDate is >= testStartTime
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt
index 85c63b8..91a2ed9 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS event.dataTransfer contains a File object on drop.
 PASS file.lastModifiedDate verified
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-dragged-file-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-dragged-file-expected.txt
index 4ab5e8d..d8c96a8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-dragged-file-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-dragged-file-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS event.dataTransfer contains a File object on drop.
 PASS Expected response data received.
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-sliced-dragged-file-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-sliced-dragged-file-expected.txt
index 551513f..3bc2386 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-sliced-dragged-file-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/fileapi/send-sliced-dragged-file-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Test slicing and sending an empty file.
 PASS event.dataTransfer contains a File object on drop.
 PASS subfile.size is 0
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/form-data-with-unknown-file-extension-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/form-data-with-unknown-file-extension-expected.txt
index 799c0be..d438f3e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/form-data-with-unknown-file-extension-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/form-data-with-unknown-file-extension-expected.txt
@@ -1,4 +1,3 @@
-
 PASS xhr.responseText is 'file1=file.invalid:application/octet-stream:1234567890'
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-expected.txt
index 12a2266..955a509 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing one string with empty name:
 
+
 Sending FormData containing one file with empty name:
 
+
 Sending FormData containing one string:
 string=foo
 Sending FormData containing one file:
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-bad-string-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-bad-string-expected.txt
index a30fc9e4..b4f67bf 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-bad-string-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-bad-string-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS formData.append(badString, 'test') threw exception Exception in toString().
 PASS formData.append('textarea', badString) threw exception Exception in toString().
 PASS formData.append('blob', new Blob(['']), badString) threw exception Exception in toString().
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-blob-filename-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-blob-filename-expected.txt
index f44261e..8b32d59 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-blob-filename-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-blob-filename-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing one blob with custom empty filename:
 PASS filename is ''
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-file-filename-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-file-filename-expected.txt
index 37ea9af..b48d47b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-file-filename-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-file-filename-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing one file with custom empty filename:
 PASS filename is ''
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-name-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-name-expected.txt
index 6defe99..66d619d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-name-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-empty-name-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS the FormData of string type containing a empty name echoed correctly
 PASS the FormData of string type containing a name echoed correctly
 PASS the FormData of file type containing a empty name echoed correctly
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-filename-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-filename-expected.txt
index 6d756495..a398204 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-filename-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-filename-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing one file with custom filename:
 file1=custom-name.txt:text/plain:1234567890
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-null-string-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-null-string-expected.txt
index ab8602f..a9914b6 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-null-string-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-null-string-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing a null value:
 name=null
 Sending FormData containing a null field name:
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt
index cfecf6fe..63628d1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt
@@ -3,9 +3,9 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 Sending FormData containing one sliced file with empty name:
 
+
 Sending FormData containing one sliced file:
 file=blob:application/octet-stream:23456
 Sending FormData containing one sliced file with optional null filename:
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-string-containing-null-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-string-containing-null-expected.txt
index 0198f504..7ffce95 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-string-containing-null-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/send-form-data-with-string-containing-null-expected.txt
@@ -3,7 +3,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS the string containing a null byte is echoed correctly.
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/formdata/upload-events-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/formdata/upload-events-expected.txt
index a7e661d..a660573 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/formdata/upload-events-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/formdata/upload-events-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 loadstart event fired on XMLHttpRequestUpload: [object ProgressEvent]
 onprogress event fired on XMLHttpRequestUpload: [object ProgressEvent]
 load event fired on XMLHttpRequestUpload: [object ProgressEvent]
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/style-access-before-stylesheet-loaded-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/style-access-before-stylesheet-loaded-expected.txt
index d4ca090..68f6682 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/style-access-before-stylesheet-loaded-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/style-access-before-stylesheet-loaded-expected.txt
@@ -1,3 +1,2 @@
 This page has a slow loading external style sheet. Calling offsetLeft shouldn't be slow when stylesheets are loading. Works as HTTP test only.
 PASS
-
diff --git a/third_party/WebKit/LayoutTests/idle-callback/test-runner-run-idle-tasks-expected.txt b/third_party/WebKit/LayoutTests/idle-callback/test-runner-run-idle-tasks-expected.txt
index 5b5b233..fcc243e 100644
--- a/third_party/WebKit/LayoutTests/idle-callback/test-runner-run-idle-tasks-expected.txt
+++ b/third_party/WebKit/LayoutTests/idle-callback/test-runner-run-idle-tasks-expected.txt
@@ -2,10 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS x is false
 PASS x is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
 Hello, world!
diff --git a/third_party/WebKit/LayoutTests/images/12-55-expected.txt b/third_party/WebKit/LayoutTests/images/12-55-expected.txt
index 314f9f4..44bfcdc 100644
--- a/third_party/WebKit/LayoutTests/images/12-55-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/12-55-expected.txt
@@ -1,17 +1,17 @@
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
 
diff --git a/third_party/WebKit/LayoutTests/images/182-expected.txt b/third_party/WebKit/LayoutTests/images/182-expected.txt
index 314f9f4..44bfcdc 100644
--- a/third_party/WebKit/LayoutTests/images/182-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/182-expected.txt
@@ -1,17 +1,17 @@
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
 
diff --git a/third_party/WebKit/LayoutTests/images/2-dht-expected.txt b/third_party/WebKit/LayoutTests/images/2-dht-expected.txt
index 314f9f4..44bfcdc 100644
--- a/third_party/WebKit/LayoutTests/images/2-dht-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/2-dht-expected.txt
@@ -1,17 +1,17 @@
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
 
diff --git a/third_party/WebKit/LayoutTests/images/23-55-expected.txt b/third_party/WebKit/LayoutTests/images/23-55-expected.txt
index 314f9f4..44bfcdc 100644
--- a/third_party/WebKit/LayoutTests/images/23-55-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/23-55-expected.txt
@@ -1,17 +1,17 @@
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
 
diff --git a/third_party/WebKit/LayoutTests/images/55-expected.txt b/third_party/WebKit/LayoutTests/images/55-expected.txt
index 314f9f4..44bfcdc 100644
--- a/third_party/WebKit/LayoutTests/images/55-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/55-expected.txt
@@ -1,17 +1,17 @@
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
-              
+       
 
 
diff --git a/third_party/WebKit/LayoutTests/images/bad-png-expected.txt b/third_party/WebKit/LayoutTests/images/bad-png-expected.txt
index aa44e7c..7ef22e9 100644
--- a/third_party/WebKit/LayoutTests/images/bad-png-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/bad-png-expected.txt
@@ -1,2 +1 @@
-
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-expected.txt
index 1a4baf5..8b13789 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-expected.txt
@@ -1 +1 @@
-  
+
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png-expected.txt
index 1a4baf5..8b13789 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-cross-fade-png-expected.txt
@@ -1 +1 @@
-  
+
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-image-source-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-border-image-source-expected.txt
index ab45984..d99bfa2 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-image-source-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-image-source-expected.txt
@@ -1,2 +1,2 @@
-  
+ 
  
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-border-radius-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-border-radius-expected.txt
index 600b53fd..9a47ee5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-border-radius-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-border-radius-expected.txt
@@ -1,3 +1,3 @@
-     
-     
+  
+  
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-filter-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-filter-expected.txt
index 7f9642c3..9a47ee5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-filter-expected.txt
@@ -1,3 +1,3 @@
-    
-    
+  
+  
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-group-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-group-expected.txt
index 8d1c8b6..8b13789 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-group-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-group-expected.txt
@@ -1 +1 @@
- 
+
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-image-expected.txt
index 600b53fd..9a47ee5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-expected.txt
@@ -1,3 +1,3 @@
-     
-     
+  
+  
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all-expected.txt
index 92367f364..23868622 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-filter-all-expected.txt
@@ -1,25 +1,31 @@
-
 GRAYSCALE
 
-  
+ 
+
 NONE
 
-  
+ 
+
 BRIGHTNESS
 
-  
+ 
+
 SATURATION
 
  
+
 SEPIA
 
-  
+ 
+
 BLUR
 
-  
+ 
+
 OPACITY
 
-  
+ 
+
 BLUR+HUE
 
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit-expected.txt
index 1a4baf5..8d1c8b6 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-object-fit-expected.txt
@@ -1 +1 @@
-  
+ 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match-expected.txt
index 3fa127a..5c484c2f 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-profile-match-expected.txt
@@ -1,2 +1,2 @@
-    
+  
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-image-shape-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-image-shape-expected.txt
index 379b7bc..2f8181b 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-image-shape-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-image-shape-expected.txt
@@ -1,2 +1 @@
-
 Lorem
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-layer-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-layer-expected.txt
index 600b53fd..9a47ee5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-layer-expected.txt
@@ -1,3 +1,3 @@
-     
-     
+  
+  
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-layer-filter-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-layer-filter-expected.txt
index 163b4d3f..1a4baf5 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-layer-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-layer-filter-expected.txt
@@ -1 +1 @@
-    
+  
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg-expected.txt
index 1a4baf5..8d1c8b6 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-mask-image-svg-expected.txt
@@ -1 +1 @@
-  
+ 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-munsell-adobe-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-munsell-adobe-to-srgb-expected.txt
index 9027509..d8efc2e 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-munsell-adobe-to-srgb-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-munsell-adobe-to-srgb-expected.txt
@@ -1,4 +1,3 @@
-
 Color         Actual        Expected      dE
 --------------------------------------------
 Dark Skin     114,80,64     115,80,64     1
@@ -31,7 +30,8 @@
 --------------------------------------------
 
 Result: total RMS color error: 1.00
- * Munsell Cyan is outside 255 sRGB gamut
+
+* Munsell Cyan is outside 255 sRGB gamut
 
   
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-munsell-srgb-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-munsell-srgb-to-srgb-expected.txt
index 61e3d1ad..37a3b7d 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-munsell-srgb-to-srgb-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-munsell-srgb-to-srgb-expected.txt
@@ -1,4 +1,3 @@
-
 Color         Actual        Expected      dE
 --------------------------------------------
 Dark Skin     115,80,64     115,80,64     0
@@ -31,7 +30,8 @@
 --------------------------------------------
 
 Result: total RMS color error: 0.00
- * Munsell Cyan is outside 255 sRGB gamut
+
+* Munsell Cyan is outside 255 sRGB gamut
 
   
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text-expected.txt b/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text-expected.txt
index 9b89b7d2..4adc4ce 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/color-profile-svg-fill-text-expected.txt
@@ -1,2 +1 @@
 â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…â–…
-
diff --git a/third_party/WebKit/LayoutTests/images/crash-bad-cast-expected.txt b/third_party/WebKit/LayoutTests/images/crash-bad-cast-expected.txt
index 8d1c8b6..8337712 100644
--- a/third_party/WebKit/LayoutTests/images/crash-bad-cast-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/crash-bad-cast-expected.txt
@@ -1 +1 @@
- 
+//
diff --git a/third_party/WebKit/LayoutTests/images/crash-when-zoom-factor-changes-expected.txt b/third_party/WebKit/LayoutTests/images/crash-when-zoom-factor-changes-expected.txt
index 03a9ffc..e176868 100644
--- a/third_party/WebKit/LayoutTests/images/crash-when-zoom-factor-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/crash-when-zoom-factor-changes-expected.txt
@@ -1,2 +1 @@
-
 crbug.com/445304: Should not crash when zoom factor changes.
diff --git a/third_party/WebKit/LayoutTests/images/destroyed-image-load-event-expected.txt b/third_party/WebKit/LayoutTests/images/destroyed-image-load-event-expected.txt
index ec05c971..31b155b 100644
--- a/third_party/WebKit/LayoutTests/images/destroyed-image-load-event-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/destroyed-image-load-event-expected.txt
@@ -1,4 +1,3 @@
 Test for bug 34490: WebCore::ImageEventSender::dispatchPendingEvents() crashes in certain conditions.
 
 PASS
-
diff --git a/third_party/WebKit/LayoutTests/images/drag-pdf-as-image-expected.txt b/third_party/WebKit/LayoutTests/images/drag-pdf-as-image-expected.txt
index 5c768a8..1ac309f 100644
--- a/third_party/WebKit/LayoutTests/images/drag-pdf-as-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/drag-pdf-as-image-expected.txt
@@ -1,2 +1 @@
-
 This test checks that dragging a pdf doesn't crash the browser.
diff --git a/third_party/WebKit/LayoutTests/images/embed-does-not-propagate-dimensions-to-object-ancestor-expected.txt b/third_party/WebKit/LayoutTests/images/embed-does-not-propagate-dimensions-to-object-ancestor-expected.txt
index 0aeb74c..234b293b 100644
--- a/third_party/WebKit/LayoutTests/images/embed-does-not-propagate-dimensions-to-object-ancestor-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/embed-does-not-propagate-dimensions-to-object-ancestor-expected.txt
@@ -1,4 +1,4 @@
- 
- 
+
+
 
 SUCCESS
diff --git a/third_party/WebKit/LayoutTests/images/exif-orientation-css-expected.txt b/third_party/WebKit/LayoutTests/images/exif-orientation-css-expected.txt
index f02245b..8904b74 100644
--- a/third_party/WebKit/LayoutTests/images/exif-orientation-css-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/exif-orientation-css-expected.txt
@@ -4,19 +4,20 @@
 Normal 
 Flipped horizontally 
 Rotated 180° 
-Flipped vertically 
+Flipped vertically
 
 Rotated 90° CCW and flipped vertically 
 Rotated 90° CCW 
 Rotated 90° CW and flipped vertically 
-Rotated 90° CW 
+Rotated 90° CW
 
 Rotated 90° CCW and flipped vertically 
 Rotated 90° CCW 
 Rotated 90° CW and flipped vertically 
-Rotated 90° CW 
+Rotated 90° CW
 
-Undefined (invalid value) 
+Undefined (invalid value)
+
 img1 size = 100px by 50px
 img2 size = 100px by 50px
 img3 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/images/exif-orientation-expected.txt b/third_party/WebKit/LayoutTests/images/exif-orientation-expected.txt
index df488323..8975dcae 100644
--- a/third_party/WebKit/LayoutTests/images/exif-orientation-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/exif-orientation-expected.txt
@@ -4,14 +4,15 @@
 Normal 
 Flipped horizontally 
 Rotated 180° 
-Flipped vertically 
+Flipped vertically
 
 Rotated 90° CCW and flipped vertically 
 Rotated 90° CCW 
-Rotated 90° CW and flipped vertically  
-Rotated 90° CW 
+Rotated 90° CW and flipped vertically 
+Rotated 90° CW
 
-Undefined (invalid value) 
+Undefined (invalid value)
+
 img1 size = 100px by 50px
 img2 size = 100px by 50px
 img3 size = 100px by 50px
diff --git a/third_party/WebKit/LayoutTests/images/exif-orientation-image-document-expected.txt b/third_party/WebKit/LayoutTests/images/exif-orientation-image-document-expected.txt
index 47313be..781e98e 100644
--- a/third_party/WebKit/LayoutTests/images/exif-orientation-image-document-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/exif-orientation-image-document-expected.txt
@@ -1,6 +1,7 @@
 The images should be rotated respecting their EXIF orientation. In image documents, this happens independent of WebKitShouldRespectImageOrientation.
 
-    
-    
- 
+   
+   
+
+
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/huge-image-viewport-scale-expected.txt b/third_party/WebKit/LayoutTests/images/huge-image-viewport-scale-expected.txt
index 63299f8f..c88c52e 100644
--- a/third_party/WebKit/LayoutTests/images/huge-image-viewport-scale-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/huge-image-viewport-scale-expected.txt
@@ -2,6 +2,7 @@
 To manual test, open resources/5000x5000.png in a viewport-capable browser (e.g. on Android or enable viewport emulation in DevTools).
 Passes if the image shinks to fit the width.
 
+
 viewport meta: <meta name="viewport" content="width=device-width, minimum-scale=0.1">
 image style: -webkit-user-select: none;cursor: zoom-in;
 image actual width: 100
diff --git a/third_party/WebKit/LayoutTests/images/image-load-event-in-fragment-expected.txt b/third_party/WebKit/LayoutTests/images/image-load-event-in-fragment-expected.txt
index 8c57ab8..5997187 100644
--- a/third_party/WebKit/LayoutTests/images/image-load-event-in-fragment-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/image-load-event-in-fragment-expected.txt
@@ -1,4 +1,3 @@
 Test for bug 31660: Image load event fires before the document fragment is attached.
 
 PASS
-
diff --git a/third_party/WebKit/LayoutTests/images/image-map-multiple-expected.txt b/third_party/WebKit/LayoutTests/images/image-map-multiple-expected.txt
index dc6a7d5..4038f02 100644
--- a/third_party/WebKit/LayoutTests/images/image-map-multiple-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/image-map-multiple-expected.txt
@@ -1,4 +1,3 @@
- 
 This tests image map behavior when there are multiple maps with the same name.
 1: PASS: Hit the first map in the document.
 2: PASS: Hit the second map after the first was renamed.
diff --git a/third_party/WebKit/LayoutTests/images/image-map-multiple-xhtml-expected.txt b/third_party/WebKit/LayoutTests/images/image-map-multiple-xhtml-expected.txt
index dc6a7d5..4038f02 100644
--- a/third_party/WebKit/LayoutTests/images/image-map-multiple-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/image-map-multiple-xhtml-expected.txt
@@ -1,4 +1,3 @@
- 
 This tests image map behavior when there are multiple maps with the same name.
 1: PASS: Hit the first map in the document.
 2: PASS: Hit the second map after the first was renamed.
diff --git a/third_party/WebKit/LayoutTests/images/image-map-zoom-alt-content-expected.txt b/third_party/WebKit/LayoutTests/images/image-map-zoom-alt-content-expected.txt
index 47c729c..15d02286 100644
--- a/third_party/WebKit/LayoutTests/images/image-map-zoom-alt-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/image-map-zoom-alt-content-expected.txt
@@ -1,4 +1,3 @@
- 
 This tests that client side image maps still work when zooming is in effect.
 
 SUCCESS
diff --git a/third_party/WebKit/LayoutTests/images/image-map-zoom-expected.txt b/third_party/WebKit/LayoutTests/images/image-map-zoom-expected.txt
index 47c729c..15d02286 100644
--- a/third_party/WebKit/LayoutTests/images/image-map-zoom-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/image-map-zoom-expected.txt
@@ -1,4 +1,3 @@
- 
 This tests that client side image maps still work when zooming is in effect.
 
 SUCCESS
diff --git a/third_party/WebKit/LayoutTests/images/imagemap-nested-area-expected.txt b/third_party/WebKit/LayoutTests/images/imagemap-nested-area-expected.txt
index 0dbcd68..b3fede8 100644
--- a/third_party/WebKit/LayoutTests/images/imagemap-nested-area-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/imagemap-nested-area-expected.txt
@@ -1,4 +1,3 @@
 This tests that pressing Tab focuses areas in an image map even if they're not direct children.
 Test Passed
- 
 
diff --git a/third_party/WebKit/LayoutTests/images/jpeg-with-color-profile-expected.txt b/third_party/WebKit/LayoutTests/images/jpeg-with-color-profile-expected.txt
index 0a987b33..f68e93a 100644
--- a/third_party/WebKit/LayoutTests/images/jpeg-with-color-profile-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/jpeg-with-color-profile-expected.txt
@@ -1,2 +1,3 @@
 The red sector of the image should be at the 12 o'clock position.
 
+
diff --git a/third_party/WebKit/LayoutTests/images/large-size-image-crash-expected.txt b/third_party/WebKit/LayoutTests/images/large-size-image-crash-expected.txt
index aa44e7c..7ef22e9 100644
--- a/third_party/WebKit/LayoutTests/images/large-size-image-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/large-size-image-crash-expected.txt
@@ -1,2 +1 @@
-
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/link-body-content-imageDimensionChanged-crash-expected.txt b/third_party/WebKit/LayoutTests/images/link-body-content-imageDimensionChanged-crash-expected.txt
index c3b0e09c..e32dd62a 100644
--- a/third_party/WebKit/LayoutTests/images/link-body-content-imageDimensionChanged-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/link-body-content-imageDimensionChanged-crash-expected.txt
@@ -1,5 +1,3 @@
 Bug 85912: Crash in computedCSSPadding* functions due to LayoutImage::imageDimensionsChanged called during attachment
 
 This test PASSED if it did not crash.
-
-
diff --git a/third_party/WebKit/LayoutTests/images/png-extra-row-crash-expected.txt b/third_party/WebKit/LayoutTests/images/png-extra-row-crash-expected.txt
index 5797e3d0..c4991c9 100644
--- a/third_party/WebKit/LayoutTests/images/png-extra-row-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/png-extra-row-crash-expected.txt
@@ -1,2 +1,3 @@
-The following PNG will attempt to render a bad row. If the test succeeds this should not crash.  
+The following PNG will attempt to render a bad row. If the test succeeds this should not crash. 
+
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/png-missing-plte-before-trns-crash-expected.txt b/third_party/WebKit/LayoutTests/images/png-missing-plte-before-trns-crash-expected.txt
index aa44e7c..7ef22e9 100644
--- a/third_party/WebKit/LayoutTests/images/png-missing-plte-before-trns-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/png-missing-plte-before-trns-crash-expected.txt
@@ -1,2 +1 @@
-
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/png-suite/test-expected.txt b/third_party/WebKit/LayoutTests/images/png-suite/test-expected.txt
index e9fa1ca..f095e1d 100644
--- a/third_party/WebKit/LayoutTests/images/png-suite/test-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/png-suite/test-expected.txt
@@ -1,12 +1,12 @@
-               
-               
-        
-                         
-           
-                  
-        
-      
-                                    
-             
               
+              
+       
+                        
+          
+                 
+       
+     
+                                   
+            
+             
    
diff --git a/third_party/WebKit/LayoutTests/images/png-with-color-profile-expected.txt b/third_party/WebKit/LayoutTests/images/png-with-color-profile-expected.txt
index 0a987b33..f68e93a 100644
--- a/third_party/WebKit/LayoutTests/images/png-with-color-profile-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/png-with-color-profile-expected.txt
@@ -1,2 +1,3 @@
 The red sector of the image should be at the 12 o'clock position.
 
+
diff --git a/third_party/WebKit/LayoutTests/images/script-counter-imageDimensionChanged-crash-expected.txt b/third_party/WebKit/LayoutTests/images/script-counter-imageDimensionChanged-crash-expected.txt
index c3b0e09c..e32dd62a 100644
--- a/third_party/WebKit/LayoutTests/images/script-counter-imageDimensionChanged-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/script-counter-imageDimensionChanged-crash-expected.txt
@@ -1,5 +1,3 @@
 Bug 85912: Crash in computedCSSPadding* functions due to LayoutImage::imageDimensionsChanged called during attachment
 
 This test PASSED if it did not crash.
-
-
diff --git a/third_party/WebKit/LayoutTests/images/size-failure-expected.txt b/third_party/WebKit/LayoutTests/images/size-failure-expected.txt
index aa44e7c..7ef22e9 100644
--- a/third_party/WebKit/LayoutTests/images/size-failure-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/size-failure-expected.txt
@@ -1,2 +1 @@
-
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/style-access-during-imageChanged-style-freeze-expected.txt b/third_party/WebKit/LayoutTests/images/style-access-during-imageChanged-style-freeze-expected.txt
index 28f4629..ae8c8d7 100644
--- a/third_party/WebKit/LayoutTests/images/style-access-during-imageChanged-style-freeze-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/style-access-during-imageChanged-style-freeze-expected.txt
@@ -1,3 +1,2 @@
-
 This should be green, with a wide broken image above.
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/viewport-in-standalone-image-document-expected.txt b/third_party/WebKit/LayoutTests/images/viewport-in-standalone-image-document-expected.txt
index 564d0a0..27359f57 100644
--- a/third_party/WebKit/LayoutTests/images/viewport-in-standalone-image-document-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/viewport-in-standalone-image-document-expected.txt
@@ -1,4 +1,5 @@
 This tests that a standalone image document has viewport settings.
 
 
+
 PASS
diff --git a/third_party/WebKit/LayoutTests/images/width-on-broken-data-src-expected.txt b/third_party/WebKit/LayoutTests/images/width-on-broken-data-src-expected.txt
index c3ead7d..18044cd 100644
--- a/third_party/WebKit/LayoutTests/images/width-on-broken-data-src-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/width-on-broken-data-src-expected.txt
@@ -1,6 +1,6 @@
-
 FAIL:
 Expected 0 for width, but got 16. 
 
 <img src="data:dsa" data-expected-width="0">
+
 crbug.com/522673: Return the correct width for images with broken data: uri's.
diff --git a/third_party/WebKit/LayoutTests/images/zoomed-img-size-expected.txt b/third_party/WebKit/LayoutTests/images/zoomed-img-size-expected.txt
index aefafbf..9314807 100644
--- a/third_party/WebKit/LayoutTests/images/zoomed-img-size-expected.txt
+++ b/third_party/WebKit/LayoutTests/images/zoomed-img-size-expected.txt
@@ -1,46 +1,61 @@
 Ideally, all ovals below should be reported as having width=37, height=33. Currently rounding prevents us from doing this.
 
 
+
 Zoom 1% PASS: 100x100 close enough to 37x33
 
 
+
 Zoom 2% PASS: 50x50 close enough to 37x33
 
 
+
 Zoom 3% PASS: 33x33 close enough to 37x33
 
 
+
 Zoom 4% PASS: 25x25 close enough to 37x33
 
 
+
 Zoom 5% PASS: 40x40 close enough to 37x33
 
 
+
 Zoom 30% PASS: 36x33 close enough to 37x33
 
 
+
 Zoom 33% PASS: 36x33 close enough to 37x33
 
 
+
 Zoom 50% PASS: 38x34 close enough to 37x33
 
 
+
 Zoom 70% PASS: 37x32 close enough to 37x33
 
 
+
 Zoom 100% PASS: 37x33
 
 
+
 Zoom 111% PASS: 37x33
 
 
+
 Zoom 150% PASS: 37x33
 
 
+
 Zoom 333% PASS: 37x33
 
 
+
 Zoom 400% PASS: 37x33
 
 
+
 Zoom 1234% PASS: 37x33
diff --git a/third_party/WebKit/LayoutTests/intersection-observer/observer-shared-from-iframe-expected.txt b/third_party/WebKit/LayoutTests/intersection-observer/observer-shared-from-iframe-expected.txt
index 73409ae..c2541f4 100644
--- a/third_party/WebKit/LayoutTests/intersection-observer/observer-shared-from-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/intersection-observer/observer-shared-from-iframe-expected.txt
@@ -1,2 +1 @@
 PASS if no crash.
-
diff --git a/third_party/WebKit/LayoutTests/loader/iframe-src-change-onload-crash-expected.txt b/third_party/WebKit/LayoutTests/loader/iframe-src-change-onload-crash-expected.txt
index 8f0a7c98..fac6ed8 100644
--- a/third_party/WebKit/LayoutTests/loader/iframe-src-change-onload-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/loader/iframe-src-change-onload-crash-expected.txt
@@ -1,3 +1 @@
 Test PASSES if it does not crash in ASAN builds.
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_empty_if_no_src-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_empty_if_no_src-expected.txt
index 97e1060..9935dce 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_empty_if_no_src-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_empty_if_no_src-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "" is ""
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
index 6dffd44..c016262 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
@@ -2,12 +2,11 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
index b95961f..b7f3e401 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
@@ -2,12 +2,11 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_property_exists-expected.txt
index 5b88a358..e3ae6f7a 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/currentSrc/currentSrc_property_exists-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_null-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_null-expected.txt
index a22ca9e..0cc3179 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_null-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_null-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "null" is "null"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_onerror_called_on_bogus_source-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_onerror_called_on_bogus_source-expected.txt
index f0fe73c4..5f633ab 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_onerror_called_on_bogus_source-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_onerror_called_on_bogus_source-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "4" is "4"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_property_exists-expected.txt
index 4731c12..c702bdaf 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/error/error_property_exists-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay-expected.txt
index a1c45ff..c630e88 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay_manual-expected.txt
index b7ad44c..08720a7 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplay_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough-expected.txt
index 7e4ac6a..8bdc759 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough_manual-expected.txt
index bab33e1..9cdaaa8 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_canplaythrough_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata-expected.txt
index 7390189..09d5774 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata_manual-expected.txt
index fb4ac09..df5b362 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadeddata_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata-expected.txt
index 94037434..b44003b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata_manual-expected.txt
index 5770ccc2..652504b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadedmetadata_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart-expected.txt
index 707897ba..9f6633e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart_manual-expected.txt
index d6dc4bba..d3f3db2f 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_loadstart_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_canplaythrough-expected.txt
index 49fbd7a..f8ed66be 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_playing-expected.txt
index 7545d2c..afe9608 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_canplay_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadedmetadata_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadedmetadata_loadeddata-expected.txt
index ca20263..49989609 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadedmetadata_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadedmetadata_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadstart_progress-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadstart_progress-expected.txt
index e13c007..db5ec733 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadstart_progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_order_loadstart_progress-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_pause_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_pause_manual-expected.txt
index 8c4a5928..11d025b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_pause_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_pause_manual-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play-expected.txt
index c2544301..0a4bbb4b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play_manual-expected.txt
index 085e5e93..2973e2b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_play_manual-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing-expected.txt
index 9443f4d9..2cf51c4e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing_manual-expected.txt
index 5f65f3e7..9004dde 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_playing_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress-expected.txt
index 004eeca..ba8eb91 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress_manual-expected.txt
index c9f3059f..1cfce079 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_progress_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate-expected.txt
index fec4ad3f4..cd9b4538 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate_manual-expected.txt
index 17a0c5b..6ebd1b0 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/events/event_timeupdate_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_during_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_during_loadstart-expected.txt
index 0787a38..af7e9ac 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_during_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_during_loadstart-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "2" is "2"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_initial-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_initial-expected.txt
index 55c930ef1..01d6f0e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_initial-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_initial-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "0" is "0"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_property_exists-expected.txt
index a069a145..9c45242 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/networkState/networkState_property_exists-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_false_during_play-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_false_during_play-expected.txt
index c6e8f278..eb205aa 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_false_during_play-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_false_during_play-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_true_during_pause-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_true_during_pause-expected.txt
index 1f581ce3..96c098f 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_true_during_pause-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/paused/paused_true_during_pause-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_property_exists-expected.txt
index 16117410..4c6c5a9 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_property_exists-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_auto_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_auto_value-expected.txt
index 8add3bb..80eada5c 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_auto_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_auto_value-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "auto" is "auto"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_bogus_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_bogus_value-expected.txt
index 11b0902..0ac8d7b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_bogus_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_bogus_value-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_empty-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_empty-expected.txt
index 8add3bb..80eada5c 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_empty-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_empty-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "auto" is "auto"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_metadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_metadata-expected.txt
index 11b0902..0ac8d7b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_metadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_metadata-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_no_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_no_value-expected.txt
index 11b0902..0ac8d7b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_no_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_no_value-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none-expected.txt
index a3901484..db50cd9e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "none" is "none"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none_autoplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none_autoplay-expected.txt
index a3901484..db50cd9e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none_autoplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/preload/preload_reflects_none_autoplay-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "none" is "none"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplay-expected.txt
index c5a123d..7938885 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplay-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplaythrough-expected.txt
index 99a3ed792..294072b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "4" is "4"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadeddata-expected.txt
index 903afd0..113d826a 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadedmetadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadedmetadata-expected.txt
index d082739..79cb975 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadedmetadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_loadedmetadata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_playing-expected.txt
index aac9e89..52a148d9 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_during_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_initial-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_initial-expected.txt
index 36ff53d..adf7368 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_initial-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_initial-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "0" is "0"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_property_exists-expected.txt
index 2bdd626e..220f2016 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/readyState/readyState_property_exists-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_attribute_not_source_elements-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_attribute_not_source_elements-expected.txt
index 15cf485..f5f787d3 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_attribute_not_source_elements-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_attribute_not_source_elements-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_no_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_no_value-expected.txt
index 254e8d26..86fe903 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_no_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_reflects_no_value-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "" is ""
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_removal_does_not_trigger_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_removal_does_not_trigger_loadstart-expected.txt
index e744462..30c28eb 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_removal_does_not_trigger_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/audio/src/src_removal_does_not_trigger_loadstart-expected.txt
@@ -2,11 +2,10 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_empty_if_no_src-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_empty_if_no_src-expected.txt
index a0a5c9c..68a120f8 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_empty_if_no_src-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_empty_if_no_src-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "" is ""
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
index ccacd0c..24652d9 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_adding_source_child-expected.txt
@@ -2,12 +2,13 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
index df7fb91..1b0fbc7 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_nonempty_after_setting_src-expected.txt
@@ -2,12 +2,11 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
-
-
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_property_exists-expected.txt
index aff6fc9..fba4ea5f 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/currentSrc/currentSrc_property_exists-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_null-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_null-expected.txt
index 1049860..25e39adaf 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_null-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_null-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "null" is "null"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_onerror_called_on_bogus_source-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_onerror_called_on_bogus_source-expected.txt
index a90257a2c..0ddf265 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_onerror_called_on_bogus_source-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_onerror_called_on_bogus_source-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "4" is "4"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_property_exists-expected.txt
index 29198e78..151d6b5 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/error/error_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/error/error_property_exists-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay-expected.txt
index a1c45ff..c630e88 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay_manual-expected.txt
index b7ad44c..08720a7 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplay_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough-expected.txt
index 7e4ac6a..8bdc759 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough_manual-expected.txt
index bab33e1..9cdaaa8 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_canplaythrough_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata-expected.txt
index 7390189..09d5774 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata_manual-expected.txt
index fb4ac09..df5b362 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadeddata_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata-expected.txt
index 94037434..b44003b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata_manual-expected.txt
index 5770ccc2..652504b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadedmetadata_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart-expected.txt
index 707897ba..9f6633e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart_manual-expected.txt
index d6dc4bba..d3f3db2f 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_loadstart_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_canplaythrough-expected.txt
index 49fbd7a..f8ed66be 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_playing-expected.txt
index 7545d2c..afe9608 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_canplay_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadedmetadata_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadedmetadata_loadeddata-expected.txt
index ca20263..49989609 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadedmetadata_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadedmetadata_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadstart_progress-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadstart_progress-expected.txt
index e13c007..db5ec733 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadstart_progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_order_loadstart_progress-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_pause_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_pause_manual-expected.txt
index 8c4a5928..11d025b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_pause_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_pause_manual-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play-expected.txt
index c2544301..0a4bbb4b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play_manual-expected.txt
index 085e5e93..2973e2b 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_play_manual-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing-expected.txt
index 9443f4d9..2cf51c4e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing_manual-expected.txt
index 5f65f3e7..9004dde 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_playing_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress-expected.txt
index 004eeca..ba8eb91 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress_manual-expected.txt
index c9f3059f..1cfce079 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_progress_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate-expected.txt
index fec4ad3f4..cd9b4538 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate_manual-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate_manual-expected.txt
index 17a0c5b..6ebd1b0 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate_manual-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/events/event_timeupdate_manual-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_loadstart-expected.txt
index 826a628a..2d5f9f9 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_loadstart-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "2" is "2"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_progress-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_progress-expected.txt
index 2178262..c83b50e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_during_progress-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 FAIL "1" should be 2. Was 1.
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_initial-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_initial-expected.txt
index 64539c7..4d480df 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_initial-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_initial-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "0" is "0"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_property_exists-expected.txt
index 119bbe5..b431e1e 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/networkState/networkState_property_exists-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_false_during_play-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_false_during_play-expected.txt
index b2d014b..30d5dc89 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_false_during_play-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_false_during_play-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS false is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_true_during_pause-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_true_during_pause-expected.txt
index 3d84cef..b7b22ba 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_true_during_pause-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/paused/paused_true_during_pause-expected.txt
@@ -3,11 +3,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_property_exists-expected.txt
index 161c4b75..8293baf 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_property_exists-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_auto_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_auto_value-expected.txt
index a5c565d4f..fa541b4 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_auto_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_auto_value-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "auto" is "auto"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_bogus_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_bogus_value-expected.txt
index 58c859b6..85096897 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_bogus_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_bogus_value-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_empty-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_empty-expected.txt
index a5c565d4f..fa541b4 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_empty-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_empty-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "auto" is "auto"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_metadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_metadata-expected.txt
index 58c859b6..85096897 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_metadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_metadata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_no_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_no_value-expected.txt
index 58c859b6..85096897 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_no_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_no_value-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "metadata" is "metadata"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none-expected.txt
index cc54a5e..4609eda1 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "none" is "none"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none_autoplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none_autoplay-expected.txt
index cc54a5e..4609eda1 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none_autoplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/preload/preload_reflects_none_autoplay-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "none" is "none"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplay-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplay-expected.txt
index db63c5c..27d4a42 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplay-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplaythrough-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplaythrough-expected.txt
index 7e5b1a9..e94ad7ad 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplaythrough-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_canplaythrough-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "4" is "4"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadeddata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadeddata-expected.txt
index 0f6d9656..6b3626c3 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadeddata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadeddata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadedmetadata-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadedmetadata-expected.txt
index 363ef2b..50359663 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadedmetadata-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_loadedmetadata-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_playing-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_playing-expected.txt
index 0a43494..3331b93 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_playing-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_during_playing-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_initial-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_initial-expected.txt
index e277b61..040a7c9 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_initial-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_initial-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "0" is "0"
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_property_exists-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_property_exists-expected.txt
index 34b37a1..2cea77d 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_property_exists-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/readyState/readyState_property_exists-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_attribute_not_source_elements-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_attribute_not_source_elements-expected.txt
index 690c9ab729..c30a2511 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_attribute_not_source_elements-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_attribute_not_source_elements-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_no_value-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_no_value-expected.txt
index 3005fcb..7b150af 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_no_value-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_reflects_no_value-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS "" is ""
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_removal_does_not_trigger_loadstart-expected.txt b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_removal_does_not_trigger_loadstart-expected.txt
index e744462..b2d83df3 100644
--- a/third_party/WebKit/LayoutTests/media/W3C/video/src/src_removal_does_not_trigger_loadstart-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/W3C/video/src/src_removal_does_not_trigger_loadstart-expected.txt
@@ -2,11 +2,12 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS true is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 spec reference
 
 
diff --git a/third_party/WebKit/LayoutTests/media/absolute-positioned-video-crash-expected.txt b/third_party/WebKit/LayoutTests/media/absolute-positioned-video-crash-expected.txt
index d380fbf..730ebf6 100644
--- a/third_party/WebKit/LayoutTests/media/absolute-positioned-video-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/absolute-positioned-video-crash-expected.txt
@@ -1,2 +1 @@
 This test passes if it doesn't crash.
-
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt601-smpte-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt601-smpte-to-srgb-expected.txt
index 203b0d5..cc868b7 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt601-smpte-to-srgb-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt601-smpte-to-srgb-expected.txt
@@ -1,4 +1,3 @@
-
 Color         Actual        Expected      dE
 --------------------------------------------
 Dark Skin     115,80,64     115,80,64     0
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt709-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt709-to-srgb-expected.txt
index d34b900..4e72fad 100644
--- a/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt709-to-srgb-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/color-profile-munsell-bt709-to-srgb-expected.txt
@@ -1,4 +1,3 @@
-
 Color         Actual        Expected      dE
 --------------------------------------------
 Dark Skin     114,80,64     115,80,64     1
diff --git a/third_party/WebKit/LayoutTests/media/controls-slider-appearance-crash-expected.txt b/third_party/WebKit/LayoutTests/media/controls-slider-appearance-crash-expected.txt
index b29bed0..d101c50 100644
--- a/third_party/WebKit/LayoutTests/media/controls-slider-appearance-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/controls-slider-appearance-crash-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/media/media-document-audio-size-expected.txt b/third_party/WebKit/LayoutTests/media/media-document-audio-size-expected.txt
index cb02db2..089b79d 100644
--- a/third_party/WebKit/LayoutTests/media/media-document-audio-size-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/media-document-audio-size-expected.txt
@@ -2,3 +2,4 @@
 
 PASS
 
+
diff --git a/third_party/WebKit/LayoutTests/media/network-no-source-const-shadow-expected.txt b/third_party/WebKit/LayoutTests/media/network-no-source-const-shadow-expected.txt
index 34a6005a..70f920b 100644
--- a/third_party/WebKit/LayoutTests/media/network-no-source-const-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/network-no-source-const-shadow-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS HTMLMediaElement.NETWORK_NO_SOURCE is 3
 PASS HTMLMediaElement.prototype.NETWORK_NO_SOURCE is 3
 PASS element.NETWORK_NO_SOURCE is 3
diff --git a/third_party/WebKit/LayoutTests/media/remove-from-document-config-controls-no-crash-expected.txt b/third_party/WebKit/LayoutTests/media/remove-from-document-config-controls-no-crash-expected.txt
index d131b088..ba526420 100644
--- a/third_party/WebKit/LayoutTests/media/remove-from-document-config-controls-no-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/remove-from-document-config-controls-no-crash-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/media/video-buffered-too-few-arguments-expected.txt b/third_party/WebKit/LayoutTests/media/video-buffered-too-few-arguments-expected.txt
index 06352f9..d2e5a36 100644
--- a/third_party/WebKit/LayoutTests/media/video-buffered-too-few-arguments-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-buffered-too-few-arguments-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS v.buffered.start() threw exception TypeError: Failed to execute 'start' on 'TimeRanges': 1 argument required, but only 0 present..
 PASS v.buffered.end() threw exception TypeError: Failed to execute 'end' on 'TimeRanges': 1 argument required, but only 0 present..
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-start-selection-expected.txt b/third_party/WebKit/LayoutTests/media/video-controls-start-selection-expected.txt
index d10e6a1..8c339a5 100644
--- a/third_party/WebKit/LayoutTests/media/video-controls-start-selection-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-controls-start-selection-expected.txt
@@ -2,6 +2,8 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
+
+
 Start selection on video with controls
 
  Some text here
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-visible-audio-only-expected.txt b/third_party/WebKit/LayoutTests/media/video-controls-visible-audio-only-expected.txt
index ad66654..ec4df24 100644
--- a/third_party/WebKit/LayoutTests/media/video-controls-visible-audio-only-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-controls-visible-audio-only-expected.txt
@@ -4,7 +4,8 @@
 
 mouse parks here, am I blue?
 
- 
+
+
 
 TEST(video.paused) OK
 TEST(!video.paused) OK
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-with-mutation-event-handler-expected.txt b/third_party/WebKit/LayoutTests/media/video-controls-with-mutation-event-handler-expected.txt
index 1889004..1f37edf 100644
--- a/third_party/WebKit/LayoutTests/media/video-controls-with-mutation-event-handler-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-controls-with-mutation-event-handler-expected.txt
@@ -2,5 +2,4 @@
 
 If you can see the movie below, the test passed.
 
-
 SUCCESS: The test ran without crashing
diff --git a/third_party/WebKit/LayoutTests/media/video-default-poster-expected.txt b/third_party/WebKit/LayoutTests/media/video-default-poster-expected.txt
index 5c50a8e6..1f514b5 100644
--- a/third_party/WebKit/LayoutTests/media/video-default-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-default-poster-expected.txt
@@ -1,4 +1,3 @@
 PASS
 PASS
 PASS
-
diff --git a/third_party/WebKit/LayoutTests/media/video-no-default-poster-expected.txt b/third_party/WebKit/LayoutTests/media/video-no-default-poster-expected.txt
index f7f0fa5..38e0352 100644
--- a/third_party/WebKit/LayoutTests/media/video-no-default-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/media/video-no-default-poster-expected.txt
@@ -1,3 +1,2 @@
 PASS
 PASS
-
diff --git a/third_party/WebKit/LayoutTests/mhtml/cid_in_html_iframe-expected.txt b/third_party/WebKit/LayoutTests/mhtml/cid_in_html_iframe-expected.txt
index 96fce66..7fcd869a 100644
--- a/third_party/WebKit/LayoutTests/mhtml/cid_in_html_iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/mhtml/cid_in_html_iframe-expected.txt
@@ -1,2 +1 @@
-
 No Content-ID url are expected outside of an MHTML document. The document should load without crashing.
diff --git a/third_party/WebKit/LayoutTests/mhtml/cid_in_html_resource-expected.txt b/third_party/WebKit/LayoutTests/mhtml/cid_in_html_resource-expected.txt
index 96fce66..7fcd869a 100644
--- a/third_party/WebKit/LayoutTests/mhtml/cid_in_html_resource-expected.txt
+++ b/third_party/WebKit/LayoutTests/mhtml/cid_in_html_resource-expected.txt
@@ -1,2 +1 @@
-
 No Content-ID url are expected outside of an MHTML document. The document should load without crashing.
diff --git a/third_party/WebKit/LayoutTests/netinfo/basic-operation-expected.txt b/third_party/WebKit/LayoutTests/netinfo/basic-operation-expected.txt
index e2c42204..7c4fd3b5 100644
--- a/third_party/WebKit/LayoutTests/netinfo/basic-operation-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/basic-operation-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS navigator.connection is defined.
 PASS navigator.connection.type is defined.
 PASS navigator.connection.saveData is defined.
diff --git a/third_party/WebKit/LayoutTests/netinfo/connection-types-expected.txt b/third_party/WebKit/LayoutTests/netinfo/connection-types-expected.txt
index 8a7c3b6..ad4ef5e 100644
--- a/third_party/WebKit/LayoutTests/netinfo/connection-types-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/connection-types-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS connection.type is types[count][0]
 PASS connection.downlinkMax is types[count][1]
 PASS connection.type is types[count][0]
diff --git a/third_party/WebKit/LayoutTests/netinfo/estimate-basic-operation-expected.txt b/third_party/WebKit/LayoutTests/netinfo/estimate-basic-operation-expected.txt
index 94a51ab5..3e51b0f 100644
--- a/third_party/WebKit/LayoutTests/netinfo/estimate-basic-operation-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/estimate-basic-operation-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS navigator.connection is defined.
 PASS navigator.connection.type is defined.
 PASS navigator.connection.saveData is defined.
diff --git a/third_party/WebKit/LayoutTests/netinfo/estimate-web-worker-expected.txt b/third_party/WebKit/LayoutTests/netinfo/estimate-web-worker-expected.txt
index c3a18b21..0fe27ef 100644
--- a/third_party/WebKit/LayoutTests/netinfo/estimate-web-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/estimate-web-worker-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/netinfo/gc-frame-listeners-expected.txt b/third_party/WebKit/LayoutTests/netinfo/gc-frame-listeners-expected.txt
index 798678f9..586ba257 100644
--- a/third_party/WebKit/LayoutTests/netinfo/gc-frame-listeners-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/gc-frame-listeners-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS childFrameObserver.wasCollected is false
 PASS childConnectionObserver.wasCollected is false
 PASS callbackObserver.wasCollected is false
diff --git a/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners-expected.txt b/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners-expected.txt
index f7ad9c17..4983a2b0 100644
--- a/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/gc-unused-listeners-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS callbackObserver.wasCollected is false
 PASS callbackObserver.wasCollected is true
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/netinfo/gc-used-listeners-expected.txt b/third_party/WebKit/LayoutTests/netinfo/gc-used-listeners-expected.txt
index 74a78f3..ce4f249 100644
--- a/third_party/WebKit/LayoutTests/netinfo/gc-used-listeners-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/gc-used-listeners-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS callbackObserver.wasCollected is false
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/netinfo/multiple-frames-expected.txt b/third_party/WebKit/LayoutTests/netinfo/multiple-frames-expected.txt
index 2ad4a5dd..a91b1e1 100644
--- a/third_party/WebKit/LayoutTests/netinfo/multiple-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/multiple-frames-expected.txt
@@ -2,8 +2,8 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
 
+
diff --git a/third_party/WebKit/LayoutTests/netinfo/network-quality-expected.txt b/third_party/WebKit/LayoutTests/netinfo/network-quality-expected.txt
index d807449c..dcdc2fa 100644
--- a/third_party/WebKit/LayoutTests/netinfo/network-quality-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/network-quality-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS connection.effectiveType is values[count][3]
 PASS connection.rtt is values[count][4]
 PASS connection.downlink is values[count][5]
diff --git a/third_party/WebKit/LayoutTests/netinfo/saveData-basic-operation-expected.txt b/third_party/WebKit/LayoutTests/netinfo/saveData-basic-operation-expected.txt
index 2028130..86bf103 100644
--- a/third_party/WebKit/LayoutTests/netinfo/saveData-basic-operation-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/saveData-basic-operation-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS navigator.connection is defined.
 PASS navigator.connection.type is defined.
 PASS navigator.connection.saveData is defined.
diff --git a/third_party/WebKit/LayoutTests/netinfo/type-change-no-listener-expected.txt b/third_party/WebKit/LayoutTests/netinfo/type-change-no-listener-expected.txt
index 28ddab5..7a3c36d 100644
--- a/third_party/WebKit/LayoutTests/netinfo/type-change-no-listener-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/type-change-no-listener-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS connection.type is newConnectionType
 PASS connection.downlinkMax is newDownlinkMax
 PASS connection.effectiveType is newEffectiveType
diff --git a/third_party/WebKit/LayoutTests/netinfo/unregister-during-event-expected.txt b/third_party/WebKit/LayoutTests/netinfo/unregister-during-event-expected.txt
index 5eda713c..d4ae67a9 100644
--- a/third_party/WebKit/LayoutTests/netinfo/unregister-during-event-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/unregister-during-event-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS connection.type is newConnectionType
 PASS connection.downlinkMax is newDownlinkMax
 PASS connection.effectiveType is initialEffectiveType
diff --git a/third_party/WebKit/LayoutTests/netinfo/web-worker-expected.txt b/third_party/WebKit/LayoutTests/netinfo/web-worker-expected.txt
index c3a18b21..0fe27ef 100644
--- a/third_party/WebKit/LayoutTests/netinfo/web-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/netinfo/web-worker-expected.txt
@@ -2,7 +2,6 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/overflow-text-hit-testing-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/overflow-text-hit-testing-expected.png
index 94ac06b..d2132815 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/overflow-text-hit-testing-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/overflow-text-hit-testing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/linux/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
index 280b5cf..7a129ff9 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..721093c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
index b32229f..aa51b9d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..f4466ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..f4466ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..f4466ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..f4466ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-text-hit-testing-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-text-hit-testing-expected.png
index 2e7eed22..5357d87 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-text-hit-testing-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-text-hit-testing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/mac/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
index 2b95f88..3c0eeb751 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-srgb-to-srgb-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-srgb-to-srgb-expected.txt
index 61e3d1ad..37a3b7d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-srgb-to-srgb-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-srgb-to-srgb-expected.txt
@@ -1,4 +1,3 @@
-
 Color         Actual        Expected      dE
 --------------------------------------------
 Dark Skin     115,80,64     115,80,64     0
@@ -31,7 +30,8 @@
 --------------------------------------------
 
 Result: total RMS color error: 0.00
- * Munsell Cyan is outside 255 sRGB gamut
+
+* Munsell Cyan is outside 255 sRGB gamut
 
   
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index 57327e60..f4466ab 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
index a2b88cf..0ef62a7 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-text-hit-testing-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-text-hit-testing-expected.png
index d502dd3..649544d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-text-hit-testing-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-text-hit-testing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index ccb72df..721093c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
new file mode 100644
index 0000000..ca06a90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
new file mode 100644
index 0000000..721093c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..7f9645e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
index b32229f..aa51b9d 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/reporting-observer/buffering.html b/third_party/WebKit/LayoutTests/reporting-observer/buffering.html
index 0368a10..a02789b 100644
--- a/third_party/WebKit/LayoutTests/reporting-observer/buffering.html
+++ b/third_party/WebKit/LayoutTests/reporting-observer/buffering.html
@@ -35,7 +35,7 @@
       // is set to false by default.
       assert_equals(reports.length, 1);
       assert_equals(reports[0].type, "deprecation");
-      assert_equals(reports[0].body.id, "PrefixedWindowURL");
+      assert_equals(reports[0].body.id, "PrefixedRequestAnimationFrame");
     });
 
     test.done();
@@ -45,7 +45,7 @@
   // and one after calling observe().
   causeIntervention();
   observer2.observe();
-  window.webkitURL;  // id = "PrefixedWindowURL"
+  webkitRequestAnimationFrame(() => {});  // id = "PrefixedCancelAnimationFrame"
   observer2.disconnect();
 }, "Buffered reports not observed");
 </script>
diff --git a/third_party/WebKit/LayoutTests/reporting-observer/resources/deprecation.js b/third_party/WebKit/LayoutTests/reporting-observer/resources/deprecation.js
index 5078e75..37a6724 100644
--- a/third_party/WebKit/LayoutTests/reporting-observer/resources/deprecation.js
+++ b/third_party/WebKit/LayoutTests/reporting-observer/resources/deprecation.js
@@ -38,5 +38,5 @@
 
   // Use two deprecated features to generate two deprecation reports.
   window.webkitStorageInfo;
-  window.webkitURL;
+  window.webkitRequestAnimationFrame(() => {});
 }, "Deprecation reports");
diff --git a/third_party/WebKit/LayoutTests/reporting-observer/type-filtering.html b/third_party/WebKit/LayoutTests/reporting-observer/type-filtering.html
index cf77153..1d13037 100644
--- a/third_party/WebKit/LayoutTests/reporting-observer/type-filtering.html
+++ b/third_party/WebKit/LayoutTests/reporting-observer/type-filtering.html
@@ -15,7 +15,7 @@
       // |types| option.
       assert_equals(reports.length, 1);
       assert_equals(reports[0].type, "deprecation");
-      assert_equals(reports[0].body.id, "PrefixedWindowURL");
+      assert_equals(reports[0].body.id, "PrefixedRequestAnimationFrame");
     });
 
     test.done();
@@ -23,7 +23,8 @@
   observer1.observe();
 
   // Generate a deprecation report and an intervention report.
-  window.webkitURL;  // id = "PrefixedWindowURL"
+  // id = "PrefixedRequestAnimationFrame"
+  webkitRequestAnimationFrame(() => {});
   causeIntervention();
 
   observer1.disconnect();
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index f7bcb49..6cdbeef 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 5cd9a18..8402f40 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-platform-specific-expected.txt
index 9dcb6e8..72d6afde 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 80: 'webkitURL' is deprecated. Please use 'URL' instead.
 This test documents all interface attributes and methods on the global window object and element instances.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/abseil-cpp/absl/hash/internal/hash.h b/third_party/abseil-cpp/absl/hash/internal/hash.h
index 4543d67..78217d2 100644
--- a/third_party/abseil-cpp/absl/hash/internal/hash.h
+++ b/third_party/abseil-cpp/absl/hash/internal/hash.h
@@ -303,13 +303,13 @@
 
 // AbslHashValue for hashing tuples
 template <typename H, typename... Ts>
-#if _MSC_VER
+#if defined(_MSC_VER)
 // This SFINAE gets MSVC confused under some conditions. Let's just disable it
 // for now.
 H
-#else
+#else  // _MSC_VER
 typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type
-#endif
+#endif  // _MSC_VER
 AbslHashValue(H hash_state, const std::tuple<Ts...>& t) {
   return hash_internal::hash_tuple(std::move(hash_state), t,
                                    absl::make_index_sequence<sizeof...(Ts)>());
@@ -536,7 +536,7 @@
 // In MSVC we can't probe std::hash or stdext::hash because it triggers a
 // static_assert instead of failing substitution.
 #if defined(_MSC_VER)
-#undef ABSL_HASH_INTERNAL_CAN_POISON_
+#define ABSL_HASH_INTERNAL_CAN_POISON_ 0
 #else   // _MSC_VER
 #define ABSL_HASH_INTERNAL_CAN_POISON_ 1
 #endif  // _MSC_VER
@@ -544,6 +544,8 @@
 #if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \
     ABSL_HASH_INTERNAL_CAN_POISON_
 #define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1
+#else
+#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0
 #endif
 
 enum class InvokeHashTag {
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc
index d0a8054..1237911 100644
--- a/third_party/blink/common/feature_policy/feature_policy.cc
+++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -34,10 +34,12 @@
     mojom::FeaturePolicyFeature feature,
     bool matches_all_origins,
     bool matches_opaque_src,
+    mojom::FeaturePolicyDisposition disposition,
     std::vector<url::Origin> origins)
     : feature(feature),
       matches_all_origins(matches_all_origins),
       matches_opaque_src(matches_opaque_src),
+      disposition(disposition),
       origins(origins) {}
 
 ParsedFeaturePolicyDeclaration::ParsedFeaturePolicyDeclaration(
@@ -56,8 +58,22 @@
   // but-not-identical allowlists, or eliminate those comparisons by maintaining
   // the allowlists in a normalized form.
   // https://crbug.com/710324
-  return std::tie(lhs.feature, lhs.matches_all_origins, lhs.origins) ==
-         std::tie(rhs.feature, rhs.matches_all_origins, rhs.origins);
+  return std::tie(lhs.feature, lhs.matches_all_origins, lhs.origins,
+                  lhs.disposition) == std::tie(rhs.feature,
+                                               rhs.matches_all_origins,
+                                               rhs.origins, rhs.disposition);
+}
+
+std::unique_ptr<ParsedFeaturePolicy> DirectivesWithDisposition(
+    mojom::FeaturePolicyDisposition disposition,
+    const ParsedFeaturePolicy& policy) {
+  std::unique_ptr<ParsedFeaturePolicy> filtered_policy =
+      std::make_unique<ParsedFeaturePolicy>();
+  for (const auto& directive : policy) {
+    if (directive.disposition == disposition)
+      filtered_policy->push_back(directive);
+  }
+  return filtered_policy;
 }
 
 FeaturePolicy::Allowlist::Allowlist() : matches_all_origins_(false) {}
diff --git a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
index 468be4c..d96a6b5 100644
--- a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
+++ b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
@@ -13,6 +13,7 @@
     Read(blink::mojom::ParsedFeaturePolicyDeclarationDataView in,
          blink::ParsedFeaturePolicyDeclaration* out) {
   out->matches_all_origins = in.matches_all_origins();
+  out->disposition = in.disposition();
 
   return in.ReadOrigins(&out->origins) && in.ReadFeature(&out->feature);
 }
diff --git a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
index 8f8bfb9..c0fe8ee 100644
--- a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
+++ b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
@@ -83,6 +83,10 @@
       const blink::ParsedFeaturePolicyDeclaration& policy) {
     return policy.matches_all_origins;
   }
+  static blink::mojom::FeaturePolicyDisposition disposition(
+      const blink::ParsedFeaturePolicyDeclaration& policy) {
+    return policy.disposition;
+  }
   static const std::vector<url::Origin>& origins(
       const blink::ParsedFeaturePolicyDeclaration& policy) {
     return policy.origins;
diff --git a/third_party/blink/common/feature_policy/feature_policy_unittest.cc b/third_party/blink/common/feature_policy/feature_policy_unittest.cc
index 7b30aec..1f8294f 100644
--- a/third_party/blink/common/feature_policy/feature_policy_unittest.cc
+++ b/third_party/blink/common/feature_policy/feature_policy_unittest.cc
@@ -143,8 +143,11 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_b_}}}});
+  policy2->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultSelfFeature));
 }
 
@@ -166,8 +169,11 @@
   // they are at a different origin.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_a_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -199,8 +205,11 @@
   // it is embedded by frame 2, for which the feature is not enabled.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -227,8 +236,11 @@
   // enabled.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -248,8 +260,9 @@
   // Default-on feature should be disabled in top-level frame.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultOnFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   EXPECT_FALSE(policy1->IsFeatureEnabled(kDefaultOnFeature));
 }
 
@@ -265,8 +278,9 @@
   // Feature should be disabled in child frame.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultOnFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_a_);
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOnFeature));
@@ -286,8 +300,9 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}});
+  policy2->SetHeaderPolicy({{{kDefaultOnFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOnFeature));
 }
 
@@ -310,7 +325,11 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy({{{kDefaultOnFeature, false, false, {origin_b_}}}});
+  policy2->SetHeaderPolicy({{{kDefaultOnFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
@@ -329,8 +348,9 @@
   // Default-on feature should be disabled in cross-origin child frame.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultOnFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOnFeature));
@@ -352,8 +372,9 @@
   // Feature should be enabled in top and second level; disabled in frame 3.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -379,7 +400,11 @@
   // Feature should be disabled in frame 1; enabled in frames 2, 3 and 4.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy({{{kDefaultOnFeature, false, false, {origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOnFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -408,8 +433,11 @@
   // Feature should be disabled in frames 1 and 4; enabled in frames 2 and 3.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -438,15 +466,27 @@
   // Feature should be disabled in frames 1, 3 and 4; enabled in frame 2 only.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_b_}}}});
+  policy2->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
-  policy4->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_c_}}}});
+  policy4->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_c_}}}});
   EXPECT_FALSE(policy1->IsFeatureEnabled(kDefaultOffFeature));
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOffFeature));
   EXPECT_FALSE(policy3->IsFeatureEnabled(kDefaultOffFeature));
@@ -469,12 +509,14 @@
   // Feature should be enabled in all frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy2->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_a_);
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultSelfFeature));
@@ -498,12 +540,16 @@
   // Feature should be enabled at the top level; disabled in all other frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy2->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_a_);
   std::unique_ptr<FeaturePolicy> policy4 =
@@ -530,12 +576,18 @@
   // Feature should be enabled in all frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_, origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_b_, origin_c_}}}});
+  policy2->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_, origin_c_}}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultSelfFeature));
@@ -559,8 +611,11 @@
   // Feature should be enabled in frames 1, 2, and 3, and disabled in frame 4.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultOnFeature, false, false, {origin_a_, origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOnFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -590,8 +645,11 @@
   // 4.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_, origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -623,14 +681,24 @@
   // should be enabled in frame 1, and disabled in frames 2 and 3.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_, origin_b_}},
-        {kDefaultOnFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}},
+                             {kDefaultOnFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()},
-        {kDefaultOnFeature, true, false, std::vector<url::Origin>()}}});
+      {{{kDefaultSelfFeature, true, false,
+         mojom::FeaturePolicyDisposition::kEnforce, std::vector<url::Origin>()},
+        {kDefaultOnFeature, true, false,
+         mojom::FeaturePolicyDisposition::kEnforce,
+         std::vector<url::Origin>()}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultSelfFeature));
@@ -650,8 +718,11 @@
   // and disabled for origin C.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultOffFeature, false, false, {origin_a_, origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}}}});
   EXPECT_TRUE(
       policy1->IsFeatureEnabledForOrigin(kDefaultOffFeature, origin_a_));
   EXPECT_TRUE(
@@ -680,7 +751,11 @@
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, false, false, {origin_b_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy, origin_b_);
   EXPECT_TRUE(
@@ -715,7 +790,9 @@
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy, origin_b_);
   EXPECT_TRUE(
@@ -761,11 +838,19 @@
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultSelfFeature, false, false, {origin_b_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultSelfFeature, false, false, {origin_c_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_c_}}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy2.get(), frame_policy2, origin_c_);
   std::unique_ptr<FeaturePolicy> policy4 =
@@ -806,11 +891,15 @@
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}};
+      {{kDefaultOnFeature, false, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_a_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultOnFeature, false, false, std::vector<url::Origin>()}}};
+      {{kDefaultOnFeature, false, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
   EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
@@ -851,13 +940,25 @@
   // child frames because they did not declare their own policy to enable it.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultOffFeature, false, false, {origin_a_}}}};
+      {{kDefaultOffFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_a_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_a_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultOffFeature, false, false, {origin_b_}}}};
+      {{kDefaultOffFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
   EXPECT_TRUE(
@@ -902,17 +1003,37 @@
   // they declare their own policy to enable it.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_a_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultOffFeature, false, false, {origin_a_}}}};
+      {{kDefaultOffFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_a_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_a_);
-  policy2->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_a_}}}});
+  policy2->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_}}}});
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultOffFeature, false, false, {origin_b_}}}};
+      {{kDefaultOffFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
-  policy3->SetHeaderPolicy({{{kDefaultOffFeature, false, false, {origin_b_}}}});
+  policy3->SetHeaderPolicy({{{kDefaultOffFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   EXPECT_TRUE(
       policy1->IsFeatureEnabledForOrigin(kDefaultOffFeature, origin_a_));
   EXPECT_FALSE(
@@ -956,18 +1077,28 @@
   // policy.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_a_, origin_b_}}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_a_, origin_b_}}}});
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultSelfFeature, false, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, false, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultSelfFeature, false, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, false, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
-  policy3->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, {origin_b_}}}});
+  policy3->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
   EXPECT_FALSE(
@@ -1002,13 +1133,20 @@
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultSelfFeature, false, false, {origin_b_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
-  policy2->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy2->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultSelfFeature, false, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, false, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy2.get(), frame_policy2, origin_c_);
   std::unique_ptr<FeaturePolicy> policy4 =
@@ -1043,14 +1181,21 @@
   // Default-self feature should be disabled in all frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, false, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultSelfFeature, false, false, {origin_b_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_a_);
   EXPECT_FALSE(
@@ -1088,17 +1233,31 @@
   // enabled in the remaining frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy({{kDefaultSelfFeature, false, false, {origin_b_}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature,
+                              false,
+                              false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              {origin_b_}}}});
   ParsedFeaturePolicy frame_policy1 = {
-      {{kDefaultSelfFeature, false, false, {origin_a_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_a_}}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
-      {{kDefaultSelfFeature, false, false, {origin_b_}}}};
+      {{kDefaultSelfFeature,
+        false,
+        false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        {origin_b_}}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
   ParsedFeaturePolicy frame_policy3 = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy3, origin_b_);
   EXPECT_FALSE(
@@ -1164,7 +1323,9 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   url::Origin sandboxed_origin = url::Origin();
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
       policy1.get(), frame_policy, sandboxed_origin);
   EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
@@ -1195,7 +1356,9 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   url::Origin sandboxed_origin = url::Origin();
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, false, true, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, false, true,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
       policy1.get(), frame_policy, sandboxed_origin);
   EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
@@ -1223,11 +1386,14 @@
   // However, it will not pass that on to any other origin
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
-  policy1->SetHeaderPolicy(
-      {{{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}});
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature, true, false,
+                              mojom::FeaturePolicyDisposition::kEnforce,
+                              std::vector<url::Origin>()}}});
   url::Origin sandboxed_origin = url::Origin();
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, false, true, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, false, true,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
       policy1.get(), frame_policy, sandboxed_origin);
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultSelfFeature));
@@ -1260,7 +1426,9 @@
   url::Origin sandboxed_origin_1 = url::Origin();
   url::Origin sandboxed_origin_2 = url::Origin();
   ParsedFeaturePolicy frame_policy = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
       policy1.get(), frame_policy, sandboxed_origin_1);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -1304,11 +1472,15 @@
   url::Origin sandboxed_origin_1 = origin_a_.DeriveNewOpaqueOrigin();
   url::Origin sandboxed_origin_2 = sandboxed_origin_1.DeriveNewOpaqueOrigin();
   ParsedFeaturePolicy frame_policy_1 = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 = CreateFromParentWithFramePolicy(
       policy1.get(), frame_policy_1, sandboxed_origin_1);
   ParsedFeaturePolicy frame_policy_2 = {
-      {{kDefaultSelfFeature, true, false, std::vector<url::Origin>()}}};
+      {{kDefaultSelfFeature, true, false,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy3 = CreateFromParentWithFramePolicy(
       policy2.get(), frame_policy_2, sandboxed_origin_2);
   EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
@@ -1340,8 +1512,10 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy = {
       {{mojom::FeaturePolicyFeature::kNotFound, false, true,
-        std::vector<url::Origin>()},
-       {kUnavailableFeature, false, true, std::vector<url::Origin>()}}};
+        mojom::FeaturePolicyDisposition::kEnforce, std::vector<url::Origin>()},
+       {kUnavailableFeature, false, true,
+        mojom::FeaturePolicyDisposition::kEnforce,
+        std::vector<url::Origin>()}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy, origin_b_);
   EXPECT_FALSE(PolicyContainsInheritedValue(
@@ -1354,4 +1528,19 @@
       PolicyContainsInheritedValue(policy2.get(), kUnavailableFeature));
 }
 
+TEST_F(FeaturePolicyTest, TestReportOnlyFeaturesIncludedInHeader) {
+  // +---------------------------------------------------+
+  // |(1)Origin A                                        |
+  // |Feature-Policy: default-self-report-only 'none'    |
+  // +---------------------------------------------------+
+  // A feature which is tagged as '-report-only' should be included in the
+  // reporting policy.
+  std::unique_ptr<FeaturePolicy> policy1 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  policy1->SetHeaderPolicy({{{kDefaultSelfFeature, false, false,
+                              mojom::FeaturePolicyDisposition::kReport,
+                              std::vector<url::Origin>()}}});
+  EXPECT_FALSE(policy1->IsFeatureEnabled(kDefaultSelfFeature));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/public/common/feature_policy/feature_policy.h b/third_party/blink/public/common/feature_policy/feature_policy.h
index f90a7ab..e929f47 100644
--- a/third_party/blink/public/common/feature_policy/feature_policy.h
+++ b/third_party/blink/public/common/feature_policy/feature_policy.h
@@ -92,6 +92,7 @@
   ParsedFeaturePolicyDeclaration(mojom::FeaturePolicyFeature feature,
                                  bool matches_all_origins,
                                  bool matches_opaque_src,
+                                 mojom::FeaturePolicyDisposition disposition,
                                  std::vector<url::Origin> origins);
   ParsedFeaturePolicyDeclaration(const ParsedFeaturePolicyDeclaration& rhs);
   ParsedFeaturePolicyDeclaration& operator=(
@@ -106,6 +107,7 @@
   // of the iframe to be present in |origins|, but for sandboxed iframes, this
   // flag is set instead.
   bool matches_opaque_src;
+  mojom::FeaturePolicyDisposition disposition;
   std::vector<url::Origin> origins;
 };
 
@@ -114,6 +116,13 @@
 bool BLINK_COMMON_EXPORT operator==(const ParsedFeaturePolicyDeclaration& lhs,
                                     const ParsedFeaturePolicyDeclaration& rhs);
 
+// ParsedFeaturePolicy objects can contain directives of both enforcing and
+// report-only dispositions. This utility function will extract just the items
+// of one disposition or the other.
+BLINK_COMMON_EXPORT std::unique_ptr<ParsedFeaturePolicy>
+DirectivesWithDisposition(mojom::FeaturePolicyDisposition disposition,
+                          const ParsedFeaturePolicy& policy);
+
 class BLINK_COMMON_EXPORT FeaturePolicy {
  public:
   // Represents a collection of origins which make up an allowlist in a feature
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
index 508e0c03..63d4b44 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
@@ -105,6 +105,13 @@
   // chromium/src/tools/metrics/histograms/ to update the UMA mapping.
 };
 
+// This enum is used to distinguish between report-only directives and enforcing
+// directives.
+enum FeaturePolicyDisposition {
+  kEnforce,
+  kReport,
+};
+
 // This struct holds feature policy allowlist data that needs to be replicated
 // between a RenderFrame and any of its associated RenderFrameProxies. A list of
 // these form a ParsedFeaturePolicy.
@@ -112,5 +119,6 @@
 struct ParsedFeaturePolicyDeclaration {
   FeaturePolicyFeature feature;
   bool matches_all_origins;
+  FeaturePolicyDisposition disposition;
   array<url.mojom.Origin> origins;
 };
diff --git a/third_party/blink/public/platform/reporting.mojom b/third_party/blink/public/platform/reporting.mojom
index c9e9127..f1da9a34 100644
--- a/third_party/blink/public/platform/reporting.mojom
+++ b/third_party/blink/public/platform/reporting.mojom
@@ -51,6 +51,7 @@
   // (See //third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h.)
   QueueFeaturePolicyViolationReport(url.mojom.Url url,
                                     string policy,
+                                    string disposition,
                                     string message,
                                     string? source_file,
                                     int32 line_number,
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index a117fc4..6246a426 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2078,6 +2078,7 @@
   kCSSUnknownNamespacePrefixInSelector = 2626,
   kPageLifeCycleFreeze = 2627,
   kDefaultInCustomIdent = 2628,
+  kHTMLAnchorElementHrefTranslateAttribute = 2629,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
index cc2257f..5bab0a1b 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
@@ -150,11 +150,12 @@
   //             DOMString.
   if (event.IsBeforeUnloadEvent() &&
       event.type() == event_type_names::kBeforeunload) {
-    DCHECK(result_for_beforeunload);
-    event.preventDefault();
-    BeforeUnloadEvent* before_unload_event = ToBeforeUnloadEvent(&event);
-    if (before_unload_event->returnValue().IsEmpty())
-      before_unload_event->setReturnValue(result_for_beforeunload);
+    if (result_for_beforeunload) {
+      event.preventDefault();
+      BeforeUnloadEvent* before_unload_event = ToBeforeUnloadEvent(&event);
+      if (before_unload_event->returnValue().IsEmpty())
+        before_unload_event->setReturnValue(result_for_beforeunload);
+    }
   } else if (!IsOnBeforeUnloadEventHandler()) {
     if (special_error_event_handling && v8_return_value->IsBoolean() &&
         v8_return_value.As<v8::Boolean>()->Value())
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index b6811cc..a7a9c1a 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1729,6 +1729,7 @@
     "css/css_computed_style_declaration_test.cc",
     "css/css_font_face_source_test.cc",
     "css/css_gradient_value_test.cc",
+    "css/css_invalid_variable_value_test.cc",
     "css/css_page_rule_test.cc",
     "css/css_paint_value_test.cc",
     "css/css_primitive_value_test.cc",
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
index de8f1f2..101252c 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
@@ -120,7 +120,7 @@
   bool IsValid(const InterpolationEnvironment& environment,
                const InterpolationValue&) const final {
     DCHECK(ToCSSInterpolationEnvironment(environment).HasVariableResolver());
-    bool cycle_detected;
+    bool cycle_detected = false;
     scoped_refptr<CSSVariableData> resolved_tokens =
         ToCSSInterpolationEnvironment(environment)
             .VariableResolver()
@@ -233,7 +233,7 @@
 
   scoped_refptr<CSSVariableData> resolved_tokens;
   if (declaration.Value()->NeedsVariableResolution()) {
-    bool cycle_detected;
+    bool cycle_detected = false;
     resolved_tokens = variable_resolver.ResolveCustomPropertyAnimationKeyframe(
         declaration, cycle_detected);
     DCHECK(!cycle_detected);
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index d347864..e155a4b 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -92,6 +92,8 @@
     "css_inherited_value.h",
     "css_initial_value.cc",
     "css_initial_value.h",
+    "css_invalid_variable_value.cc",
+    "css_invalid_variable_value.h",
     "css_keyframe_rule.cc",
     "css_keyframe_rule.h",
     "css_keyframes_rule.cc",
diff --git a/third_party/blink/renderer/core/css/css_invalid_variable_value.cc b/third_party/blink/renderer/core/css/css_invalid_variable_value.cc
new file mode 100644
index 0000000..7d191c2
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_invalid_variable_value.cc
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
+
+#include "third_party/blink/renderer/core/css/css_value_pool.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+CSSInvalidVariableValue* CSSInvalidVariableValue::Create() {
+  return CssValuePool().InvalidVariableValue();
+}
+
+String CSSInvalidVariableValue::CustomCSSText() const {
+  return "";
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_invalid_variable_value.h b/third_party/blink/renderer/core/css/css_invalid_variable_value.h
new file mode 100644
index 0000000..090ca83
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_invalid_variable_value.h
@@ -0,0 +1,35 @@
+// 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_CORE_CSS_CSS_INVALID_VARIABLE_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_INVALID_VARIABLE_VALUE_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_value.h"
+
+namespace blink {
+
+class CORE_EXPORT CSSInvalidVariableValue : public CSSValue {
+ public:
+  static CSSInvalidVariableValue* Create();
+
+  String CustomCSSText() const;
+
+  bool Equals(const CSSInvalidVariableValue&) const { return true; }
+
+  void TraceAfterDispatch(blink::Visitor* visitor) {
+    CSSValue::TraceAfterDispatch(visitor);
+  }
+
+ private:
+  friend class CSSValuePool;
+
+  CSSInvalidVariableValue() : CSSValue(kInvalidVariableValueClass) {}
+};
+
+DEFINE_CSS_VALUE_TYPE_CASTS(CSSInvalidVariableValue, IsInvalidVariableValue());
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_INVALID_VARIABLE_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/css_invalid_variable_value_test.cc b/third_party/blink/renderer/core/css/css_invalid_variable_value_test.cc
new file mode 100644
index 0000000..dcedc4f
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_invalid_variable_value_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace {
+
+TEST(CSSInvalidVariableValueTest, Create) {
+  EXPECT_TRUE(CSSInvalidVariableValue::Create());
+}
+
+TEST(CSSInvalidVariableValueTest, Pool) {
+  const CSSInvalidVariableValue* value1 = CSSInvalidVariableValue::Create();
+  const CSSInvalidVariableValue* value2 = CSSInvalidVariableValue::Create();
+  EXPECT_EQ(value1, value2);
+}
+
+TEST(CSSInvalidVariableValueTest, Equals) {
+  const CSSInvalidVariableValue* value1 = CSSInvalidVariableValue::Create();
+  const CSSInvalidVariableValue* value2 = CSSInvalidVariableValue::Create();
+  EXPECT_TRUE(value1->Equals(*value2));
+}
+
+TEST(CSSInvalidVariableValueTest, CustomCSSText) {
+  EXPECT_EQ("", CSSInvalidVariableValue::Create()->CustomCSSText());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc
index d3a88cbf..3b39794 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -97,7 +97,7 @@
   if (value < 0 || value > CSSValuePool::kMaximumCacheableIntegerValue)
     return new CSSPrimitiveValue(value, type);
 
-  int int_value = static_cast<int>(value);
+  int int_value = clampTo<int>(value);
   if (value != int_value)
     return new CSSPrimitiveValue(value, type);
 
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc
index 565e099..e00e74e 100644
--- a/third_party/blink/renderer/core/css/css_value.cc
+++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/css/css_image_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
 #include "third_party/blink/renderer/core/css/css_layout_function_value.h"
 #include "third_party/blink/renderer/core/css/css_paint_value.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
@@ -246,6 +247,8 @@
         return CompareCSSValues<CSSVariableReferenceValue>(*this, other);
       case kPendingSubstitutionValueClass:
         return CompareCSSValues<CSSPendingSubstitutionValue>(*this, other);
+      case kInvalidVariableValueClass:
+        return CompareCSSValues<CSSInvalidVariableValue>(*this, other);
     }
     NOTREACHED();
     return false;
@@ -351,6 +354,8 @@
       return ToCSSCustomPropertyDeclaration(this)->CustomCSSText();
     case kPendingSubstitutionValueClass:
       return ToCSSPendingSubstitutionValue(this)->CustomCSSText();
+    case kInvalidVariableValueClass:
+      return ToCSSInvalidVariableValue(this)->CustomCSSText();
   }
   NOTREACHED();
   return String();
@@ -503,6 +508,9 @@
     case kPendingSubstitutionValueClass:
       ToCSSPendingSubstitutionValue(this)->~CSSPendingSubstitutionValue();
       return;
+    case kInvalidVariableValueClass:
+      ToCSSInvalidVariableValue(this)->~CSSInvalidVariableValue();
+      return;
   }
   NOTREACHED();
 }
@@ -653,6 +661,9 @@
     case kPendingSubstitutionValueClass:
       ToCSSPendingSubstitutionValue(this)->TraceAfterDispatch(visitor);
       return;
+    case kInvalidVariableValueClass:
+      ToCSSInvalidVariableValue(this)->TraceAfterDispatch(visitor);
+      return;
   }
   NOTREACHED();
 }
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index 84b6e12..e1bd0489 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -161,6 +161,9 @@
   bool IsPendingSubstitutionValue() const {
     return class_type_ == kPendingSubstitutionValueClass;
   }
+  bool IsInvalidVariableValue() const {
+    return class_type_ == kInvalidVariableValueClass;
+  }
 
   bool HasFailedOrCanceledSubresources() const;
   bool MayContainUrl() const;
@@ -235,6 +238,7 @@
     kVariableReferenceClass,
     kCustomPropertyDeclarationClass,
     kPendingSubstitutionValueClass,
+    kInvalidVariableValueClass,
     kLayoutFunctionClass,
 
     kCSSContentDistributionClass,
diff --git a/third_party/blink/renderer/core/css/css_value_pool.cc b/third_party/blink/renderer/core/css/css_value_pool.cc
index 147ed0e..d9fec1f 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.cc
+++ b/third_party/blink/renderer/core/css/css_value_pool.cc
@@ -47,6 +47,7 @@
     : inherited_value_(new CSSInheritedValue),
       initial_value_(new CSSInitialValue()),
       unset_value_(new CSSUnsetValue),
+      invalid_variable_value_(new CSSInvalidVariableValue),
       color_transparent_(new CSSColorValue(Color::kTransparent)),
       color_white_(new CSSColorValue(Color::kWhite)),
       color_black_(new CSSColorValue(Color::kBlack)) {
@@ -60,6 +61,7 @@
   visitor->Trace(inherited_value_);
   visitor->Trace(initial_value_);
   visitor->Trace(unset_value_);
+  visitor->Trace(invalid_variable_value_);
   visitor->Trace(color_transparent_);
   visitor->Trace(color_white_);
   visitor->Trace(color_black_);
diff --git a/third_party/blink/renderer/core/css/css_value_pool.h b/third_party/blink/renderer/core/css/css_value_pool.h
index ecfe36cd..1f2cb9d1 100644
--- a/third_party/blink/renderer/core/css/css_value_pool.h
+++ b/third_party/blink/renderer/core/css/css_value_pool.h
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -67,6 +68,9 @@
   CSSInheritedValue* InheritedValue() { return inherited_value_; }
   CSSInitialValue* InitialValue() { return initial_value_; }
   CSSUnsetValue* UnsetValue() { return unset_value_; }
+  CSSInvalidVariableValue* InvalidVariableValue() {
+    return invalid_variable_value_;
+  }
 
   // Vector caches.
   CSSIdentifierValue* IdentifierCacheValue(CSSValueID ident) {
@@ -126,6 +130,7 @@
   Member<CSSInheritedValue> inherited_value_;
   Member<CSSInitialValue> initial_value_;
   Member<CSSUnsetValue> unset_value_;
+  Member<CSSInvalidVariableValue> invalid_variable_value_;
   Member<CSSColorValue> color_transparent_;
   Member<CSSColorValue> color_white_;
   Member<CSSColorValue> color_black_;
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
index 8ee79047..363efe4 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/resolver/css_variable_resolver.h"
 
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
+#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
 #include "third_party/blink/renderer/core/css/css_pending_substitution_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
@@ -109,11 +110,31 @@
 
   CSSVariableData* variable_data = GetVariable(name, registration);
 
-  if (!variable_data)
-    return registration ? registration->InitialVariableData() : nullptr;
+  if (!variable_data) {
+    // For unregistered properties, not having a CSSVariableData here means
+    // that it either never existed, or we have resolved it earlier, but
+    // resolution failed. Either way, we return nullptr to signify that this is
+    // an invalid variable.
+    if (!registration)
+      return nullptr;
+    // For registered properties, it's more complicated. Here too, it can mean
+    // that it never existed, or that resolution failed earlier, but now we need
+    // to know which; in the former case we must provide the initial value, and
+    // in the latter case the variable is invalid.
+    return IsRegisteredVariableInvalid(name, *registration)
+               ? nullptr
+               : registration->InitialVariableData();
+  }
 
-  scoped_refptr<CSSVariableData> resolved_data =
-      ResolveCustomPropertyIfNeeded(name, variable_data, options);
+  bool cycle_detected = false;
+  scoped_refptr<CSSVariableData> resolved_data = ResolveCustomPropertyIfNeeded(
+      name, variable_data, options, cycle_detected);
+
+  if (!resolved_data && cycle_detected) {
+    if (options.absolutize)
+      SetInvalidVariable(name, registration);
+    return nullptr;
+  }
 
   if (resolved_data) {
     if (IsVariableDisallowed(*resolved_data, options, registration))
@@ -198,9 +219,10 @@
   bool success = ResolveTokenRange(variable_data.Tokens(), options, result);
   variables_seen_.erase(name);
 
-  if (!success || !cycle_start_points_.IsEmpty()) {
-    cycle_start_points_.erase(name);
+  if (!cycle_start_points_.IsEmpty())
     cycle_detected = true;
+  if (!success || cycle_detected) {
+    cycle_start_points_.erase(name);
     return nullptr;
   }
   cycle_detected = false;
@@ -220,14 +242,14 @@
 CSSVariableResolver::ResolveCustomPropertyIfNeeded(
     AtomicString name,
     CSSVariableData* variable_data,
-    const Options& options) {
+    const Options& options,
+    bool& cycle_detected) {
   DCHECK(variable_data);
   bool resolve_urls = ShouldResolveRelativeUrls(name, *variable_data);
   if (!variable_data->NeedsVariableResolution() && !resolve_urls)
     return variable_data;
-  bool unused_cycle_detected;
   return ResolveCustomProperty(name, *variable_data, options, resolve_urls,
-                               unused_cycle_detected);
+                               cycle_detected);
 }
 
 void CSSVariableResolver::ResolveRelativeUrls(
@@ -320,6 +342,25 @@
   }
 }
 
+void CSSVariableResolver::SetInvalidVariable(
+    const AtomicString& name,
+    const PropertyRegistration* registration) {
+  // TODO(andruud): Use RemoveVariable instead, but currently it also does
+  // a lookup in the registered map, which seems wasteful.
+  SetVariable(name, registration, nullptr);
+  if (registration) {
+    const CSSValue* value = CSSInvalidVariableValue::Create();
+    SetRegisteredVariable(name, *registration, value);
+  }
+}
+
+bool CSSVariableResolver::IsRegisteredVariableInvalid(
+    const AtomicString& name,
+    const PropertyRegistration& registration) {
+  const CSSValue* value = GetRegisteredVariable(name, registration);
+  return value && value->IsInvalidVariableValue();
+}
+
 bool CSSVariableResolver::ResolveVariableReference(CSSParserTokenRange range,
                                                    const Options& options,
                                                    bool is_env_variable,
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
index d748138..c137115 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
@@ -163,7 +163,8 @@
   scoped_refptr<CSSVariableData> ResolveCustomPropertyIfNeeded(
       AtomicString name,
       CSSVariableData*,
-      const Options&);
+      const Options&,
+      bool& cycle_detected);
   // Rewrites (in-place) kUrlTokens and kFunctionToken/CSSValueUrls to contain
   // absolute URLs.
   void ResolveRelativeUrls(Vector<CSSParserToken>& tokens,
@@ -191,6 +192,10 @@
   void SetRegisteredVariable(const AtomicString& name,
                              const PropertyRegistration&,
                              const CSSValue*);
+  void SetInvalidVariable(const AtomicString& name,
+                          const PropertyRegistration*);
+  bool IsRegisteredVariableInvalid(const AtomicString& name,
+                                   const PropertyRegistration&);
 
   const StyleResolverState& state_;
   StyleInheritedVariables* inherited_variables_;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 227799f..196a751 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7676,8 +7676,10 @@
   return *lazy_load_image_observer_;
 }
 
-void Document::ReportFeaturePolicyViolation(mojom::FeaturePolicyFeature feature,
-                                            const String& message) const {
+void Document::ReportFeaturePolicyViolation(
+    mojom::FeaturePolicyFeature feature,
+    mojom::FeaturePolicyDisposition disposition,
+    const String& message) const {
   if (!RuntimeEnabledFeatures::FeaturePolicyReportingEnabled())
     return;
   LocalFrame* frame = GetFrame();
@@ -7685,7 +7687,10 @@
     return;
   const String& feature_name = GetNameForFeature(feature);
   FeaturePolicyViolationReportBody* body = new FeaturePolicyViolationReportBody(
-      feature_name, "Feature policy violation", SourceLocation::Capture());
+      feature_name, "Feature policy violation",
+      (disposition == mojom::FeaturePolicyDisposition::kReport ? "report"
+                                                               : "enforce"),
+      SourceLocation::Capture());
   Report* report = new Report("feature-policy", Url().GetString(), body);
   ReportingContext::From(this)->QueueReport(report);
 
@@ -7697,13 +7702,19 @@
 
   // Send the feature policy violation report to the Reporting API.
   frame->GetReportingService()->QueueFeaturePolicyViolationReport(
-      Url(), feature_name, "Feature policy violation", body->sourceFile(),
-      line_number, column_number);
-  frame->Console().AddMessage(ConsoleMessage::Create(
-      kViolationMessageSource, kErrorMessageLevel,
-      (message.IsEmpty() ? ("Feature policy violation: " + feature_name +
-                            " is not allowed in this document.")
-                         : message)));
+      Url(), feature_name,
+      (disposition == mojom::FeaturePolicyDisposition::kReport ? "report"
+                                                               : "enforce"),
+      "Feature policy violation", body->sourceFile(), line_number,
+      column_number);
+  // TODO(iclelland): Report something different in report-only mode
+  if (disposition == mojom::FeaturePolicyDisposition::kEnforce) {
+    frame->Console().AddMessage(ConsoleMessage::Create(
+        kViolationMessageSource, kErrorMessageLevel,
+        (message.IsEmpty() ? ("Feature policy violation: " + feature_name +
+                              " is not allowed in this document.")
+                           : message)));
+  }
 }
 
 void Document::IncrementNumberOfCanvases() {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index f1d89465..f2f5a59ff 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1488,6 +1488,7 @@
 
   void ReportFeaturePolicyViolation(
       mojom::FeaturePolicyFeature,
+      mojom::FeaturePolicyDisposition,
       const String& message = g_empty_string) const override;
 
   bool IsParsedFeaturePolicy(mojom::FeaturePolicyFeature feature) const {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 8b283a3..7ef69b1 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2231,7 +2231,6 @@
   }
 
   if (ShouldCallRecalcStyleForChildren(change)) {
-
     UpdatePseudoElement(kPseudoIdBefore, change);
 
     if (change > kUpdatePseudoElements || ChildNeedsStyleRecalc()) {
@@ -3218,7 +3217,9 @@
          IsRootEditableElement(*this) ||
          (IsShadowHost(this) && AuthorShadowRoot() &&
           AuthorShadowRoot()->delegatesFocus()) ||
-         SupportsSpatialNavigationFocus();
+         SupportsSpatialNavigationFocus() ||
+         (RuntimeEnabledFeatures::KeyboardFocusableScrollersEnabled() &&
+          IsScrollableNode(this));
 }
 
 bool Element::SupportsSpatialNavigationFocus() const {
diff --git a/third_party/blink/renderer/core/editing/granularity_strategy.cc b/third_party/blink/renderer/core/editing/granularity_strategy.cc
index 307fd639..aa1e71b7 100644
--- a/third_party/blink/renderer/core/editing/granularity_strategy.cc
+++ b/third_party/blink/renderer/core/editing/granularity_strategy.cc
@@ -40,19 +40,19 @@
 // the direction in which to search for the next bound. nextIfOnBound
 // controls whether |pos| or the next boundary is returned when |pos| is
 // located exactly on word boundary.
-static VisiblePosition NextWordBound(const VisiblePosition& pos,
-                                     SearchDirection direction,
-                                     BoundAdjust word_bound_adjust) {
+static Position NextWordBound(const Position& pos,
+                              SearchDirection direction,
+                              BoundAdjust word_bound_adjust) {
   bool next_bound_if_on_bound =
       word_bound_adjust == BoundAdjust::kNextBoundIfOnBound;
   if (direction == SearchDirection::kSearchForward) {
     EWordSide word_side = next_bound_if_on_bound ? kNextWordIfOnBoundary
                                                  : kPreviousWordIfOnBoundary;
-    return EndOfWord(pos, word_side);
+    return EndOfWordPosition(pos, word_side);
   }
   EWordSide word_side = next_bound_if_on_bound ? kPreviousWordIfOnBoundary
                                                : kNextWordIfOnBoundary;
-  return StartOfWord(pos, word_side);
+  return StartOfWordPosition(pos, word_side);
 }
 
 GranularityStrategy::GranularityStrategy() = default;
@@ -198,32 +198,34 @@
 
     // Determine the word boundary, i.e. the boundary extending beyond which
     // should change the granularity to WordGranularity.
-    VisiblePosition word_boundary;
+    Position word_boundary_position;
     if (extent_base_order_switched) {
       // Special case.
       // If the extent-base order was switched, then the selection is now
       // expanding in a different direction than before. Therefore we
       // calculate the word boundary in this new direction and based on
       // the |base| position.
-      word_boundary = NextWordBound(base,
-                                    new_extent_base_order > 0
-                                        ? SearchDirection::kSearchForward
-                                        : SearchDirection::kSearchBackwards,
-                                    BoundAdjust::kNextBoundIfOnBound);
+      word_boundary_position = NextWordBound(
+          base.DeepEquivalent(),
+          new_extent_base_order > 0 ? SearchDirection::kSearchForward
+                                    : SearchDirection::kSearchBackwards,
+          BoundAdjust::kNextBoundIfOnBound);
       granularity_ = TextGranularity::kCharacter;
     } else {
       // Calculate the word boundary based on |oldExtentWithGranularity|.
       // If selection was shrunk in the last update and the extent is now
       // exactly on the word boundary - we need to take the next bound as
       // the bound of the current word.
-      word_boundary = NextWordBound(old_offset_extent_position,
-                                    old_extent_base_order > 0
-                                        ? SearchDirection::kSearchForward
-                                        : SearchDirection::kSearchBackwards,
-                                    state_ == StrategyState::kShrinking
-                                        ? BoundAdjust::kNextBoundIfOnBound
-                                        : BoundAdjust::kCurrentPosIfOnBound);
+      word_boundary_position = NextWordBound(
+          old_offset_extent_position.DeepEquivalent(),
+          old_extent_base_order > 0 ? SearchDirection::kSearchForward
+                                    : SearchDirection::kSearchBackwards,
+          state_ == StrategyState::kShrinking
+              ? BoundAdjust::kNextBoundIfOnBound
+              : BoundAdjust::kCurrentPosIfOnBound);
     }
+    VisiblePosition word_boundary =
+        CreateVisiblePosition(word_boundary_position);
 
     bool expanded_beyond_word_boundary;
     if (selection_expanded)
@@ -251,12 +253,12 @@
     // Determine the bounds of the word where the extent is located.
     // Set the selection extent to one of the two bounds depending on
     // whether the extent is passed the middle of the word.
-    VisiblePosition bound_before_extent = NextWordBound(
-        new_offset_extent_position, SearchDirection::kSearchBackwards,
-        BoundAdjust::kCurrentPosIfOnBound);
-    VisiblePosition bound_after_extent = NextWordBound(
-        new_offset_extent_position, SearchDirection::kSearchForward,
-        BoundAdjust::kCurrentPosIfOnBound);
+    VisiblePosition bound_before_extent = CreateVisiblePosition(NextWordBound(
+        new_offset_extent_position.DeepEquivalent(),
+        SearchDirection::kSearchBackwards, BoundAdjust::kCurrentPosIfOnBound));
+    VisiblePosition bound_after_extent = CreateVisiblePosition(NextWordBound(
+        new_offset_extent_position.DeepEquivalent(),
+        SearchDirection::kSearchForward, BoundAdjust::kCurrentPosIfOnBound));
     int x_middle_between_bounds = (PositionLocation(bound_after_extent).X() +
                                    PositionLocation(bound_before_extent).X()) /
                                   2;
diff --git a/third_party/blink/renderer/core/editing/selection_adjuster.cc b/third_party/blink/renderer/core/editing/selection_adjuster.cc
index 3953903..fd8ec16 100644
--- a/third_party/blink/renderer/core/editing/selection_adjuster.cc
+++ b/third_party/blink/renderer/core/editing/selection_adjuster.cc
@@ -186,7 +186,8 @@
         const VisiblePositionTemplate<Strategy> original_end =
             CreateVisiblePosition(passed_end);
         const VisiblePositionTemplate<Strategy> word_end =
-            EndOfWord(original_end, ChooseWordSide(original_end));
+            CreateVisiblePosition(EndOfWordPosition(
+                passed_end.GetPosition(), ChooseWordSide(original_end)));
         if (!IsEndOfParagraph(original_end))
           return word_end.DeepEquivalent();
         if (IsEmptyTableCell(start.AnchorNode()))
diff --git a/third_party/blink/renderer/core/execution_context/security_context.cc b/third_party/blink/renderer/core/execution_context/security_context.cc
index 60748fa6f..28651e7 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context.cc
@@ -111,32 +111,70 @@
   feature_policy_ = std::move(feature_policy);
 }
 
+// Uses the parent enforcing policy; parsed_header and container_policy can
+// both contain report-only directives, which will be used to construct the
+// report-only policy for this context.
 void SecurityContext::InitializeFeaturePolicy(
     const ParsedFeaturePolicy& parsed_header,
     const ParsedFeaturePolicy& container_policy,
     const FeaturePolicy* parent_feature_policy) {
+  report_only_feature_policy_ = nullptr;
   if (!HasCustomizedFeaturePolicy()) {
     feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
         nullptr, {}, security_origin_->ToUrlOrigin());
     return;
   }
+
   feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
-      parent_feature_policy, container_policy, security_origin_->ToUrlOrigin());
-  feature_policy_->SetHeaderPolicy(parsed_header);
+      parent_feature_policy,
+      *DirectivesWithDisposition(mojom::FeaturePolicyDisposition::kEnforce,
+                                 container_policy),
+      security_origin_->ToUrlOrigin());
+  feature_policy_->SetHeaderPolicy(*DirectivesWithDisposition(
+      mojom::FeaturePolicyDisposition::kEnforce, parsed_header));
+  if (RuntimeEnabledFeatures::FeaturePolicyReportingEnabled()) {
+    report_only_feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
+        parent_feature_policy,
+        *DirectivesWithDisposition(mojom::FeaturePolicyDisposition::kReport,
+                                   container_policy),
+        security_origin_->ToUrlOrigin());
+    report_only_feature_policy_->SetHeaderPolicy(*DirectivesWithDisposition(
+        mojom::FeaturePolicyDisposition::kReport, parsed_header));
+  }
 }
 
 bool SecurityContext::IsFeatureEnabled(mojom::FeaturePolicyFeature feature,
                                        ReportOptions report_on_failure,
                                        const String& message) const {
+  FeatureEnabledState state = GetFeatureEnabledState(feature);
+  if (state == FeatureEnabledState::kEnabled)
+    return true;
+  if (report_on_failure == ReportOptions::kReportOnFailure &&
+      RuntimeEnabledFeatures::FeaturePolicyReportingEnabled()) {
+    ReportFeaturePolicyViolation(
+        feature,
+        (state == FeatureEnabledState::kReportOnly
+             ? mojom::FeaturePolicyDisposition::kReport
+             : mojom::FeaturePolicyDisposition::kEnforce),
+        message);
+  }
+  return (state != FeatureEnabledState::kDisabled);
+}
+
+FeatureEnabledState SecurityContext::GetFeatureEnabledState(
+    mojom::FeaturePolicyFeature feature) const {
   // The policy should always be initialized before checking it to ensure we
   // properly inherit the parent policy.
   DCHECK(feature_policy_);
 
-  if (feature_policy_->IsFeatureEnabled(feature))
-    return true;
-  if (report_on_failure == ReportOptions::kReportOnFailure)
-    ReportFeaturePolicyViolation(feature, message);
-  return false;
+  if (feature_policy_->IsFeatureEnabled(feature)) {
+    if (report_only_feature_policy_ &&
+        !report_only_feature_policy_->IsFeatureEnabled(feature)) {
+      return FeatureEnabledState::kReportOnly;
+    }
+    return FeatureEnabledState::kEnabled;
+  }
+  return FeatureEnabledState::kDisabled;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/security_context.h b/third_party/blink/renderer/core/execution_context/security_context.h
index 52347473d..0257f3e 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.h
+++ b/third_party/blink/renderer/core/execution_context/security_context.h
@@ -51,8 +51,10 @@
 // Whether to report policy violations when checking whether a feature is
 // enabled.
 enum class ReportOptions { kReportOnFailure, kDoNotReport };
+enum class FeatureEnabledState { kDisabled, kReportOnly, kEnabled };
 
 namespace mojom {
+enum class FeaturePolicyDisposition : int32_t;
 enum class FeaturePolicyFeature : int32_t;
 enum class IPAddressSpace : int32_t;
 }
@@ -124,6 +126,9 @@
   }
 
   FeaturePolicy* GetFeaturePolicy() const { return feature_policy_.get(); }
+  FeaturePolicy* GetReportOnlyFeaturePolicy() const {
+    return report_only_feature_policy_.get();
+  }
   void SetFeaturePolicy(std::unique_ptr<FeaturePolicy> feature_policy);
   void InitializeFeaturePolicy(const ParsedFeaturePolicy& parsed_header,
                                const ParsedFeaturePolicy& container_policy,
@@ -138,8 +143,10 @@
       mojom::FeaturePolicyFeature,
       ReportOptions report_on_failure = ReportOptions::kDoNotReport,
       const String& message = g_empty_string) const;
+  FeatureEnabledState GetFeatureEnabledState(mojom::FeaturePolicyFeature) const;
   virtual void ReportFeaturePolicyViolation(
       mojom::FeaturePolicyFeature,
+      mojom::FeaturePolicyDisposition,
       const String& message = g_empty_string) const {}
 
   // Apply the sandbox flag. In addition, if the origin is not already opaque,
@@ -165,6 +172,7 @@
   scoped_refptr<SecurityOrigin> security_origin_;
   Member<ContentSecurityPolicy> content_security_policy_;
   std::unique_ptr<FeaturePolicy> feature_policy_;
+  std::unique_ptr<FeaturePolicy> report_only_feature_policy_;
 
   mojom::IPAddressSpace address_space_;
   WebInsecureRequestPolicy insecure_request_policy_;
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
index 7f707d2..7797037 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
@@ -94,15 +94,17 @@
 // It forwards its ThreadableLoaderClient notifications to a
 // WebAssociatedURLLoaderClient.
 class WebAssociatedURLLoaderImpl::ClientAdapter final
-    : public ThreadableLoaderClient {
+    : public GarbageCollectedFinalized<ClientAdapter>,
+      public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(ClientAdapter);
+
  public:
-  static std::unique_ptr<ClientAdapter> Create(
-      WebAssociatedURLLoaderImpl*,
-      WebAssociatedURLLoaderClient*,
-      const WebAssociatedURLLoaderOptions&,
-      network::mojom::FetchRequestMode,
-      network::mojom::FetchCredentialsMode,
-      scoped_refptr<base::SingleThreadTaskRunner>);
+  ClientAdapter(WebAssociatedURLLoaderImpl*,
+                WebAssociatedURLLoaderClient*,
+                const WebAssociatedURLLoaderOptions&,
+                network::mojom::FetchRequestMode,
+                network::mojom::FetchCredentialsMode,
+                scoped_refptr<base::SingleThreadTaskRunner>);
 
   // ThreadableLoaderClient
   void DidSendData(unsigned long long /*bytesSent*/,
@@ -140,13 +142,6 @@
   }
 
  private:
-  ClientAdapter(WebAssociatedURLLoaderImpl*,
-                WebAssociatedURLLoaderClient*,
-                const WebAssociatedURLLoaderOptions&,
-                network::mojom::FetchRequestMode,
-                network::mojom::FetchCredentialsMode,
-                scoped_refptr<base::SingleThreadTaskRunner>);
-
   void NotifyError(TimerBase*);
 
   WebAssociatedURLLoaderImpl* loader_;
@@ -163,19 +158,6 @@
   DISALLOW_COPY_AND_ASSIGN(ClientAdapter);
 };
 
-std::unique_ptr<WebAssociatedURLLoaderImpl::ClientAdapter>
-WebAssociatedURLLoaderImpl::ClientAdapter::Create(
-    WebAssociatedURLLoaderImpl* loader,
-    WebAssociatedURLLoaderClient* client,
-    const WebAssociatedURLLoaderOptions& options,
-    network::mojom::FetchRequestMode fetch_request_mode,
-    network::mojom::FetchCredentialsMode credentials_mode,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  return base::WrapUnique(new ClientAdapter(loader, client, options,
-                                            fetch_request_mode,
-                                            credentials_mode, task_runner));
-}
-
 WebAssociatedURLLoaderImpl::ClientAdapter::ClientAdapter(
     WebAssociatedURLLoaderImpl* loader,
     WebAssociatedURLLoaderClient* client,
@@ -406,7 +388,7 @@
     task_runner = Thread::Current()->GetTaskRunner();
   }
   client_ = client;
-  client_adapter_ = ClientAdapter::Create(
+  client_adapter_ = MakeGarbageCollected<ClientAdapter>(
       this, client, options_, request.GetFetchRequestMode(),
       request.GetFetchCredentialsMode(), std::move(task_runner));
 
@@ -442,7 +424,7 @@
 
     if (observer_) {
       Document& document = To<Document>(*observer_->LifecycleContext());
-      loader_ = new ThreadableLoader(document, client_adapter_.get(),
+      loader_ = new ThreadableLoader(document, client_adapter_,
                                      resource_loader_options);
       loader_->Start(webcore_request);
     }
@@ -477,7 +459,7 @@
     loader_->Cancel();
     loader_ = nullptr;
   }
-  client_adapter_.reset();
+  client_adapter_ = nullptr;
 }
 
 void WebAssociatedURLLoaderImpl::SetDefersLoading(bool defers_loading) {
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.h b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.h
index b74551d9..6d6f21c 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.h
@@ -59,7 +59,7 @@
 
   // An adapter which converts the hreadableLoaderClient method
   // calls into the WebURLLoaderClient method calls.
-  std::unique_ptr<ClientAdapter> client_adapter_;
+  Persistent<ClientAdapter> client_adapter_;
   Persistent<ThreadableLoader> loader_;
 
   // A ContextLifecycleObserver for cancelling |m_loader| when the Document
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
index 5f6828e..63876c9 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -17,6 +17,9 @@
 
 namespace blink {
 
+constexpr char kReportOnlySuffix[] = "-report-only";
+constexpr size_t kReportOnlySuffixLength = 12;
+
 ParsedFeaturePolicy ParseFeaturePolicyHeader(
     const String& policy,
     scoped_refptr<const SecurityOrigin> origin,
@@ -64,15 +67,31 @@
       // Empty policy. Skip.
       if (tokens.IsEmpty())
         continue;
-      if (!feature_names.Contains(tokens[0])) {
-        if (messages)
+      mojom::FeaturePolicyDisposition disposition =
+          mojom::FeaturePolicyDisposition::kEnforce;
+      String feature_name;
+      if (RuntimeEnabledFeatures::FeaturePolicyReportingEnabled() &&
+          tokens[0].EndsWith(kReportOnlySuffix)) {
+        feature_name = tokens[0].Substring(
+            0, tokens[0].length() - kReportOnlySuffixLength);
+        disposition = mojom::FeaturePolicyDisposition::kReport;
+      } else {
+        feature_name = tokens[0];
+      }
+      if (!feature_names.Contains(feature_name)) {
+        if (messages) {
+          // Console message should display the entire string, with
+          // "-report-only" suffix if it was originally included.
           messages->push_back("Unrecognized feature: '" + tokens[0] + "'.");
+        }
         continue;
       }
 
-      mojom::FeaturePolicyFeature feature = feature_names.at(tokens[0]);
+      mojom::FeaturePolicyFeature feature = feature_names.at(feature_name);
       // If a policy has already been specified for the current feature, drop
       // the new policy.
+      // TODO(crbug.com/904880): Allow a report-only and an enforcing version in
+      // the same parsed policy.
       if (features_specified.QuickGet(static_cast<int>(feature)))
         continue;
 
@@ -92,6 +111,7 @@
 
       ParsedFeaturePolicyDeclaration allowlist;
       allowlist.feature = feature;
+      allowlist.disposition = disposition;
       features_specified.QuickSet(static_cast<int>(feature));
       std::vector<url::Origin> origins;
       // If a policy entry has no (optional) values (e,g,
@@ -181,6 +201,7 @@
   allowlist.feature = feature;
   allowlist.matches_all_origins = false;
   allowlist.matches_opaque_src = false;
+  allowlist.disposition = mojom::FeaturePolicyDisposition::kEnforce;
   policy.push_back(allowlist);
   return true;
 }
@@ -193,6 +214,7 @@
   allowlist.feature = feature;
   allowlist.matches_all_origins = true;
   allowlist.matches_opaque_src = true;
+  allowlist.disposition = mojom::FeaturePolicyDisposition::kEnforce;
   policy.push_back(allowlist);
   return true;
 }
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
index 3ec5bf5..54d36ab 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
@@ -439,10 +439,12 @@
   ParsedFeaturePolicy test_policy = {{mojom::FeaturePolicyFeature::kFullscreen,
                                       false,
                                       false,
+                                      mojom::FeaturePolicyDisposition::kEnforce,
                                       {url_origin_a_, url_origin_b_}},
                                      {mojom::FeaturePolicyFeature::kGeolocation,
                                       false,
                                       false,
+                                      mojom::FeaturePolicyDisposition::kEnforce,
                                       {url_origin_a_}}};
   ParsedFeaturePolicy empty_policy = {};
 };
diff --git a/third_party/blink/renderer/core/feature_policy/iframe_policy.h b/third_party/blink/renderer/core/feature_policy/iframe_policy.h
index e53ec64..d4645da 100644
--- a/third_party/blink/renderer/core/feature_policy/iframe_policy.h
+++ b/third_party/blink/renderer/core/feature_policy/iframe_policy.h
@@ -33,7 +33,9 @@
       const ParsedFeaturePolicy& container_policy,
       scoped_refptr<const SecurityOrigin> src_origin) override {
     policy_ = FeaturePolicy::CreateFromParentPolicy(
-        parent_document_->GetFeaturePolicy(), container_policy,
+        parent_document_->GetFeaturePolicy(),
+        *DirectivesWithDisposition(mojom::FeaturePolicyDisposition::kEnforce,
+                                   container_policy),
         src_origin->ToUrlOrigin());
   }
 
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 393c8b2..c388917 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -201,7 +201,7 @@
 class FetchManager::Loader final
     : public GarbageCollectedFinalized<FetchManager::Loader>,
       public ThreadableLoaderClient {
-  USING_PRE_FINALIZER(FetchManager::Loader, Dispose);
+  USING_GARBAGE_COLLECTED_MIXIN(Loader);
 
  public:
   static Loader* Create(ExecutionContext* execution_context,
@@ -215,7 +215,7 @@
   }
 
   ~Loader() override;
-  virtual void Trace(blink::Visitor*);
+  void Trace(blink::Visitor*) override;
 
   bool WillFollowRedirect(const KURL&, const ResourceResponse&) override;
   void DidReceiveResponse(unsigned long,
@@ -413,6 +413,7 @@
   visitor->Trace(integrity_verifier_);
   visitor->Trace(signal_);
   visitor->Trace(execution_context_);
+  ThreadableLoaderClient::Trace(visitor);
 }
 
 bool FetchManager::Loader::WillFollowRedirect(
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
index b0a54f2e..ebe0794f 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
@@ -16,15 +16,20 @@
  public:
   FeaturePolicyViolationReportBody(const String& feature,
                                    const String& message,
+                                   const String& disposition,
                                    std::unique_ptr<SourceLocation> location)
-      : MessageReportBody(message, std::move(location)), feature_(feature) {}
+      : MessageReportBody(message, std::move(location)),
+        feature_(feature),
+        disposition_(disposition) {}
 
   String feature() const { return feature_; }
+  String disposition() const { return disposition_; }
 
   ~FeaturePolicyViolationReportBody() override = default;
 
  private:
   const String feature_;
+  const String disposition_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
index 42f3ef1c..968088b0 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
@@ -8,6 +8,7 @@
     NoInterfaceObject
 ] interface FeaturePolicyViolationReportBody : ReportBody {
   readonly attribute DOMString feature;
+  readonly attribute DOMString disposition;
   readonly attribute DOMString message;
   readonly attribute DOMString? sourceFile;
   readonly attribute unsigned long? lineNumber;
diff --git a/third_party/blink/renderer/core/frame/window.idl b/third_party/blink/renderer/core/frame/window.idl
index e1901d57..70afaaf6 100644
--- a/third_party/blink/renderer/core/frame/window.idl
+++ b/third_party/blink/renderer/core/frame/window.idl
@@ -187,6 +187,9 @@
     // https://github.com/WICG/aom/blob/HEAD/explainer.md
     [RuntimeEnabled=AccessibilityObjectModel, CallWith=ScriptState] Promise<ComputedAccessibleNode> getComputedAccessibleNode(Element element);
 
+    // TODO(yhirano): Move this to url.idl when LegacyWindowAlias is supported.
+    attribute URLConstructor webkitURL;
+
     // Non-standard APIs
     [MeasureAs=WindowClientInformation, Replaceable] readonly attribute Navigator clientInformation;
     [Replaceable, MeasureAs=WindowEvent, Custom=Getter, NotEnumerable] readonly attribute Event event;
@@ -205,7 +208,6 @@
     [MeasureAs=StyleMedia] readonly attribute StyleMedia styleMedia;
     [DeprecateAs=PrefixedRequestAnimationFrame] long webkitRequestAnimationFrame(FrameRequestCallback callback);
     [DeprecateAs=PrefixedCancelAnimationFrame, ImplementedAs=cancelAnimationFrame] void webkitCancelAnimationFrame(long id);
-    [DeprecateAs=PrefixedWindowURL] attribute URLConstructor webkitURL;
     [MeasureAs=PrefixedMutationObserverConstructor] attribute MutationObserverConstructor WebKitMutationObserver;
 
     // Event handler attributes
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 77655a92..674b516 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/core/loader/ping_loader.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
@@ -430,7 +431,12 @@
   }
   if (HasRel(kRelationNoOpener))
     frame_request.SetShouldSetOpener(kNeverSetOpener);
-  frame_request.SetHrefTranslate(FastGetAttribute(kHreftranslateAttr));
+  if (origin_trials::HrefTranslateEnabled(&GetDocument()) &&
+      hasAttribute(kHreftranslateAttr)) {
+    frame_request.SetHrefTranslate(FastGetAttribute(kHreftranslateAttr));
+    UseCounter::Count(GetDocument(),
+                      WebFeature::kHTMLAnchorElementHrefTranslateAttribute);
+  }
   frame_request.SetTriggeringEventInfo(
       event.isTrusted() ? WebTriggeringEventInfo::kFromTrustedEvent
                         : WebTriggeringEventInfo::kFromUntrustedEvent);
diff --git a/third_party/blink/renderer/core/html/html_frame_element.cc b/third_party/blink/renderer/core/html/html_frame_element.cc
index 46555b9..dc1bdd9 100644
--- a/third_party/blink/renderer/core/html/html_frame_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element.cc
@@ -85,14 +85,15 @@
 ParsedFeaturePolicy HTMLFrameElement::ConstructContainerPolicy(
     Vector<String>*) const {
   // Frame elements are not allowed to enable the fullscreen feature. Add an
-  // empty whitelist for the fullscreen feature so that the framed content is
+  // empty allowlist for the fullscreen feature so that the framed content is
   // unable to use the API, regardless of origin.
   // https://fullscreen.spec.whatwg.org/#model
   ParsedFeaturePolicy container_policy;
-  ParsedFeaturePolicyDeclaration whitelist;
-  whitelist.feature = mojom::FeaturePolicyFeature::kFullscreen;
-  whitelist.matches_all_origins = false;
-  container_policy.push_back(whitelist);
+  ParsedFeaturePolicyDeclaration allowlist;
+  allowlist.feature = mojom::FeaturePolicyFeature::kFullscreen;
+  allowlist.matches_all_origins = false;
+  allowlist.disposition = mojom::FeaturePolicyDisposition::kEnforce;
+  container_policy.push_back(allowlist);
   return container_policy;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index f7d52096..dec73f77 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -299,15 +299,16 @@
 ParsedFeaturePolicy HTMLPlugInElement::ConstructContainerPolicy(
     Vector<String>*) const {
   // Plugin elements (<object> and <embed>) are not allowed to enable the
-  // fullscreen feature. Add an empty whitelist for the fullscreen feature so
+  // fullscreen feature. Add an empty allowlist for the fullscreen feature so
   // that the nested browsing context is unable to use the API, regardless of
   // origin.
   // https://fullscreen.spec.whatwg.org/#model
   ParsedFeaturePolicy container_policy;
-  ParsedFeaturePolicyDeclaration whitelist;
-  whitelist.feature = mojom::FeaturePolicyFeature::kFullscreen;
-  whitelist.matches_all_origins = false;
-  container_policy.push_back(whitelist);
+  ParsedFeaturePolicyDeclaration allowlist;
+  allowlist.feature = mojom::FeaturePolicyFeature::kFullscreen;
+  allowlist.matches_all_origins = false;
+  allowlist.disposition = mojom::FeaturePolicyDisposition::kEnforce;
+  container_policy.push_back(allowlist);
   return container_policy;
 }
 
diff --git a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
index 8ac695d..8ab20da 100644
--- a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
+++ b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
@@ -69,7 +69,8 @@
   if (!style.LogicalWidth().IsSpecified() &&
       !style.LogicalHeight().IsSpecified()) {
     layout_object->GetDocument().ReportFeaturePolicyViolation(
-        mojom::FeaturePolicyFeature::kUnsizedMedia);
+        mojom::FeaturePolicyFeature::kUnsizedMedia,
+        mojom::FeaturePolicyDisposition::kEnforce);
   }
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 1b6d47502..b3348be 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -663,6 +663,7 @@
   visitor->Trace(inspected_frames_);
   visitor->Trace(worker_global_scope_);
   visitor->Trace(resources_data_);
+  visitor->Trace(pending_request_);
   visitor->Trace(replay_xhrs_);
   visitor->Trace(replay_xhrs_to_be_deleted_);
   visitor->Trace(pending_xhr_replay_data_);
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 5f2d2ed1..beeaa32 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -269,7 +269,7 @@
   // Stores the pending ThreadableLoaderClient till an identifier for
   // the load is generated by the loader and passed to the inspector
   // via the WillSendRequest() method.
-  ThreadableLoaderClient* pending_request_;
+  Member<ThreadableLoaderClient> pending_request_;
   InspectorPageAgent::ResourceType pending_request_type_;
 
   Member<XHRReplayData> pending_xhr_replay_data_;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index f1d27a4..7835792 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1007,7 +1007,7 @@
   String header_content_language =
       response_.HttpHeaderField(http_names::kContentLanguage);
   if (!header_content_language.IsEmpty()) {
-    size_t comma_index = header_content_language.find(',');
+    wtf_size_t comma_index = header_content_language.find(',');
     // kNotFound == -1 == don't truncate
     header_content_language.Truncate(comma_index);
     header_content_language =
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
index fee91a7c..6a7db26 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
@@ -377,7 +377,7 @@
   //
   // [FD] Step 7. These invocations of the internal module script graph fetching
   // procedure should be performed in parallel to each other.
-  for (size_t i = 0; i < urls.size(); ++i) {
+  for (wtf_size_t i = 0; i < urls.size(); ++i) {
     // [FD] Step 7. ... perform the internal module script graph fetching
     // procedure given url, fetch client settings object, destination, options,
     // module script's settings object, visited set, module script's base URL,
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index 69452f9..204f0ee32 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -28,6 +28,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/numerics/safe_conversions.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_info.h"
@@ -297,8 +298,9 @@
   GetContent()->DestroyDecodedData();
   if (GetContent()->HasImage() && !IsUnusedPreload() &&
       GetContent()->IsRefetchableDataFromDiskCache()) {
-    UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize",
-                            EncodedSize() / 1024);
+    UMA_HISTOGRAM_MEMORY_KB(
+        "Memory.Renderer.EstimatedDroppableEncodedSize",
+        base::saturated_cast<base::Histogram::Sample>(EncodedSize() / 1024));
   }
 }
 
@@ -335,7 +337,7 @@
 void ImageResource::AppendData(const char* data, size_t length) {
   v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length);
   if (multipart_parser_) {
-    multipart_parser_->AppendData(data, length);
+    multipart_parser_->AppendData(data, SafeCast<wtf_size_t>(length));
   } else {
     Resource::AppendData(data, length);
 
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
index e5764f1..b07ff69 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -77,7 +77,7 @@
   if (response.HttpHeaderField("chrome-proxy-content-transform") ==
       "empty-image") {
     const String& str = response.HttpHeaderField("chrome-proxy");
-    size_t index = str.Find("ofcl=");
+    wtf_size_t index = str.Find("ofcl=");
     if (index != kNotFound) {
       bool ok = false;
       int bytes = str.Substring(index + (sizeof("ofcl=") - 1)).ToInt(&ok);
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index 4c3522e..510be9592 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -1769,7 +1769,7 @@
   const struct {
     int status_code;
     AtomicString content_range;
-    size_t data_size;
+    uint32_t data_size;
   } tests[] = {
       {200, g_null_atom, sizeof(kBadImageData)},
       {206, BuildContentRange(sizeof(kBadImageData), sizeof(kBadImageData)),
diff --git a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.cc b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.cc
index f1d2fbde..0b032bc 100644
--- a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.cc
+++ b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.cc
@@ -23,7 +23,8 @@
     boundary_.push_front("--", 2);
 }
 
-void MultipartImageResourceParser::AppendData(const char* bytes, size_t size) {
+void MultipartImageResourceParser::AppendData(const char* bytes,
+                                              wtf_size_t size) {
   DCHECK(!IsCancelled());
   // m_sawLastBoundary means that we've already received the final boundary
   // token. The server should stop sending us data at this point, but if it
@@ -34,7 +35,7 @@
 
   if (is_parsing_top_) {
     // Eat leading \r\n
-    size_t pos = SkippableLength(data_, 0);
+    wtf_size_t pos = SkippableLength(data_, 0);
     // +2 for "--"
     if (data_.size() < boundary_.size() + 2 + pos) {
       // We don't have enough data yet to make a boundary token.  Just wait
@@ -65,11 +66,11 @@
       return;
   }
 
-  size_t boundary_position;
+  wtf_size_t boundary_position;
   while ((boundary_position = FindBoundary(data_, &boundary_)) != kNotFound) {
     // Strip out trailing \r\n characters in the buffer preceding the boundary
     // on the same lines as does Firefox.
-    size_t data_size = boundary_position;
+    wtf_size_t data_size = boundary_position;
     if (boundary_position > 0 && data_[boundary_position - 1] == '\n') {
       data_size--;
       if (boundary_position > 1 && data_[boundary_position - 2] == '\r') {
@@ -81,7 +82,7 @@
       if (IsCancelled())
         return;
     }
-    size_t boundary_end_position = boundary_position + boundary_.size();
+    wtf_size_t boundary_end_position = boundary_position + boundary_.size();
     if (boundary_end_position < data_.size() &&
         '-' == data_[boundary_end_position]) {
       // This was the last boundary so we can stop processing.
@@ -106,7 +107,7 @@
   // buffered to handle a boundary that may have been truncated. "+2" for CRLF,
   // as we may ignore the last CRLF.
   if (!is_parsing_headers_ && data_.size() > boundary_.size() + 2) {
-    size_t send_length = data_.size() - boundary_.size() - 2;
+    wtf_size_t send_length = data_.size() - boundary_.size() - 2;
     client_->MultipartDataReceived(data_.data(), send_length);
     data_.EraseAt(0, send_length);
   }
@@ -124,8 +125,9 @@
   saw_last_boundary_ = true;
 }
 
-size_t MultipartImageResourceParser::SkippableLength(const Vector<char>& data,
-                                                     size_t pos) {
+wtf_size_t MultipartImageResourceParser::SkippableLength(
+    const Vector<char>& data,
+    wtf_size_t pos) {
   if (data.size() >= pos + 2 && data[pos] == '\r' && data[pos + 1] == '\n')
     return 2;
   if (data.size() >= pos + 1 && data[pos] == '\n')
@@ -135,7 +137,7 @@
 
 bool MultipartImageResourceParser::ParseHeaders() {
   // Eat leading \r\n
-  size_t pos = SkippableLength(data_, 0);
+  wtf_size_t pos = SkippableLength(data_, 0);
 
   // Create a ResourceResponse based on the original set of headers + the
   // replacement headers. We only replace the same few headers that gecko does.
@@ -159,14 +161,14 @@
 
 // Boundaries are supposed to be preceeded with --, but it looks like gecko
 // doesn't require the dashes to exist.  See nsMultiMixedConv::FindToken.
-size_t MultipartImageResourceParser::FindBoundary(const Vector<char>& data,
-                                                  Vector<char>* boundary) {
+wtf_size_t MultipartImageResourceParser::FindBoundary(const Vector<char>& data,
+                                                      Vector<char>* boundary) {
   auto* it = std::search(data.data(), data.data() + data.size(),
                          boundary->data(), boundary->data() + boundary->size());
   if (it == data.data() + data.size())
     return kNotFound;
 
-  size_t boundary_position = it - data.data();
+  wtf_size_t boundary_position = static_cast<wtf_size_t>(it - data.data());
   // Back up over -- for backwards compat
   // TODO(tc): Don't we only want to do this once?  Gecko code doesn't seem to
   // care.
diff --git a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.h b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.h
index 2452ac4..cfa2351 100644
--- a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.h
+++ b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.h
@@ -64,26 +64,28 @@
   MultipartImageResourceParser(const ResourceResponse&,
                                const Vector<char>& boundary,
                                Client*);
-  void AppendData(const char* bytes, size_t);
+  void AppendData(const char* bytes, wtf_size_t);
   void Finish();
   void Cancel() { is_cancelled_ = true; }
 
   void Trace(blink::Visitor*);
 
-  static size_t SkippableLengthForTest(const Vector<char>& data, size_t size) {
+  static wtf_size_t SkippableLengthForTest(const Vector<char>& data,
+                                           wtf_size_t size) {
     return SkippableLength(data, size);
   }
-  static size_t FindBoundaryForTest(const Vector<char>& data,
-                                    Vector<char>* boundary) {
+  static wtf_size_t FindBoundaryForTest(const Vector<char>& data,
+                                        Vector<char>* boundary) {
     return FindBoundary(data, boundary);
   }
 
  private:
   bool ParseHeaders();
   bool IsCancelled() const { return is_cancelled_; }
-  static size_t SkippableLength(const Vector<char>&, size_t);
+  static wtf_size_t SkippableLength(const Vector<char>&, wtf_size_t);
   // This function updates |*boundary|.
-  static size_t FindBoundary(const Vector<char>& data, Vector<char>* boundary);
+  static wtf_size_t FindBoundary(const Vector<char>& data,
+                                 Vector<char>* boundary);
 
   const ResourceResponse original_response_;
   Vector<char> boundary_;
diff --git a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser_test.cc b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser_test.cc
index f3b1ae5..cf793f2 100644
--- a/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser_test.cc
@@ -4,13 +4,14 @@
 
 #include "third_party/blink/renderer/core/loader/resource/multipart_image_resource_parser.h"
 
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
 
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
 namespace blink {
 namespace multipart_image_resource_parser_test {
 
@@ -30,7 +31,7 @@
     data_.push_back(Vector<char>());
   }
   void MultipartDataReceived(const char* bytes, size_t size) override {
-    data_.back().Append(bytes, size);
+    data_.back().Append(bytes, SafeCast<wtf_size_t>(size));
   }
 
   Vector<ResourceResponse> responses_;
@@ -40,8 +41,8 @@
 TEST(MultipartResponseTest, SkippableLength) {
   struct {
     const char* input;
-    const size_t position;
-    const size_t expected;
+    const wtf_size_t position;
+    const wtf_size_t expected;
   } line_tests[] = {
       {"Line", 0, 0},         {"Line", 2, 0},         {"Line", 10, 0},
       {"\r\nLine", 0, 2},     {"\nLine", 0, 1},       {"\n\nLine", 0, 1},
@@ -50,7 +51,8 @@
   };
   for (size_t i = 0; i < arraysize(line_tests); ++i) {
     Vector<char> input;
-    input.Append(line_tests[i].input, strlen(line_tests[i].input));
+    input.Append(line_tests[i].input,
+                 static_cast<wtf_size_t>(strlen(line_tests[i].input)));
     EXPECT_EQ(line_tests[i].expected,
               MultipartImageResourceParser::SkippableLengthForTest(
                   input, line_tests[i].position));
@@ -71,8 +73,9 @@
   for (size_t i = 0; i < arraysize(boundary_tests); ++i) {
     Vector<char> boundary, data;
     boundary.Append(boundary_tests[i].boundary,
-                    strlen(boundary_tests[i].boundary));
-    data.Append(boundary_tests[i].data, strlen(boundary_tests[i].data));
+                    static_cast<uint32_t>(strlen(boundary_tests[i].boundary)));
+    data.Append(boundary_tests[i].data,
+                static_cast<uint32_t>(strlen(boundary_tests[i].data)));
     EXPECT_EQ(
         boundary_tests[i].position,
         MultipartImageResourceParser::FindBoundaryForTest(data, &boundary));
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index ee028f2..98789a1b 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -104,6 +104,8 @@
 class ThreadableLoader::DetachedClient final
     : public GarbageCollectedFinalized<DetachedClient>,
       public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(DetachedClient);
+
  public:
   explicit DetachedClient(ThreadableLoader* loader)
       : self_keep_alive_(this), loader_(loader) {}
@@ -114,7 +116,10 @@
   }
   void DidFail(const ResourceError&) override { self_keep_alive_.Clear(); }
   void DidFailRedirectCheck() override { self_keep_alive_.Clear(); }
-  void Trace(Visitor* visitor) { visitor->Trace(loader_); }
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(loader_);
+    ThreadableLoaderClient::Trace(visitor);
+  }
 
  private:
   SelfKeepAlive<DetachedClient> self_keep_alive_;
@@ -482,15 +487,7 @@
   LoadRequest(cross_origin_request, cross_origin_options);
 }
 
-ThreadableLoader::~ThreadableLoader() {
-  // |client_| is a raw pointer and having a non-null |client_| here probably
-  // means UaF.
-  // In the detached case, |this| is held by DetachedClient defined above, but
-  // SelfKeepAlive in DetachedClient is forcibly cancelled on worker thread
-  // termination. We can safely ignore this case.
-  CHECK(!client_ || detached_);
-  DCHECK(!GetResource());
-}
+ThreadableLoader::~ThreadableLoader() {}
 
 void ThreadableLoader::SetTimeout(const TimeDelta& timeout) {
   timeout_ = timeout;
@@ -913,7 +910,7 @@
 
   if (!actual_request_.IsNull())
     return;
-  client_->DidReceiveCachedMetadata(data, size);
+  client_->DidReceiveCachedMetadata(data, SafeCast<int>(size));
 }
 
 void ThreadableLoader::DataReceived(Resource* resource,
@@ -1105,6 +1102,7 @@
 
 void ThreadableLoader::Trace(blink::Visitor* visitor) {
   visitor->Trace(execution_context_);
+  visitor->Trace(client_);
   RawResourceClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.h b/third_party/blink/renderer/core/loader/threadable_loader.h
index 3b65f86..439fe892 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.h
+++ b/third_party/blink/renderer/core/loader/threadable_loader.h
@@ -89,10 +89,6 @@
   // After any of these methods is called, the loader won't call any of the
   // ThreadableLoaderClient methods.
   //
-  // A user must guarantee that the loading completes before the attached
-  // client gets invalid. Also, a user must guarantee that the loading
-  // completes before the ThreadableLoader is destructed.
-  //
   // When ThreadableLoader::Cancel() is called,
   // ThreadableLoaderClient::DidFail() is called with a ResourceError
   // with IsCancellation() returning true, if any of DidFinishLoading()
@@ -100,7 +96,7 @@
   // called with a ResourceError with IsCancellation() returning true
   // also for cancellation happened inside the loader.)
   //
-  // ThreadableLoaderClient methods may call cancel().
+  // ThreadableLoaderClient methods may call Cancel().
   ThreadableLoader(ExecutionContext&,
                    ThreadableLoaderClient*,
                    const ResourceLoaderOptions&);
@@ -211,7 +207,7 @@
   // TODO(kinuko): Remove dependency to document.
   Document* GetDocument() const;
 
-  ThreadableLoaderClient* client_;
+  Member<ThreadableLoaderClient> client_;
   Member<ExecutionContext> execution_context_;
 
   TimeDelta timeout_;
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_client.h b/third_party/blink/renderer/core/loader/threadable_loader_client.h
index 9eac59e..8701604 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_client.h
+++ b/third_party/blink/renderer/core/loader/threadable_loader_client.h
@@ -46,7 +46,7 @@
 class ResourceResponse;
 class ResourceTimingInfo;
 
-class CORE_EXPORT ThreadableLoaderClient {
+class CORE_EXPORT ThreadableLoaderClient : public GarbageCollectedMixin {
  public:
   virtual void DidSendData(unsigned long long /*bytesSent*/,
                            unsigned long long /*totalBytesToBeSent*/) {}
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_test.cc b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
index 464acf9..7a589ac6 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
@@ -53,12 +53,13 @@
 
 constexpr char kFileName[] = "fox-null-terminated.html";
 
-class MockThreadableLoaderClient : public ThreadableLoaderClient {
+class MockThreadableLoaderClient final
+    : public GarbageCollectedFinalized<MockThreadableLoaderClient>,
+      public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(MockThreadableLoaderClient);
+
  public:
-  static std::unique_ptr<MockThreadableLoaderClient> Create() {
-    return base::WrapUnique(
-        new testing::StrictMock<MockThreadableLoaderClient>);
-  }
+  MockThreadableLoaderClient() = default;
   MOCK_METHOD2(DidSendData, void(unsigned long long, unsigned long long));
   MOCK_METHOD3(DidReceiveResponseMock,
                void(unsigned long,
@@ -77,9 +78,6 @@
   MOCK_METHOD0(DidFailRedirectCheck, void());
   MOCK_METHOD1(DidReceiveResourceTiming, void(const ResourceTimingInfo&));
   MOCK_METHOD1(DidDownloadData, void(int));
-
- protected:
-  MockThreadableLoaderClient() = default;
 };
 
 bool IsCancellation(const ResourceError& error) {
@@ -242,19 +240,21 @@
 
   void CreateLoader() { helper_->CreateLoader(Client()); }
 
-  MockThreadableLoaderClient* Client() const { return client_.get(); }
+  MockThreadableLoaderClient* Client() const { return client_.Get(); }
 
  private:
   void SetUp() override {
-    client_ = MockThreadableLoaderClient::Create();
+    client_ = MakeGarbageCollected<MockThreadableLoaderClient>();
     helper_->OnSetUp();
   }
 
   void TearDown() override {
     helper_->OnTearDown();
-    client_.reset();
+    client_ = nullptr;
+    // We need GC here to avoid gmock flakiness.
+    ThreadState::Current()->CollectAllGarbage();
   }
-  std::unique_ptr<MockThreadableLoaderClient> client_;
+  Persistent<MockThreadableLoaderClient> client_;
   std::unique_ptr<ThreadableLoaderTestHelper> helper_;
 };
 
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index 4b95c673..52eb2495 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -39,23 +39,25 @@
 WorkerFetchContext* WorkerFetchContext::Create(
     WorkerOrWorkletGlobalScope& global_scope,
     scoped_refptr<WebWorkerFetchContext> web_context,
-    SubresourceFilter* subresource_filter) {
+    SubresourceFilter* subresource_filter,
+    FetchClientSettingsObject* fetch_client_settings_object) {
   if (!web_context)
     return nullptr;
   return new WorkerFetchContext(global_scope, std::move(web_context),
-                                subresource_filter);
+                                subresource_filter,
+                                fetch_client_settings_object);
 }
 
 WorkerFetchContext::WorkerFetchContext(
     WorkerOrWorkletGlobalScope& global_scope,
     scoped_refptr<WebWorkerFetchContext> web_context,
-    SubresourceFilter* subresource_filter)
+    SubresourceFilter* subresource_filter,
+    FetchClientSettingsObject* fetch_client_settings_object)
     : BaseFetchContext(global_scope.GetTaskRunner(TaskType::kInternalLoading)),
       global_scope_(global_scope),
       web_context_(std::move(web_context)),
       subresource_filter_(subresource_filter),
-      fetch_client_settings_object_(
-          new FetchClientSettingsObjectImpl(*global_scope_)),
+      fetch_client_settings_object_(fetch_client_settings_object),
       save_data_enabled_(GetNetworkStateNotifier().SaveDataEnabled()) {
   DCHECK(global_scope.IsContextThread());
   DCHECK(web_context_);
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.h b/third_party/blink/renderer/core/loader/worker_fetch_context.h
index 5e9b1546..70c9027 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.h
@@ -30,7 +30,8 @@
  public:
   static WorkerFetchContext* Create(WorkerOrWorkletGlobalScope&,
                                     scoped_refptr<WebWorkerFetchContext>,
-                                    SubresourceFilter*);
+                                    SubresourceFilter*,
+                                    FetchClientSettingsObject*);
   ~WorkerFetchContext() override;
 
   // BaseFetchContext implementation:
@@ -128,7 +129,8 @@
  private:
   WorkerFetchContext(WorkerOrWorkletGlobalScope&,
                      scoped_refptr<WebWorkerFetchContext>,
-                     SubresourceFilter*);
+                     SubresourceFilter*,
+                     FetchClientSettingsObject*);
 
   void SetFirstPartyCookie(ResourceRequest&);
 
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.cc b/third_party/blink/renderer/core/page/spatial_navigation.cc
index c7bdcd4..2dccf54 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation.cc
@@ -47,7 +47,6 @@
 namespace blink {
 
 static void DeflateIfOverlapped(LayoutRect&, LayoutRect&);
-static bool IsScrollableNode(const Node*);
 
 FocusCandidate::FocusCandidate(Node* node, WebFocusType direction)
     : visible_node(nullptr),
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.h b/third_party/blink/renderer/core/page/spatial_navigation.h
index 9838a14..05d9b3f 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation.h
@@ -89,6 +89,7 @@
 bool ScrollInDirection(LocalFrame*, WebFocusType);
 bool ScrollInDirection(Node* container, WebFocusType);
 bool IsNavigableContainer(const Node*, WebFocusType);
+CORE_EXPORT bool IsScrollableNode(const Node* node);
 CORE_EXPORT bool IsScrollableAreaOrDocument(const Node*);
 CORE_EXPORT Node* ScrollableAreaOrDocumentOf(Node*);
 bool CanScrollInDirection(const Node* container, WebFocusType);
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
index 9ec62bb..006ef23 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
@@ -243,6 +243,7 @@
   visitor->Trace(threadable_loader_);
   visitor->Trace(content_security_policy_);
   visitor->Trace(execution_context_);
+  ThreadableLoaderClient::Trace(visitor);
 }
 
 void WorkerClassicScriptLoader::Cancel() {
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.h b/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
index 2ac345e..5581031 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
@@ -55,11 +55,7 @@
 class CORE_EXPORT WorkerClassicScriptLoader final
     : public GarbageCollectedFinalized<WorkerClassicScriptLoader>,
       public ThreadableLoaderClient {
-  // We have to cancel |threadable_loader_| before destruction. Otherwise
-  // DidFail() of the deleted |this| will be called from
-  // ThreadableLoader::NotifyFinished() when the associated context will be
-  // destroyed.
-  USING_PRE_FINALIZER(WorkerClassicScriptLoader, Cancel);
+  USING_GARBAGE_COLLECTED_MIXIN(WorkerClassicScriptLoader);
 
  public:
   WorkerClassicScriptLoader();
@@ -122,7 +118,7 @@
   void DidFail(const ResourceError&) override;
   void DidFailRedirectCheck() override;
 
-  virtual void Trace(Visitor*);
+  void Trace(Visitor*) override;
 
  private:
   void NotifyError();
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index e0c2bef..9d81f12 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/loader/subresource_filter.h"
 #include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
+#include "third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
@@ -117,23 +118,37 @@
 
 ResourceFetcher* WorkerOrWorkletGlobalScope::EnsureFetcher() {
   DCHECK(IsContextThread());
-  if (resource_fetcher_)
-    return resource_fetcher_;
+  if (inside_settings_resource_fetcher_)
+    return inside_settings_resource_fetcher_;
+  inside_settings_resource_fetcher_ =
+      CreateFetcherInternal(new FetchClientSettingsObjectImpl(*this));
+  return inside_settings_resource_fetcher_;
+}
 
+ResourceFetcher* WorkerOrWorkletGlobalScope::CreateFetcherInternal(
+    FetchClientSettingsObject* fetch_client_settings_object) {
+  DCHECK(IsContextThread());
   InitializeWebFetchContextIfNeeded();
   WorkerFetchContext* fetch_context = WorkerFetchContext::Create(
-      *this, web_worker_fetch_context_, subresource_filter_);
-  resource_fetcher_ = ResourceFetcher::Create(fetch_context);
+      *this, web_worker_fetch_context_, subresource_filter_,
+      fetch_client_settings_object);
+  ResourceFetcher* resource_fetcher = ResourceFetcher::Create(fetch_context);
   if (IsContextPaused())
-    resource_fetcher_->SetDefersLoading(true);
-  DCHECK(resource_fetcher_);
-  return resource_fetcher_;
+    resource_fetcher->SetDefersLoading(true);
+  resource_fetchers_.insert(resource_fetcher);
+  return resource_fetcher;
 }
 
 ResourceFetcher* WorkerOrWorkletGlobalScope::Fetcher() const {
   DCHECK(IsContextThread());
-  DCHECK(resource_fetcher_);
-  return resource_fetcher_;
+  DCHECK(inside_settings_resource_fetcher_);
+  return inside_settings_resource_fetcher_;
+}
+
+ResourceFetcher* WorkerOrWorkletGlobalScope::CreateOutsideSettingsFetcher(
+    FetchClientSettingsObject* fetch_client_settings_object) {
+  DCHECK(IsContextThread());
+  return CreateFetcherInternal(fetch_client_settings_object);
 }
 
 bool WorkerOrWorkletGlobalScope::IsJSExecutionForbidden() const {
@@ -157,9 +172,9 @@
   script_controller_->Dispose();
   script_controller_.Clear();
 
-  if (resource_fetcher_) {
-    resource_fetcher_->StopFetching();
-    resource_fetcher_->ClearContext();
+  for (ResourceFetcher* resource_fetcher : resource_fetchers_) {
+    resource_fetcher->StopFetching();
+    resource_fetcher->ClearContext();
   }
 }
 
@@ -227,18 +242,19 @@
 
 void WorkerOrWorkletGlobalScope::TasksWerePaused() {
   ExecutionContext::TasksWerePaused();
-  if (resource_fetcher_)
-    resource_fetcher_->SetDefersLoading(true);
+  for (ResourceFetcher* resource_fetcher : resource_fetchers_)
+    resource_fetcher->SetDefersLoading(true);
 }
 
 void WorkerOrWorkletGlobalScope::TasksWereUnpaused() {
   ExecutionContext::TasksWereUnpaused();
-  if (resource_fetcher_)
-    resource_fetcher_->SetDefersLoading(false);
+  for (ResourceFetcher* resource_fetcher : resource_fetchers_)
+    resource_fetcher->SetDefersLoading(false);
 }
 
 void WorkerOrWorkletGlobalScope::Trace(blink::Visitor* visitor) {
-  visitor->Trace(resource_fetcher_);
+  visitor->Trace(inside_settings_resource_fetcher_);
+  visitor->Trace(resource_fetchers_);
   visitor->Trace(subresource_filter_);
   visitor->Trace(script_controller_);
   visitor->Trace(modulator_);
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index 2820a06..83c2687 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -23,6 +23,7 @@
 
 namespace blink {
 
+class FetchClientSettingsObject;
 class FetchClientSettingsObjectSnapshot;
 class Modulator;
 class ModuleTreeClient;
@@ -93,6 +94,17 @@
   ResourceFetcher* Fetcher() const override;
   ResourceFetcher* EnsureFetcher();
 
+  // ResourceFetcher for off-the-main-thread worker top-level script fetching,
+  // corresponding to "outside" fetch client's settings object.
+  // CreateOutsideSettingsFetcher() is called for each invocation of top-level
+  // script fetch, which can occur multiple times in worklets.
+  // TODO(hiroshige, nhiroki): Currently this outside ResourceFetcher and its
+  // WorkerFetchContext is mostly the copy of the insideSettings
+  // ResourceFetcher, and have dependencies to WorkerOrWorkletGlobalScope. Plumb
+  // more data to the outside ResourceFetcher to fix the behavior and reduce the
+  // dependencies.
+  ResourceFetcher* CreateOutsideSettingsFetcher(FetchClientSettingsObject*);
+
   WorkerClients* Clients() const { return worker_clients_.Get(); }
 
   WorkerOrWorkletScriptController* ScriptController() {
@@ -124,16 +136,23 @@
 
  private:
   void InitializeWebFetchContextIfNeeded();
+  ResourceFetcher* CreateFetcherInternal(FetchClientSettingsObject*);
 
   bool web_fetch_context_initialized_ = false;
 
   CrossThreadPersistent<WorkerClients> worker_clients_;
 
-  Member<ResourceFetcher> resource_fetcher_;
+  Member<ResourceFetcher> inside_settings_resource_fetcher_;
+
+  // Keeps track of all ResourceFetchers (including
+  // |inside_settings_resource_fetcher_|) for disposing and pausing/unpausing.
+  HeapHashSet<WeakMember<ResourceFetcher>> resource_fetchers_;
 
   // A WorkerOrWorkletGlobalScope has one WebWorkerFetchContext and one
   // corresponding SubresourceFilter, which are shared by all
-  // WorkerFetchContexts of |this| global scope after crbug.com/880027.
+  // WorkerFetchContexts of |this| global scope, i.e. those behind
+  // ResourceFetchers created by EnsureFetcher() and
+  // CreateOutsideSettingsFetcher().
   // As all references to |web_worker_fetch_context_| are on the context
   // thread, |web_worker_fetch_context_| is destructed on the context
   // thread.
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 92c1b5c38..c2532299 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -2014,6 +2014,7 @@
   visitor->Trace(blob_loader_);
   visitor->Trace(response_text_);
   XMLHttpRequestEventTarget::Trace(visitor);
+  ThreadableLoaderClient::Trace(visitor);
   DocumentParserClient::Trace(visitor);
   PausableObject::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
index 90ce666e..1b88077 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
@@ -77,12 +77,6 @@
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(XMLHttpRequest);
 
-  // In some cases hasPendingActivity doesn't work correctly, i.e.,
-  // doesn't keep |this| alive. We need to cancel the loader in such cases,
-  // which is why we need this pre-finalizer.
-  // TODO(yhirano): Remove this pre-finalizer when the bug is fixed.
-  USING_PRE_FINALIZER(XMLHttpRequest, Dispose);
-
  public:
   static XMLHttpRequest* Create(ScriptState*);
   static XMLHttpRequest* Create(ExecutionContext*);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
index 8a6ee5ba..77005a6 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.h
@@ -23,6 +23,8 @@
 class MODULES_EXPORT BackgroundFetchIconLoader final
     : public GarbageCollectedFinalized<BackgroundFetchIconLoader>,
       public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(BackgroundFetchIconLoader);
+
  public:
   // The bitmap may be empty if the request failed or the image data could not
   // be decoded. The int64_t returned is the scale of the ideal to chosen icon,
@@ -50,9 +52,10 @@
   void DidFail(const ResourceError& error) override;
   void DidFailRedirectCheck() override;
 
-  void Trace(blink::Visitor* visitor) {
+  void Trace(blink::Visitor* visitor) override {
     visitor->Trace(threadable_loader_);
     visitor->Trace(icons_);
+    ThreadableLoaderClient::Trace(visitor);
   }
 
  private:
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 94cd090..750c1f20 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -359,6 +359,7 @@
   visitor->Trace(parser_);
   visitor->Trace(loader_);
   EventTargetWithInlineData::Trace(visitor);
+  ThreadableLoaderClient::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   EventSourceParser::Client::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/modules/notifications/notification_image_loader.h b/third_party/blink/renderer/modules/notifications/notification_image_loader.h
index 7b40f7d..1971cb6 100644
--- a/third_party/blink/renderer/modules/notifications/notification_image_loader.h
+++ b/third_party/blink/renderer/modules/notifications/notification_image_loader.h
@@ -27,6 +27,8 @@
 class MODULES_EXPORT NotificationImageLoader final
     : public GarbageCollectedFinalized<NotificationImageLoader>,
       public ThreadableLoaderClient {
+  USING_GARBAGE_COLLECTED_MIXIN(NotificationImageLoader);
+
  public:
   // Type names are used in UMAs, so do not rename.
   enum class Type { kImage, kIcon, kBadge, kActionIcon };
@@ -59,7 +61,10 @@
   void DidFail(const ResourceError& error) override;
   void DidFailRedirectCheck() override;
 
-  void Trace(blink::Visitor* visitor) { visitor->Trace(threadable_loader_); }
+  void Trace(blink::Visitor* visitor) override {
+    visitor->Trace(threadable_loader_);
+    ThreadableLoaderClient::Trace(visitor);
+  }
 
  private:
   void RunCallbackWithEmptyBitmap();
diff --git a/third_party/blink/renderer/modules/peerconnection/OWNERS b/third_party/blink/renderer/modules/peerconnection/OWNERS
index 076109be..347366a 100644
--- a/third_party/blink/renderer/modules/peerconnection/OWNERS
+++ b/third_party/blink/renderer/modules/peerconnection/OWNERS
@@ -1,6 +1,7 @@
 guidou@chromium.org
 hbos@chromium.org
 orphis@chromium.org
+steveanton@chromium.org
 tommi@chromium.org
 
 # COMPONENT: Blink>WebRTC
diff --git a/third_party/blink/renderer/modules/webusb/usb.cc b/third_party/blink/renderer/modules/webusb/usb.cc
index 647b920..d41521b2 100644
--- a/third_party/blink/renderer/modules/webusb/usb.cc
+++ b/third_party/blink/renderer/modules/webusb/usb.cc
@@ -110,11 +110,19 @@
         script_state,
         DOMException::Create(DOMExceptionCode::kNotSupportedError));
   }
-  if (!IsFeatureEnabled()) {
+
+  FeatureEnabledState state = GetFeatureEnabledState();
+  if (state != FeatureEnabledState::kEnabled) {
     ExecutionContext* execution_context = ExecutionContext::From(script_state);
     if (auto* document = DynamicTo<Document>(execution_context)) {
-      document->ReportFeaturePolicyViolation(mojom::FeaturePolicyFeature::kUsb);
+      document->ReportFeaturePolicyViolation(
+          mojom::FeaturePolicyFeature::kUsb,
+          (state == FeatureEnabledState::kReportOnly
+               ? mojom::FeaturePolicyDisposition::kReport
+               : mojom::FeaturePolicyDisposition::kEnforce));
     }
+  }
+  if (state == FeatureEnabledState::kDisabled) {
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(DOMExceptionCode::kSecurityError,
                                            kFeaturePolicyBlocked));
@@ -271,7 +279,8 @@
     return;
   }
 
-  if (!IsContextSupported() || !IsFeatureEnabled())
+  if (!IsContextSupported() ||
+      GetFeatureEnabledState() == FeatureEnabledState::kDisabled)
     return;
 
   EnsureServiceConnection();
@@ -282,7 +291,7 @@
     return;
 
   DCHECK(IsContextSupported());
-  DCHECK(IsFeatureEnabled());
+  DCHECK(GetFeatureEnabledState() != FeatureEnabledState::kDisabled);
   GetExecutionContext()->GetInterfaceProvider()->GetInterface(
       mojo::MakeRequest(&service_));
   service_.set_connection_error_handler(
@@ -311,8 +320,8 @@
   return true;
 }
 
-bool USB::IsFeatureEnabled() const {
-  return GetExecutionContext()->GetSecurityContext().IsFeatureEnabled(
+FeatureEnabledState USB::GetFeatureEnabledState() const {
+  return GetExecutionContext()->GetSecurityContext().GetFeatureEnabledState(
       mojom::FeaturePolicyFeature::kUsb);
 }
 
diff --git a/third_party/blink/renderer/modules/webusb/usb.h b/third_party/blink/renderer/modules/webusb/usb.h
index 8a37d0c..b19b102 100644
--- a/third_party/blink/renderer/modules/webusb/usb.h
+++ b/third_party/blink/renderer/modules/webusb/usb.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
@@ -79,7 +80,7 @@
   void EnsureServiceConnection();
 
   bool IsContextSupported() const;
-  bool IsFeatureEnabled() const;
+  FeatureEnabledState GetFeatureEnabledState() const;
 
   mojom::blink::WebUsbServicePtr service_;
   HeapHashSet<Member<ScriptPromiseResolver>> get_devices_requests_;
diff --git a/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc b/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
index 4c248ff..4bbe43d 100644
--- a/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
+++ b/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
@@ -188,9 +188,9 @@
 }
 #endif
 
-const char* const RuntimeCallStatsScopedTracer::s_category_group_ =
+constexpr const char* RuntimeCallStatsScopedTracer::s_category_group_ =
     TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats");
-const char* const RuntimeCallStatsScopedTracer::s_name_ =
+constexpr const char* RuntimeCallStatsScopedTracer::s_name_ =
     "BlinkRuntimeCallStats";
 
 void RuntimeCallStatsScopedTracer::AddBeginTraceEventIfEnabled(
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 604e85ab..0dccfd51 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -742,8 +742,9 @@
   // stale while revalidate (based on a list of hosts). This allows us
   // to only log metrics for sites that would possibly benefit from
   // stale while revalidate being enabled.
-  resource_request.SetStaleRevalidateCandidate(host_matches_allow ||
-                                               host_matches_control);
+  resource_request.SetStaleRevalidateCandidate(
+      (host_matches_allow || host_matches_control) &&
+      !params.IsStaleRevalidation());
   // Indicate whether the network stack can return a stale resource. If a
   // stale resource is returned a StaleRevalidation request will be scheduled.
   // Explicitly disallow stale responses for fetchers that don't have SWR
diff --git a/third_party/blink/renderer/platform/network/network_instrumentation.cc b/third_party/blink/renderer/platform/network/network_instrumentation.cc
index 1c0e4a2..cf4f6a2 100644
--- a/third_party/blink/renderer/platform/network/network_instrumentation.cc
+++ b/third_party/blink/renderer/platform/network/network_instrumentation.cc
@@ -18,7 +18,8 @@
 const char kBlinkResourceID[] = "BlinkResourceID";
 const char kResourceLoadTitle[] = "ResourceLoad";
 const char kResourcePrioritySetTitle[] = "ResourcePrioritySet";
-const char kNetInstrumentationCategory[] = TRACE_DISABLED_BY_DEFAULT("network");
+constexpr const char kNetInstrumentationCategory[] =
+    TRACE_DISABLED_BY_DEFAULT("network");
 
 const char* RequestOutcomeToString(RequestOutcome outcome) {
   switch (outcome) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index aed08a2b..a22b9de 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -632,6 +632,10 @@
       status: "experimental",
     },
     {
+      name: "KeyboardFocusableScrollers",
+      status: "stable",
+    },
+    {
       name: "LangAttributeAwareFormControlUI",
     },
     {
@@ -904,7 +908,7 @@
     // Compute touch action rects in paint instead of ScrollingCoordinator.
     {
       name: "PaintTouchActionRects",
-      status: "test",
+      status: "stable",
     },
     // PaintTracking enables the Largest Text Paint metric, Last Text Paint
     // metric, Largest Image Paint metric and Last Image Paint metric.
diff --git a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
index 8a8c1dc..a1e8fc41 100644
--- a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
@@ -185,7 +185,7 @@
       // It's okay to use |test_task_runner_| just as a mock clock because
       // it isn't bound to thread and all tasks will go through a MessageLoop.
       sequence_manager = base::sequence_manager::SequenceManagerForTest::Create(
-          message_loop_.get(), message_loop_->task_runner(),
+          message_loop_->GetMessageLoopBase(), message_loop_->task_runner(),
           test_task_runner_->GetMockTickClock());
     }
     sequence_manager_ = sequence_manager.get();
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
index 6a428d5..8e297487 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -155,7 +155,11 @@
 }
 
 void SchedulerHelper::SetTimerSlack(base::TimerSlack timer_slack) {
-  sequence_manager_->SetTimerSlack(timer_slack);
+  if (sequence_manager_) {
+    static_cast<base::sequence_manager::internal::SequenceManagerImpl*>(
+        sequence_manager_.get())
+        ->SetTimerSlack(timer_slack);
+  }
 }
 
 double SchedulerHelper::GetSamplingRateForRecordingCPUTime() const {
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h b/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h
index 0d0fea4e9..586fe5f 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h
@@ -115,7 +115,7 @@
   // See CPUTimeBudgetPool::SetMinBudgetLevelToRun.
   base::TimeDelta min_budget_level_to_run_;
 
-  TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+  TraceableCounter<base::TimeDelta, TracingCategoryName::kInfo>
       current_budget_level_;
   base::TimeTicks last_checkpoint_;
   double cpu_percentage_;
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.cc b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.cc
index 46a18f9..277b1ca 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.cc
@@ -10,12 +10,10 @@
 namespace blink {
 namespace scheduler {
 
-const char kTracingCategoryNameTopLevel[] = "toplevel";
-const char kTracingCategoryNameDefault[] = "renderer.scheduler";
-const char kTracingCategoryNameInfo[] =
-    TRACE_DISABLED_BY_DEFAULT("renderer.scheduler");
-const char kTracingCategoryNameDebug[] =
-    TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug");
+constexpr const char TracingCategoryName::kTopLevel[];
+constexpr const char TracingCategoryName::kDefault[];
+constexpr const char TracingCategoryName::kInfo[];
+constexpr const char TracingCategoryName::kDebug[];
 
 namespace internal {
 
@@ -24,19 +22,19 @@
   // portable way to use string literals as a template argument.
   // Unfortunately, static_assert won't work with templates either because
   // inequality (!=) of linker symbols is undefined in compile-time.
-  DCHECK(category == kTracingCategoryNameTopLevel ||
-         category == kTracingCategoryNameDefault ||
-         category == kTracingCategoryNameInfo ||
-         category == kTracingCategoryNameDebug);
+  DCHECK(category == TracingCategoryName::kTopLevel ||
+         category == TracingCategoryName::kDefault ||
+         category == TracingCategoryName::kInfo ||
+         category == TracingCategoryName::kDebug);
 }
 
 }  // namespace internal
 
 void WarmupTracingCategories() {
   // No need to warm-up toplevel category here.
-  TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameDefault);
-  TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameInfo);
-  TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameDebug);
+  TRACE_EVENT_WARMUP_CATEGORY(TracingCategoryName::kDefault);
+  TRACE_EVENT_WARMUP_CATEGORY(TracingCategoryName::kInfo);
+  TRACE_EVENT_WARMUP_CATEGORY(TracingCategoryName::kDebug);
 }
 
 std::string PointerToString(const void* pointer) {
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
index c28776d..eb5ceb5f 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
@@ -21,10 +21,16 @@
 // DISCLAIMER
 // Using these constants in TRACE_EVENTs is discouraged nor should you pass any
 // non-literal string as a category, unless familiar with tracing internals.
-PLATFORM_EXPORT extern const char kTracingCategoryNameTopLevel[];
-PLATFORM_EXPORT extern const char kTracingCategoryNameDefault[];
-PLATFORM_EXPORT extern const char kTracingCategoryNameInfo[];
-PLATFORM_EXPORT extern const char kTracingCategoryNameDebug[];
+// The constants are implemented as static members of a class to have an unique
+// address and not violate ODR.
+struct PLATFORM_EXPORT TracingCategoryName {
+  static constexpr const char kTopLevel[] = "toplevel";
+  static constexpr const char kDefault[] = "renderer.scheduler";
+  static constexpr const char kInfo[] =
+      TRACE_DISABLED_BY_DEFAULT("renderer.scheduler");
+  static constexpr const char kDebug[] =
+      TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug");
+};
 
 namespace internal {
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
index 8d31aae7..332310b 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
@@ -35,7 +35,7 @@
 }
 
 class TraceableStateForTest
-    : public TraceableState<int, kTracingCategoryNameDefault> {
+    : public TraceableState<int, TracingCategoryName::kDefault> {
  public:
   TraceableStateForTest(TraceableVariableController* controller)
       : TraceableState(0, "State", controller, controller, SignOfInt) {
@@ -74,10 +74,10 @@
 
 TEST(TracingHelperTest, TraceableStateOperators) {
   TraceableVariableController controller;
-  TraceableState<int, kTracingCategoryNameDebug> x(-1, "X", &controller,
-                                                   &controller, SignOfInt);
-  TraceableState<int, kTracingCategoryNameDebug> y(1, "Y", &controller,
-                                                   &controller, SignOfInt);
+  TraceableState<int, TracingCategoryName::kDebug> x(-1, "X", &controller,
+                                                     &controller, SignOfInt);
+  TraceableState<int, TracingCategoryName::kDebug> y(1, "Y", &controller,
+                                                     &controller, SignOfInt);
   EXPECT_EQ(0, x + y);
   EXPECT_FALSE(x == y);
   EXPECT_TRUE(x != y);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index 6c8001f..5fce666c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -266,25 +266,27 @@
   FrameScheduler::Delegate* delegate_;              // NOT OWNED
   base::trace_event::BlameContext* blame_context_;  // NOT OWNED
   SchedulingLifecycleState throttling_state_;
-  TraceableState<bool, kTracingCategoryNameInfo> frame_visible_;
-  TraceableState<bool, kTracingCategoryNameInfo> frame_paused_;
-  TraceableState<FrameOriginType, kTracingCategoryNameInfo> frame_origin_type_;
-  TraceableState<bool, kTracingCategoryNameInfo> subresource_loading_paused_;
-  StateTracer<kTracingCategoryNameInfo> url_tracer_;
-  TraceableState<bool, kTracingCategoryNameInfo> task_queues_throttled_;
+  TraceableState<bool, TracingCategoryName::kInfo> frame_visible_;
+  TraceableState<bool, TracingCategoryName::kInfo> frame_paused_;
+  TraceableState<FrameOriginType, TracingCategoryName::kInfo>
+      frame_origin_type_;
+  TraceableState<bool, TracingCategoryName::kInfo> subresource_loading_paused_;
+  StateTracer<TracingCategoryName::kInfo> url_tracer_;
+  TraceableState<bool, TracingCategoryName::kInfo> task_queues_throttled_;
   // TODO(kraynov): https://crbug.com/827113
   // Trace active connection count.
   int active_connection_count_;
   size_t subresource_loading_pause_count_;
-  TraceableState<bool, kTracingCategoryNameInfo> has_active_connection_;
+  TraceableState<bool, TracingCategoryName::kInfo> has_active_connection_;
 
   // These are the states of the Page.
   // They should be accessed via GetPageScheduler()->SetPageState().
   // they are here because we don't support page-level tracing yet.
-  TraceableState<bool, kTracingCategoryNameInfo> page_frozen_for_tracing_;
-  TraceableState<PageVisibilityState, kTracingCategoryNameInfo>
+  TraceableState<bool, TracingCategoryName::kInfo> page_frozen_for_tracing_;
+  TraceableState<PageVisibilityState, TracingCategoryName::kInfo>
       page_visibility_for_tracing_;
-  TraceableState<bool, kTracingCategoryNameInfo> page_keep_active_for_tracing_;
+  TraceableState<bool, TracingCategoryName::kInfo>
+      page_keep_active_for_tracing_;
 
   base::WeakPtrFactory<FrameSchedulerImpl> weak_factory_;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index a437274..d63270a9 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -815,49 +815,51 @@
     TaskCostEstimator loading_task_cost_estimator;
     TaskCostEstimator timer_task_cost_estimator;
     IdleTimeEstimator idle_time_estimator;
-    TraceableState<UseCase, kTracingCategoryNameDefault> current_use_case;
+    TraceableState<UseCase, TracingCategoryName::kDefault> current_use_case;
     Policy current_policy;
     base::TimeTicks current_policy_expiration_time;
     base::TimeTicks estimated_next_frame_begin;
     base::TimeTicks current_task_start_time;
     base::TimeDelta compositor_frame_interval;
-    TraceableCounter<base::TimeDelta, kTracingCategoryNameDebug>
+    TraceableCounter<base::TimeDelta, TracingCategoryName::kDebug>
         longest_jank_free_task_duration;
-    TraceableCounter<int, kTracingCategoryNameInfo>
+    TraceableCounter<int, TracingCategoryName::kInfo>
         renderer_pause_count;  // Renderer is paused if non-zero.
-    TraceableState<ExpensiveTaskPolicy, kTracingCategoryNameInfo>
+    TraceableState<ExpensiveTaskPolicy, TracingCategoryName::kInfo>
         expensive_task_policy;
-    TraceableState<v8::RAILMode, kTracingCategoryNameInfo>
+    TraceableState<v8::RAILMode, TracingCategoryName::kInfo>
         rail_mode_for_tracing;  // Don't use except for tracing.
-    TraceableState<bool, kTracingCategoryNameDebug> renderer_hidden;
-    TraceableState<bool, kTracingCategoryNameTopLevel> renderer_backgrounded;
-    TraceableState<bool, kTracingCategoryNameDefault>
+    TraceableState<bool, TracingCategoryName::kDebug> renderer_hidden;
+    TraceableState<bool, TracingCategoryName::kTopLevel> renderer_backgrounded;
+    TraceableState<bool, TracingCategoryName::kDefault>
         keep_active_fetch_or_worker;
-    TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+    TraceableCounter<base::TimeDelta, TracingCategoryName::kInfo>
         loading_task_estimated_cost;
-    TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+    TraceableCounter<base::TimeDelta, TracingCategoryName::kInfo>
         timer_task_estimated_cost;
-    TraceableState<bool, kTracingCategoryNameInfo> loading_tasks_seem_expensive;
-    TraceableState<bool, kTracingCategoryNameInfo> timer_tasks_seem_expensive;
-    TraceableState<bool, kTracingCategoryNameDefault>
+    TraceableState<bool, TracingCategoryName::kInfo>
+        loading_tasks_seem_expensive;
+    TraceableState<bool, TracingCategoryName::kInfo> timer_tasks_seem_expensive;
+    TraceableState<bool, TracingCategoryName::kDefault>
         blocking_input_expected_soon;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
         have_seen_a_begin_main_frame;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
         have_reported_blocking_intervention_in_current_policy;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
         have_reported_blocking_intervention_since_navigation;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
         has_visible_render_widget_with_touch_handler;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
         begin_frame_not_expected_soon;
-    TraceableState<bool, kTracingCategoryNameDebug> in_idle_period_for_testing;
-    TraceableState<bool, kTracingCategoryNameInfo> use_virtual_time;
-    TraceableState<bool, kTracingCategoryNameTopLevel> is_audio_playing;
-    TraceableState<bool, kTracingCategoryNameDebug>
+    TraceableState<bool, TracingCategoryName::kDebug>
+        in_idle_period_for_testing;
+    TraceableState<bool, TracingCategoryName::kInfo> use_virtual_time;
+    TraceableState<bool, TracingCategoryName::kTopLevel> is_audio_playing;
+    TraceableState<bool, TracingCategoryName::kDebug>
         compositor_will_send_main_frame_not_expected;
-    TraceableState<bool, kTracingCategoryNameDebug> has_navigated;
-    TraceableState<bool, kTracingCategoryNameDebug> pause_timers_for_webview;
+    TraceableState<bool, TracingCategoryName::kDebug> has_navigated;
+    TraceableState<bool, TracingCategoryName::kDebug> pause_timers_for_webview;
     std::unique_ptr<base::SingleSampleMetric> max_queueing_time_metric;
     base::TimeDelta max_queueing_time;
     base::TimeTicks background_status_changed_at;
@@ -866,14 +868,14 @@
         rail_mode_observers;                                      // Not owned.
     WakeUpBudgetPool* wake_up_budget_pool;                        // Not owned.
     MainThreadMetricsHelper metrics_helper;
-    TraceableState<WebRendererProcessType, kTracingCategoryNameTopLevel>
+    TraceableState<WebRendererProcessType, TracingCategoryName::kTopLevel>
         process_type;
     TraceableState<base::Optional<TaskDescriptionForTracing>,
-                   kTracingCategoryNameInfo>
+                   TracingCategoryName::kInfo>
         task_description_for_tracing;  // Don't use except for tracing.
     TraceableState<
         base::Optional<base::sequence_manager::TaskQueue::QueuePriority>,
-        kTracingCategoryNameInfo>
+        TracingCategoryName::kInfo>
         task_priority_for_tracing;  // Only used for tracing.
     base::ObserverList<VirtualTimeObserver>::Unchecked virtual_time_observers;
     base::Time initial_virtual_time;
@@ -917,17 +919,19 @@
     base::TimeTicks last_idle_period_end_time;
     base::TimeTicks fling_compositor_escalation_deadline;
     UserModel user_model;
-    TraceableState<bool, kTracingCategoryNameInfo>
+    TraceableState<bool, TracingCategoryName::kInfo>
         awaiting_touch_start_response;
-    TraceableState<bool, kTracingCategoryNameInfo> in_idle_period;
-    TraceableState<bool, kTracingCategoryNameInfo>
+    TraceableState<bool, TracingCategoryName::kInfo> in_idle_period;
+    TraceableState<bool, TracingCategoryName::kInfo>
         begin_main_frame_on_critical_path;
-    TraceableState<bool, kTracingCategoryNameInfo>
+    TraceableState<bool, TracingCategoryName::kInfo>
         last_gesture_was_compositor_driven;
-    TraceableState<bool, kTracingCategoryNameInfo> default_gesture_prevented;
-    TraceableState<bool, kTracingCategoryNameInfo> have_seen_a_blocking_gesture;
-    TraceableState<bool, kTracingCategoryNameInfo> waiting_for_meaningful_paint;
-    TraceableState<bool, kTracingCategoryNameInfo>
+    TraceableState<bool, TracingCategoryName::kInfo> default_gesture_prevented;
+    TraceableState<bool, TracingCategoryName::kInfo>
+        have_seen_a_blocking_gesture;
+    TraceableState<bool, TracingCategoryName::kInfo>
+        waiting_for_meaningful_paint;
+    TraceableState<bool, TracingCategoryName::kInfo>
         have_seen_input_since_navigation;
   };
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index f96edccc..4af2aa2 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -2258,7 +2258,8 @@
     clock_.Advance(base::TimeDelta::FromMilliseconds(5));
     Initialize(std::make_unique<MainThreadSchedulerImplForTest>(
         base::sequence_manager::SequenceManagerForTest::Create(
-            message_loop_.get(), message_loop_->task_runner(), &clock_),
+            message_loop_->GetMessageLoopBase(), message_loop_->task_runner(),
+            &clock_),
         base::nullopt));
   }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
index 4585e1f..5bacaecd 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_unittest.cc
@@ -48,7 +48,8 @@
     clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
     scheduler_.reset(new MainThreadSchedulerImpl(
         base::sequence_manager::SequenceManagerForTest::Create(
-            &message_loop_, message_loop_.task_runner(), &clock_),
+            message_loop_.GetMessageLoopBase(), message_loop_.task_runner(),
+            &clock_),
         base::nullopt));
     scheduler_overrider_ =
         std::make_unique<ScopedSchedulerOverrider>(scheduler_.get());
diff --git a/third_party/closure_compiler/externs/activity_log_private.js b/third_party/closure_compiler/externs/activity_log_private.js
deleted file mode 100644
index 27299bd..0000000
--- a/third_party/closure_compiler/externs/activity_log_private.js
+++ /dev/null
@@ -1,133 +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.
-
-// This file was generated by:
-//   tools/json_schema_compiler/compiler.py.
-// NOTE: The format of types has changed. 'FooType' is now
-//   'chrome.activityLogPrivate.FooType'.
-// Please run the closure compiler before committing changes.
-// See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
-
-/** @fileoverview Externs generated from namespace: activityLogPrivate */
-
-/**
- * @const
- */
-chrome.activityLogPrivate = {};
-
-/**
- * @enum {string}
- */
-chrome.activityLogPrivate.ExtensionActivityType = {
-  API_CALL: 'api_call',
-  API_EVENT: 'api_event',
-  CONTENT_SCRIPT: 'content_script',
-  DOM_ACCESS: 'dom_access',
-  DOM_EVENT: 'dom_event',
-  WEB_REQUEST: 'web_request',
-};
-
-/**
- * @enum {string}
- */
-chrome.activityLogPrivate.ExtensionActivityFilter = {
-  API_CALL: 'api_call',
-  API_EVENT: 'api_event',
-  CONTENT_SCRIPT: 'content_script',
-  DOM_ACCESS: 'dom_access',
-  DOM_EVENT: 'dom_event',
-  WEB_REQUEST: 'web_request',
-  ANY: 'any',
-};
-
-/**
- * @enum {string}
- */
-chrome.activityLogPrivate.ExtensionActivityDomVerb = {
-  GETTER: 'getter',
-  SETTER: 'setter',
-  METHOD: 'method',
-  INSERTED: 'inserted',
-  XHR: 'xhr',
-  WEBREQUEST: 'webrequest',
-  MODIFIED: 'modified',
-};
-
-/**
- * This corresponds to a row from the ActivityLog database. Fields will be blank if they were specified precisely in a lookup filter.
- * @typedef {{
- *   activityId: (string|undefined),
- *   extensionId: (string|undefined),
- *   activityType: !chrome.activityLogPrivate.ExtensionActivityType,
- *   time: (number|undefined),
- *   apiCall: (string|undefined),
- *   args: (string|undefined),
- *   count: (number|undefined),
- *   pageUrl: (string|undefined),
- *   pageTitle: (string|undefined),
- *   argUrl: (string|undefined),
- *   other: ({
- *     prerender: (boolean|undefined),
- *     domVerb: (!chrome.activityLogPrivate.ExtensionActivityDomVerb|undefined),
- *     webRequest: (string|undefined),
- *     extra: (string|undefined)
- *   }|undefined)
- * }}
- */
-chrome.activityLogPrivate.ExtensionActivity;
-
-/**
- * Used to specify values for a lookup.
- * @typedef {{
- *   extensionId: (string|undefined),
- *   activityType: !chrome.activityLogPrivate.ExtensionActivityFilter,
- *   apiCall: (string|undefined),
- *   pageUrl: (string|undefined),
- *   argUrl: (string|undefined),
- *   daysAgo: (number|undefined)
- * }}
- */
-chrome.activityLogPrivate.Filter;
-
-/**
- * This holds the results of a lookup, the filter of the lookup, the time of the lookup, and whether there are more results that match.
- * @typedef {{
- *   activities: !Array<!chrome.activityLogPrivate.ExtensionActivity>
- * }}
- */
-chrome.activityLogPrivate.ActivityResultSet;
-
-/**
- * Retrieves activity from the ActivityLog that matches the specified filter.
- * @param {!chrome.activityLogPrivate.Filter} filter Fill out the fields that
- *     you want to search for in the database.
- * @param {function(!chrome.activityLogPrivate.ActivityResultSet):void} callback
- */
-chrome.activityLogPrivate.getExtensionActivities = function(filter, callback) {};
-
-/**
- * Deletes activities in the ActivityLog database specified in the array of
- * activity IDs.
- * @param {!Array<string>} activityIds Erases only the activities which IDs are
- *     listed in the array.
- */
-chrome.activityLogPrivate.deleteActivities = function(activityIds) {};
-
-/**
- * Deletes the entire ActivityLog database.
- */
-chrome.activityLogPrivate.deleteDatabase = function() {};
-
-/**
- * Delete URLs in the ActivityLog database.
- * @param {!Array<string>=} urls Erases only the URLs listed; if empty, erases
- *     all URLs.
- */
-chrome.activityLogPrivate.deleteUrls = function(urls) {};
-
-/**
- * Fired when a given extension performs another activity.
- * @type {!ChromeEvent}
- */
-chrome.activityLogPrivate.onExtensionActivity;
diff --git a/third_party/googletest/BUILD.gn b/third_party/googletest/BUILD.gn
index 0db3b2f..6a4b3328 100644
--- a/third_party/googletest/BUILD.gn
+++ b/third_party/googletest/BUILD.gn
@@ -129,7 +129,10 @@
   }
 
   if (is_fuchsia) {
-    deps += [ "//third_party/fuchsia-sdk/sdk:fdio" ]
+    deps += [
+      "//third_party/fuchsia-sdk/sdk:fdio",
+      "//third_party/fuchsia-sdk/sdk:zx",
+    ]
   }
 }
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bc9bc17..0c48007 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2061,6 +2061,15 @@
   <int value="4" label="FAILED_HOSTS">Failed during watching HOSTS.</int>
 </enum>
 
+<enum name="AsyncHitTestReasons">
+  <int value="0" label="Not async hit test"/>
+  <int value="1" label="Iframe has overlapped region"/>
+  <int value="2" label="Iframe is irregularly clipped"/>
+  <int value="3" label="Iframe is not active"/>
+  <int value="4" label="Iframe has perspective transform"/>
+  <int value="5" label="Hit test data submitted by viz hit test draw quad"/>
+</enum>
+
 <enum name="AsyncRevalidationResult">
   <int value="0" label="LOADED">A new entry was stored in the cache.</int>
   <int value="1" label="REVALIDATED">
@@ -20754,6 +20763,7 @@
   <int value="2626" label="CSSUnknownNamespacePrefixInSelector"/>
   <int value="2627" label="PageLifeCycleFreeze"/>
   <int value="2628" label="DefaultInCustomIdent"/>
+  <int value="2629" label="HTMLAnchorElementHrefTranslateAttribute"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -30960,6 +30970,7 @@
       label="OmniboxUIExperimentHideSuggestionUrlTrivialSubdomains:disabled"/>
   <int value="1389729816" label="data-reduction-proxy-lo-fi"/>
   <int value="1393500952" label="EnableVirtualKeyboardUkm:disabled"/>
+  <int value="1393722373" label="SaveEditedPDFForm:disabled"/>
   <int value="1397069250" label="NetworkService:disabled"/>
   <int value="1403195370" label="ArcCupsApi:enabled"/>
   <int value="1405459667" label="enable-fast-text-autosizing"/>
@@ -31089,6 +31100,7 @@
   <int value="1639314588" label="LookalikeUrlNavigationSuggestions:disabled"/>
   <int value="1640386037" label="ContextualSuggestionsSlimPeekUI:disabled"/>
   <int value="1646498561" label="OfflineBookmarks:disabled"/>
+  <int value="1651141490" label="SaveEditedPDFForm:enabled"/>
   <int value="1657713458" label="disable-virtual-keyboard-overscroll"/>
   <int value="1658644418" label="disable-app-list-voice-search"/>
   <int value="1659082220" label="EnableManualSaving:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b9acc402..495a3571 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -25,6 +25,18 @@
 
 <histograms>
 
+<histogram name="Accessibility.Android.AnimationsEnabled" enum="BooleanEnabled"
+    expires_after="2019-11-02">
+  <owner>dmazzoni@chromium.org</owner>
+  <owner>smcgruer@chromium.org</owner>
+  <summary>
+    Tracks whether animations are enabled on Android (e.g. if the animator
+    duration scale is non-zero.) The purpose is to inform the design of the
+    prefers-reduced-motion media feature; see http://crbug.com/722548. This is
+    checked once, 45 seconds after startup.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.Android.TabSwitcherPreferenceEnabled"
     enum="BooleanEnabled">
   <owner>twellington@chromium.org</owner>
@@ -339,7 +351,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.Mac.ReduceMotion" enum="BooleanEnabled">
+<histogram name="Accessibility.Mac.ReduceMotion" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>smcgruer@chromium.org</owner>
   <summary>
@@ -29194,6 +29207,16 @@
   </summary>
 </histogram>
 
+<histogram name="Event.VizHitTest.AsyncHitTestReasons"
+    enum="AsyncHitTestReasons">
+  <owner>sunxd@chromium.org</owner>
+  <owner>event-targeting@chromium.org</owner>
+  <summary>
+    Tracks the reasons why sychronous hit testing could not be done for each hit
+    test requests processd by HitTestQuery.
+  </summary>
+</histogram>
+
 <histogram name="Event.VizHitTest.HitTestRegions" units="regions">
   <owner>riajiang@chromium.org</owner>
   <owner>event-targeting@chromium.org</owner>
@@ -34280,6 +34303,23 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.ImportController.DeviceYanked" enum="Boolean"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Whether an external media device was removed during the
+    upload process.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.ImportController.ImportCancelled"
+    enum="BooleanCanceled" expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Whether the media import process was cancelled.
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.Load" units="ms">
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
@@ -34333,6 +34373,59 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.MediaImport.Cancelled" enum="BooleanCanceled"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Whether the media import (Photos/Video) from external
+    media was cancelled.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.MediaImport.Duplicates" units="count"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Count of duplicate media (Photos/Videos) files that
+    were skipped during a single upload session.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.MediaImport.ErrorCount" units="count"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Number of errors that occured during an upload session.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.MediaImport.ImportCount" units="count"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Count of individual media (Photos/Videos) uploaded from
+    a single upload session.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.MediaImport.ImportMB" units="MBytes"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Size of the media (Photos/Videos) uploaded from a
+    single upload session.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.MediaImport.Started" enum="BooleanAttempted"
+    expires_after="M79">
+  <owner>slangley@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Whether the media import (Photos/Video) from external
+    media was started.
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.MenuItemSelected" enum="FileManagerMenuCommands">
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
@@ -34437,6 +34530,9 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoImport.Action" enum="ExternalDeviceAction">
+  <obsolete>
+    Code seems to have been removed at an unknown time.
+  </obsolete>
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <summary>
@@ -34446,6 +34542,9 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoImport.ImportCount">
+  <obsolete>
+    Code seems to have been removed at an unknown time.
+  </obsolete>
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <summary>
@@ -34455,6 +34554,9 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoImport.ImportPercentage" units="%">
+  <obsolete>
+    Code seems to have been removed at an unknown time.
+  </obsolete>
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <summary>
@@ -34464,6 +34566,9 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoImport.Load" units="ms">
+  <obsolete>
+    Code seems to have been removed at an unknown time.
+  </obsolete>
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <summary>
@@ -34474,6 +34579,9 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoImport.Scan" units="ms">
+  <obsolete>
+    Code seems to have been removed at an unknown time.
+  </obsolete>
   <owner>sashab@chromium.org</owner>
   <owner>slangley@chromium.org</owner>
   <summary>
@@ -43067,6 +43175,15 @@
   </summary>
 </histogram>
 
+<histogram name="ManualFallback.PresentedOptions.CreditCards" units="Cards"
+    expires_after="2019-12-31">
+  <owner>javierrobles@chromium.org</owner>
+  <summary>
+    Tracks the number of cards presented to the user in Manual Fallback. This is
+    logged everytime the user opens this view.
+  </summary>
+</histogram>
+
 <histogram name="ManualFallback.PresentedOptions.Passwords" units="Credentials"
     expires_after="2019-12-31">
   <owner>javierrobles@chromium.org</owner>
@@ -43076,6 +43193,15 @@
   </summary>
 </histogram>
 
+<histogram name="ManualFallback.PresentedOptions.Profiles" units="Profiles"
+    expires_after="2019-12-31">
+  <owner>javierrobles@chromium.org</owner>
+  <summary>
+    Tracks the number of profiles presented to the user in Manual Fallback. This
+    is logged everytime the user opens this view.
+  </summary>
+</histogram>
+
 <histogram name="Media.AcceleratedCompositingActive" enum="BooleanSuccess">
   <obsolete>
     Deprecated as of July 21, 2014.
@@ -75841,9 +75967,9 @@
 
 <histogram
     name="PasswordManager.BlacklistedSites.NeedRemoveBlacklistDuplicates"
-    enum="BooleanNeedsDeDuplication" expires_after="M72">
-  <owner>gemene@google.com</owner>
+    enum="BooleanNeedsDeDuplication" expires_after="M75">
   <owner>jdoerrie@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     Records once on startup whether the blacklisted sites in the password store
     need to be cleared of duplications.
@@ -75864,9 +75990,9 @@
 </histogram>
 
 <histogram name="PasswordManager.BlacklistedSites.PreventedAddingDuplicates"
-    enum="BooleanAddingBlacklistedDuplicatesPrevented" expires_after="M72">
-  <owner>gemene@google.com</owner>
+    enum="BooleanAddingBlacklistedDuplicatesPrevented" expires_after="M75">
   <owner>jdoerrie@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     Boolean indicating whether adding a blacklist entry was prevented due to an
     already existing entry. Recorded after every blacklist site submission.
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 33de0af..73afc74 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -86,11 +86,21 @@
   'system_health.memory_desktop/browse:search:google_india',
   'system_health.memory_desktop/load:games:alphabetty',
   'system_health.memory_desktop/load:games:miniclip',
+  'system_health.memory_desktop/load:media:flickr',
+  'system_health.memory_desktop/load:media:google_images',
   'system_health.memory_desktop/load:news:cnn',
   'system_health.memory_desktop/load:news:wikipedia',
+  'system_health.memory_desktop/load:search:baidu',
+  'system_health.memory_desktop/load:search:yahoo',
+  'system_health.memory_desktop/load:search:yandex',
   'system_health.memory_desktop/load:tools:stackoverflow',
   # MOBILE:
+  'system_health.memory_mobile/load:media:flickr',
+  'system_health.memory_mobile/load:media:google_images',
   'system_health.memory_mobile/load:news:wikipedia',
+  'system_health.memory_mobile/load:search:baidu',
+  'system_health.memory_mobile/load:search:yahoo',
+  'system_health.memory_mobile/load:search:yandex',
 
   # crbug.com/698006
   'system_health.memory_desktop/load:tools:drive',
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index 68e90165..ccf2069 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -144,12 +144,15 @@
         "load:media:facebook_photos:2018": {
             "DEFAULT": "system_health_desktop_eba368f102.wprgo"
         },
-        "load:media:flickr": {
-            "DEFAULT": "system_health_desktop_003.wprgo"
+        "load:media:flickr:2018": {
+            "DEFAULT": "system_health_desktop_ae117f4f81.wprgo"
         },
         "load:media:google_images": {
             "DEFAULT": "system_health_desktop_003.wprgo"
         },
+        "load:media:google_images:2018": {
+            "DEFAULT": "system_health_desktop_2fefa84cbf.wprgo"
+        },
         "load:media:imgur": {
             "DEFAULT": "system_health_desktop_025.wprgo"
         },
@@ -216,6 +219,9 @@
         "load:search:baidu": {
             "DEFAULT": "system_health_desktop_000.wprgo"
         },
+        "load:search:baidu:2018": {
+            "DEFAULT": "system_health_desktop_beec2d8329.wprgo"
+        },
         "load:search:ebay": {
             "DEFAULT": "system_health_desktop_000.wprgo"
         },
@@ -237,9 +243,15 @@
         "load:search:yahoo": {
             "DEFAULT": "system_health_desktop_000.wprgo"
         },
+        "load:search:yahoo:2018": {
+            "DEFAULT": "system_health_desktop_d661408019.wprgo"
+        },
         "load:search:yandex": {
             "DEFAULT": "system_health_desktop_000.wprgo"
         },
+        "load:search:yandex:2018": {
+            "DEFAULT": "system_health_desktop_84d11cb3d6.wprgo"
+        },
         "load:social:facebook": {
             "DEFAULT": "system_health_desktop_007.wprgo"
         },
@@ -330,4 +342,4 @@
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
     "platform_specific": true
-}
\ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/system_health_desktop_2fefa84cbf.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_2fefa84cbf.wprgo.sha1
new file mode 100644
index 0000000..2db1ada
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_2fefa84cbf.wprgo.sha1
@@ -0,0 +1 @@
+2fefa84cbf84e98ec24f174563c189273642cb12
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop_84d11cb3d6.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_84d11cb3d6.wprgo.sha1
new file mode 100644
index 0000000..e8106c6
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_84d11cb3d6.wprgo.sha1
@@ -0,0 +1 @@
+33adcbb183973c662eac06505284700928e52c20
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop_ae117f4f81.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_ae117f4f81.wprgo.sha1
new file mode 100644
index 0000000..932aece
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_ae117f4f81.wprgo.sha1
@@ -0,0 +1 @@
+ae117f4f817ff1a3d007b55cb2b29b4ee56ff202
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop_beec2d8329.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_beec2d8329.wprgo.sha1
new file mode 100644
index 0000000..c23bfd8
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_beec2d8329.wprgo.sha1
@@ -0,0 +1 @@
+beec2d8329da0a6f2156ec6bc520956386776efd
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop_d661408019.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_d661408019.wprgo.sha1
new file mode 100644
index 0000000..1d17e4c8
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_d661408019.wprgo.sha1
@@ -0,0 +1 @@
+d661408019c6c2dd2092227335ef484ad6c6f731
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 0ce692b..3f83fe99 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -129,9 +129,15 @@
         "load:media:flickr": {
             "DEFAULT": "system_health_mobile_003.wprgo"
         },
+        "load:media:flickr:2018": {
+            "DEFAULT": "system_health_mobile_3d332e7f0f.wprgo"
+        },
         "load:media:google_images": {
             "DEFAULT": "system_health_mobile_003.wprgo"
         },
+        "load:media:google_images:2018": {
+            "DEFAULT": "system_health_mobile_cac60dab45.wprgo"
+        },
         "load:media:imgur": {
             "DEFAULT": "system_health_mobile_035.wprgo"
         },
@@ -186,6 +192,9 @@
         "load:search:baidu": {
             "DEFAULT": "system_health_mobile_000.wprgo"
         },
+        "load:search:baidu:2018": {
+            "DEFAULT": "system_health_mobile_028870b3b5.wprgo"
+        },
         "load:search:ebay": {
             "DEFAULT": "system_health_mobile_000.wprgo"
         },
@@ -198,9 +207,15 @@
         "load:search:yahoo": {
             "DEFAULT": "system_health_mobile_000.wprgo"
         },
+        "load:search:yahoo:2018": {
+            "DEFAULT": "system_health_mobile_4367fb5828.wprgo"
+        },
         "load:search:yandex": {
             "DEFAULT": "system_health_mobile_000.wprgo"
         },
+        "load:search:yandex:2018": {
+            "DEFAULT": "system_health_mobile_5d042249d1.wprgo"
+        },
         "load:social:facebook": {
             "DEFAULT": "system_health_mobile_028.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_mobile_028870b3b5.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_028870b3b5.wprgo.sha1
new file mode 100644
index 0000000..05cf425e
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_028870b3b5.wprgo.sha1
@@ -0,0 +1 @@
+028870b3b5592730a7b4cc64b08f13d7b104e93c
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_3d332e7f0f.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_3d332e7f0f.wprgo.sha1
new file mode 100644
index 0000000..0b21caf
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_3d332e7f0f.wprgo.sha1
@@ -0,0 +1 @@
+3d332e7f0f1580724f1207b6fad33471d46b7c6d
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_4367fb5828.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_4367fb5828.wprgo.sha1
new file mode 100644
index 0000000..83b74ce
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_4367fb5828.wprgo.sha1
@@ -0,0 +1 @@
+4367fb5828d8ed9820c4e0822d05467e28a09c57
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_5d042249d1.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_5d042249d1.wprgo.sha1
new file mode 100644
index 0000000..cecb45ce3
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_5d042249d1.wprgo.sha1
@@ -0,0 +1 @@
+9d72c4007598701f1597e422eca5258be46df027
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_cac60dab45.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_cac60dab45.wprgo.sha1
new file mode 100644
index 0000000..97cd3df3
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_cac60dab45.wprgo.sha1
@@ -0,0 +1 @@
+cac60dab45acec6c7ddf1010a6a859a327388563
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index bbec034..c865e94 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -38,12 +38,26 @@
           story_tags.YEAR_2016]
 
 
+class LoadBaiduStory2018(_LoadingStory):
+  NAME = 'load:search:baidu:2018'
+  URL = 'https://www.baidu.com/s?word=google'
+  TAGS = [story_tags.INTERNATIONAL, story_tags.HEALTH_CHECK,
+          story_tags.YEAR_2018]
+
+
 class LoadYahooStory(_LoadingStory):
   NAME = 'load:search:yahoo'
   URL = 'https://search.yahoo.com/search;_ylt=?p=google'
   TAGS = [story_tags.YEAR_2016]
 
 
+class LoadYahooStory2018(_LoadingStory):
+  NAME = 'load:search:yahoo:2018'
+  # Use additional parameter to bypass consent screen.
+  URL = 'https://search.yahoo.com/search;_ylt=?p=google&_guc_consent_skip=1541794498'
+  TAGS = [story_tags.YEAR_2018]
+
+
 class LoadAmazonDesktopStory(_LoadingStory):
   NAME = 'load:search:amazon'
   URL = 'https://www.amazon.com/s/?field-keywords=nexus'
@@ -94,6 +108,12 @@
   TAGS = [story_tags.INTERNATIONAL, story_tags.YEAR_2016]
 
 
+class LoadYandexStory2018(_LoadingStory):
+  NAME = 'load:search:yandex:2018'
+  URL = 'https://yandex.ru/touchsearch?text=science'
+  TAGS = [story_tags.INTERNATIONAL, story_tags.YEAR_2018]
+
+
 class LoadEbayStory(_LoadingStory):
   NAME = 'load:search:ebay'
   # Redirects to the "http://" version.
@@ -335,6 +355,12 @@
   TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2016]
 
 
+class LoadGoogleImagesStory2018(_LoadingStory):
+  NAME = 'load:media:google_images:2018'
+  URL = 'https://www.google.co.uk/search?tbm=isch&q=love'
+  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2018]
+
+
 class LoadSoundCloudStory(_LoadingStory):
   # No way to disable autoplay on desktop. Album artwork doesn't load due to
   # https://github.com/chromium/web-page-replay/issues/73.
@@ -356,6 +382,12 @@
   TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2016]
 
 
+class LoadFlickrStory2018(_LoadingStory):
+  NAME = 'load:media:flickr:2018'
+  URL = 'https://www.flickr.com/photos/tags/noiretblanc'
+  TAGS = [story_tags.YEAR_2018]
+
+
 class LoadFacebookPhotosMobileStory(_LoadingStory):
   """Load a page of rihanna's facebook with a photo."""
   NAME = 'load:media:facebook_photos'
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 4dc34fd..3d33dd9 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -147,6 +147,16 @@
     if (use_glib) {
       configs += [ "//build/config/linux:glib" ]
     }
+
+    if (use_x11) {
+      sources += [ "platform/atk_util_auralinux_x11.cc" ]
+
+      configs += [ "//build/config/linux:x11" ]
+      public_deps += [
+        "//ui/events/x",
+        "//ui/gfx/x",
+      ]
+    }
   }
 
   if (use_aura) {
@@ -242,7 +252,10 @@
     libs = [ "oleacc.lib" ]
   }
   if (use_atk) {
-    sources += [ "platform/ax_platform_node_auralinux_unittest.cc" ]
+    sources += [
+      "platform/atk_util_auralinux_unittest.cc",
+      "platform/ax_platform_node_auralinux_unittest.cc",
+    ]
     configs += [ "//build/config/linux/atk" ]
   }
 }
diff --git a/ui/accessibility/platform/DEPS b/ui/accessibility/platform/DEPS
new file mode 100644
index 0000000..d457dd7d
--- /dev/null
+++ b/ui/accessibility/platform/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "atk_util_auralinux_x11.cc": [
+    "+ui/events/x",
+  ]
+}
diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc
index eab53e1..85895d8 100644
--- a/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/ui/accessibility/platform/atk_util_auralinux.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include <atk/atk.h>
+#include <map>
+#include <utility>
 
 #include "base/environment.h"
 #include "base/memory/singleton.h"
@@ -75,6 +77,25 @@
   return "1.0";
 }
 
+static std::map<guint, std::pair<AtkKeySnoopFunc, gpointer>>
+    g_key_snoop_function_map;
+
+static guint atk_util_add_key_event_listener(AtkKeySnoopFunc key_snoop_function,
+                                             gpointer data) {
+  static guint current_key_event_listener_id = 0;
+
+  current_key_event_listener_id++;
+  g_key_snoop_function_map[current_key_event_listener_id] =
+      std::make_pair(key_snoop_function, data);
+  return current_key_event_listener_id;
+}
+
+static void atk_util_remove_key_event_listener(guint listener_id) {
+  auto it = g_key_snoop_function_map.find(listener_id);
+  if (it != g_key_snoop_function_map.end())
+    g_key_snoop_function_map.erase(it);
+}
+
 static void atk_util_auralinux_class_init(AtkUtilAuraLinuxClass *klass) {
   AtkUtilClass *atk_class;
   gpointer data;
@@ -85,6 +106,8 @@
   atk_class->get_root = atk_util_auralinux_get_root;
   atk_class->get_toolkit_name = atk_util_auralinux_get_toolkit_name;
   atk_class->get_toolkit_version = atk_util_auralinux_get_toolkit_version;
+  atk_class->add_key_event_listener = atk_util_add_key_event_listener;
+  atk_class->remove_key_event_listener = atk_util_remove_key_event_listener;
 }
 
 G_END_DECLS
@@ -130,4 +153,32 @@
   InitializeAsync();
 }
 
+// static
+DiscardAtkKeyEvent AtkUtilAuraLinux::HandleAtkKeyEvent(
+    AtkKeyEventStruct* key_event) {
+  DCHECK(key_event);
+
+  if (!GetInstance()->ShouldEnableAccessibility())
+    return DiscardAtkKeyEvent::Retain;
+
+  GetInstance()->InitializeAsync();
+
+  bool discard = false;
+  for (auto it = g_key_snoop_function_map.begin();
+       it != g_key_snoop_function_map.end(); it++) {
+    AtkKeySnoopFunc key_snoop_function = it->second.first;
+    gpointer data = it->second.second;
+    if (key_snoop_function(key_event, data) != 0)
+      discard = true;
+  }
+  return discard ? DiscardAtkKeyEvent::Discard : DiscardAtkKeyEvent::Retain;
+}
+
+#if !defined(USE_X11)
+DiscardAtkKeyEvent AtkUtilAuraLinux::HandleKeyEvent(
+    const ui::KeyEvent& ui_key_event) {
+  NOTREACHED();
+}
+#endif
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/atk_util_auralinux.h b/ui/accessibility/platform/atk_util_auralinux.h
index 286d49a..b846d5f 100644
--- a/ui/accessibility/platform/atk_util_auralinux.h
+++ b/ui/accessibility/platform/atk_util_auralinux.h
@@ -5,12 +5,34 @@
 #ifndef UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_
 #define UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_
 
+#include <atk/atk.h>
+
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "ui/accessibility/ax_export.h"
 
+#if defined(USE_X11)
+#include "ui/gfx/x/x11.h"
+#endif
+
 namespace ui {
 
+// These values are duplicates of the GDK values that can be found in
+// <gdk/gdktypes.h>. ATK expects the GDK values, but we don't want to depend on
+// GDK here.
+typedef enum {
+  kAtkShiftMask = 1 << 0,
+  kAtkLockMask = 1 << 1,
+  kAtkControlMask = 1 << 2,
+  kAtkMod1Mask = 1 << 3,
+  kAtkMod2Mask = 1 << 4,
+  kAtkMod3Mask = 1 << 5,
+  kAtkMod4Mask = 1 << 6,
+  KAtkMod5Mask = 1 << 7,
+} AtkKeyModifierMask;
+
+enum DiscardAtkKeyEvent { Discard, Retain };
+
 // This singleton class initializes ATK (accessibility toolkit) and
 // registers an implementation of the AtkUtil class, a global class that
 // every accessible application needs to register once.
@@ -24,6 +46,12 @@
   void InitializeAsync();
   void InitializeForTesting();
 
+  static DiscardAtkKeyEvent HandleAtkKeyEvent(AtkKeyEventStruct* key_event);
+
+#if defined(USE_X11)
+  static DiscardAtkKeyEvent HandleKeyEvent(XEvent* xevent);
+#endif
+
  private:
   friend struct base::DefaultSingletonTraits<AtkUtilAuraLinux>;
 
diff --git a/ui/accessibility/platform/atk_util_auralinux_unittest.cc b/ui/accessibility/platform/atk_util_auralinux_unittest.cc
new file mode 100644
index 0000000..85d88e2
--- /dev/null
+++ b/ui/accessibility/platform/atk_util_auralinux_unittest.cc
@@ -0,0 +1,77 @@
+// 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.
+
+// Chromium cannot upgrade to ATK 2.12 API as it still needs to run
+// valid builds for Ubuntu Trusty.
+#define ATK_DISABLE_DEPRECATION_WARNINGS
+
+#include <atk/atk.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/platform/atk_util_auralinux.h"
+#include "ui/accessibility/platform/ax_platform_node_auralinux.h"
+#include "ui/accessibility/platform/ax_platform_node_unittest.h"
+#include "ui/accessibility/platform/test_ax_node_wrapper.h"
+
+namespace ui {
+
+class AtkUtilAuraLinuxTest : public AXPlatformNodeTest {
+ public:
+  AtkUtilAuraLinuxTest() {
+    // We need to create a platform node in order to install it as the root
+    // ATK node. The ATK bridge will complain if we try to use it without a
+    // root node installed.
+    AXNodeData root;
+    root.id = 1;
+    Init(root);
+
+    TestAXNodeWrapper* wrapper =
+        TestAXNodeWrapper::GetOrCreate(tree_.get(), GetRootNode());
+    if (!wrapper)
+      NOTREACHED();
+    AXPlatformNodeAuraLinux::SetApplication(wrapper->ax_platform_node());
+
+    AtkUtilAuraLinux::GetInstance()->InitializeForTesting();
+  }
+
+  ~AtkUtilAuraLinuxTest() override {
+    TestAXNodeWrapper* wrapper =
+        TestAXNodeWrapper::GetOrCreate(tree_.get(), GetRootNode());
+    if (!wrapper)
+      NOTREACHED();
+    g_object_unref(wrapper->ax_platform_node()->GetNativeViewAccessible());
+  }
+};
+
+TEST_F(AtkUtilAuraLinuxTest, KeySnooping) {
+  AtkKeySnoopFunc key_snoop_func = reinterpret_cast<AtkKeySnoopFunc>(
+      +[](AtkKeyEventStruct* key_event, int* keyval_seen) {
+        *keyval_seen = key_event->keyval;
+      });
+
+  int keyval_seen = 0;
+  guint listener_id = atk_add_key_event_listener(key_snoop_func, &keyval_seen);
+
+  AtkKeyEventStruct atk_key_event;
+  atk_key_event.type = ATK_KEY_EVENT_PRESS;
+  atk_key_event.state = 0;
+  atk_key_event.keyval = 55;
+  atk_key_event.keycode = 10;
+  atk_key_event.timestamp = 10;
+  atk_key_event.string = nullptr;
+  atk_key_event.length = 0;
+
+  AtkUtilAuraLinux* atk_util = AtkUtilAuraLinux::GetInstance();
+  atk_util->HandleAtkKeyEvent(&atk_key_event);
+  EXPECT_EQ(keyval_seen, 55);
+
+  atk_remove_key_event_listener(listener_id);
+
+  keyval_seen = 0;
+  atk_util->HandleAtkKeyEvent(&atk_key_event);
+
+  EXPECT_EQ(keyval_seen, 0);
+}
+
+}  // namespace ui
diff --git a/ui/accessibility/platform/atk_util_auralinux_x11.cc b/ui/accessibility/platform/atk_util_auralinux_x11.cc
new file mode 100644
index 0000000..143d9ca
--- /dev/null
+++ b/ui/accessibility/platform/atk_util_auralinux_x11.cc
@@ -0,0 +1,60 @@
+// 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 <atk/atk.h>
+
+#include "ui/accessibility/platform/atk_util_auralinux.h"
+#include "ui/events/x/events_x_utils.h"
+
+namespace ui {
+
+// static
+DiscardAtkKeyEvent AtkUtilAuraLinux::HandleKeyEvent(XEvent* xevent) {
+  if (!GetInstance()->ShouldEnableAccessibility())
+    return DiscardAtkKeyEvent::Retain;
+
+  AtkKeyEventStruct atk_key_event;
+  if (xevent->type == KeyPress)
+    atk_key_event.type = ATK_KEY_EVENT_PRESS;
+  else if (xevent->type == KeyRelease)
+    atk_key_event.type = ATK_KEY_EVENT_RELEASE;
+  else
+    NOTREACHED() << xevent->type;
+
+  XKeyEvent& xkey = xevent->xkey;
+  KeySym keysym = NoSymbol;
+  XLookupString(&xkey, nullptr, 0, &keysym, nullptr);
+
+  atk_key_event.state = xkey.state;
+  atk_key_event.keyval = keysym;
+  atk_key_event.keycode = xkey.keycode;
+  atk_key_event.timestamp = xkey.time;
+
+  // This string property matches the one that was removed from GdkEventKey. In
+  // the future, ATK clients should no longer rely on it, so we set it to null.
+  atk_key_event.string = nullptr;
+  atk_key_event.length = 0;
+
+  int flags = ui::EventFlagsFromXEvent(*xevent);
+  if (flags & ui::EF_SHIFT_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkShiftMask;
+  if (flags & ui::EF_CAPS_LOCK_ON)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkLockMask;
+  if (flags & ui::EF_CONTROL_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkControlMask;
+  if (flags & ui::EF_ALT_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkMod1Mask;
+  if (flags & ui::EF_NUM_LOCK_ON)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkMod2Mask;
+  if (flags & ui::EF_MOD3_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkMod3Mask;
+  if (flags & ui::EF_COMMAND_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::kAtkMod4Mask;
+  if (flags & ui::EF_ALTGR_DOWN)
+    atk_key_event.state |= AtkKeyModifierMask::KAtkMod5Mask;
+
+  return HandleAtkKeyEvent(&atk_key_event);
+}
+
+}  // namespace ui
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index 1ed6f474..5ddb65a8 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -80,7 +80,7 @@
    */
   this.mediaImportHandler = new importer.MediaImportHandler(
       this.progressCenter, this.historyLoader, this.dispositionChecker_,
-      this.tracker, this.driveSyncHandler);
+      this.driveSyncHandler);
 
   /**
    * String assets.
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index 5aa28d5..39397b0 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -15,12 +15,10 @@
  * @param {!ProgressCenter} progressCenter
  * @param {!importer.HistoryLoader} historyLoader
  * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
- * @param {!analytics.Tracker} tracker
  * @param {!DriveSyncHandler} driveSyncHandler
  */
 importer.MediaImportHandler = function(
-    progressCenter, historyLoader, dispositionChecker, tracker,
-    driveSyncHandler) {
+    progressCenter, historyLoader, dispositionChecker, driveSyncHandler) {
   /** @private {!ProgressCenter} */
   this.progressCenter_ = progressCenter;
 
@@ -38,9 +36,6 @@
     chrome.power.releaseKeepAwake();
   });
 
-  /** @private {!analytics.Tracker} */
-  this.tracker_ = tracker;
-
   /** @private {number} */
   this.nextTaskId_ = 0;
 
@@ -66,7 +61,7 @@
 
   var task = new importer.MediaImportHandler.ImportTask(
       this.generateTaskId_(), this.historyLoader_, scanResult, directoryPromise,
-      destination, this.getDisposition_, this.tracker_);
+      destination, this.getDisposition_);
 
   task.addObserver(this.onTaskProgress_.bind(this, task));
   task.addObserver(this.onFileImported_.bind(this, task));
@@ -230,16 +225,10 @@
  * @param {!Promise<!DirectoryEntry>} directoryPromise
  * @param {!importer.Destination} destination The logical destination.
  * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
- * @param {!analytics.Tracker} tracker
  */
 importer.MediaImportHandler.ImportTask = function(
-    taskId,
-    historyLoader,
-    scanResult,
-    directoryPromise,
-    destination,
-    dispositionChecker,
-    tracker) {
+    taskId, historyLoader, scanResult, directoryPromise, destination,
+    dispositionChecker) {
 
   importer.TaskQueue.BaseTask.call(this, taskId);
   /** @protected {string} */
@@ -257,9 +246,6 @@
   /** @private {!importer.HistoryLoader} */
   this.historyLoader_ = historyLoader;
 
-  /** @private {!analytics.Tracker} */
-  this.tracker_ = tracker;
-
   /** @private {number} */
   this.totalBytes_ = 0;
 
@@ -400,7 +386,7 @@
   this.remainingFilesCount_ = stats.newFileCount;
   this.totalBytes_ = stats.sizeBytes;
 
-  this.tracker_.send(metrics.ImportEvents.STARTED);
+  metrics.recordBoolean('MediaImport.Started', true);
 };
 
 /**
@@ -459,7 +445,7 @@
 importer.MediaImportHandler.ImportTask.prototype.importOne_ = function(
     destinationDirectory, completionCallback, entry, index, all) {
   if (this.canceled_) {
-    this.tracker_.send(metrics.ImportEvents.IMPORT_CANCELLED);
+    metrics.recordBoolean('MediaImport.Cancelled', true);
     return;
   }
 
@@ -638,18 +624,16 @@
 
   var scanStats = this.scanResult_.getStatistics();
 
-  this.tracker_.send(
-      metrics.ImportEvents.MEGABYTES_IMPORTED.value(
-          // Make megabytes of our bytes.
-          Math.floor(this.processedBytes_ / (1024 * 1024))));
+  metrics.recordMediumCount(
+      'MediaImport.ImportMB', Math.floor(this.processedBytes_ / (1024 * 1024)));
 
-  this.tracker_.send(
-      metrics.ImportEvents.FILES_IMPORTED.value(
-          // Substract the remaining files, in case the task was cancelled.
-          scanStats.newFileCount - this.remainingFilesCount_));
+  metrics.recordMediumCount(
+      'MediaImport.ImportCount',
+      // Substract the remaining files, in case the task was cancelled.
+      scanStats.newFileCount - this.remainingFilesCount_);
 
   if (this.errorCount_ > 0) {
-    this.tracker_.send(metrics.ImportEvents.ERRORS.value(this.errorCount_));
+    metrics.recordMediumCount('MediaImport.ErrorCount', this.errorCount_);
   }
 
   // Finally we want to report on the number of duplicates
@@ -660,19 +644,13 @@
   assert(scanStats.duplicates[importer.Disposition.CONTENT_DUPLICATE] === 0);
   scanStats.duplicates[importer.Disposition.CONTENT_DUPLICATE] =
       this.duplicateFilesCount_;
+
   Object.keys(scanStats.duplicates).forEach(
       function(disposition) {
         var count = scanStats.duplicates[
             /** @type {!importer.Disposition} */ (disposition)];
         totalDeduped += count;
-        this.tracker_.send(
-            metrics.ImportEvents.FILES_DEDUPLICATED
-                .label(disposition)
-                .value(count));
       }, this);
 
-  this.tracker_.send(
-      metrics.ImportEvents.FILES_DEDUPLICATED
-          .label('all-duplicates')
-          .value(totalDeduped));
+  metrics.recordMediumCount('MediaImport.Duplicates', totalDeduped);
 };
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
index 05ba29ab..c4fb84d7 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
@@ -26,7 +26,6 @@
   <script src="../../common/js/importer_common.js"></script>
   <script src="../../common/js/test_importer_common.js"></script>
   <script src="../../common/js/progress_center_common.js"></script>
-  <script src="../../common/js/test_tracker.js"></script>
 
   <script src="drive_sync_handler.js"></script>
   <script src="entry_location_impl.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
index 5a95e521..257e8712 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
@@ -39,11 +39,20 @@
 loadTimeData.data = {
   CLOUD_IMPORT_ITEMS_REMAINING: '',
   DRIVE_DIRECTORY_LABEL: 'My Drive',
-  DOWNLOADS_DIRECTORY_LABEL: 'Downloads'
+  DOWNLOADS_DIRECTORY_LABEL: 'Downloads',
+  DRIVE_OFFLINE_COLLECTION_LABEL: 'Offline',
+  DRIVE_SHARED_WITH_ME_COLLECTION_LABEL: 'Shared with me',
 };
 
 var chrome;
 
+window.metrics = {
+  recordSmallCount: function() {},
+  recordUserAction: function() {},
+  recordMediumCount: function() {},
+  recordBoolean: function() {},
+};
+
 function setUp() {
   // Set up mock chrome APIs.
   chrome = {
@@ -98,7 +107,7 @@
   mediaImporter = new importer.MediaImportHandler(
       progressCenter, importHistory, function(entry, destination) {
         return dispositionChecker(entry, destination);
-      }, new TestTracker(), driveSyncHandler);
+      }, driveSyncHandler);
 }
 
 function testImportMedia(callback) {
@@ -168,8 +177,7 @@
     return Promise.resolve(importer.Disposition.ORIGINAL);
   };
   mediaImporter = new importer.MediaImportHandler(
-      progressCenter, importHistory, dispositionChecker, new TestTracker(),
-      driveSyncHandler);
+      progressCenter, importHistory, dispositionChecker, driveSyncHandler);
   var scanResult = new TestScanResult(media);
   var importTask = mediaImporter.importFromScanResult(
       scanResult,
diff --git a/ui/file_manager/file_manager/common/js/metrics_base.js b/ui/file_manager/file_manager/common/js/metrics_base.js
index 940d2ce..3fd04ad 100644
--- a/ui/file_manager/file_manager/common/js/metrics_base.js
+++ b/ui/file_manager/file_manager/common/js/metrics_base.js
@@ -95,6 +95,15 @@
 };
 
 /**
+ * Records a boolean value to the given metric.
+ * @param {string} name Short metric name.
+ * @param {boolean} value The value to be recorded.
+ */
+metricsBase.recordBoolean = function(name, value) {
+  metrics.call_('recordBoolean', [metrics.convertName_(name), value]);
+};
+
+/**
  * Records an action performed by the user.
  * @param {string} name Short metric name.
  */
diff --git a/ui/file_manager/file_manager/common/js/metrics_events.js b/ui/file_manager/file_manager/common/js/metrics_events.js
index aee2656..33ec250 100644
--- a/ui/file_manager/file_manager/common/js/metrics_events.js
+++ b/ui/file_manager/file_manager/common/js/metrics_events.js
@@ -97,10 +97,6 @@
     index: metrics.Dimension_.SESSION_TYPE,
     value: 'Manage'
   },
-  SESSION_TYPE_IMPORT: {
-    index: metrics.Dimension_.SESSION_TYPE,
-    value: 'Import'
-  },
   MACHINE_USE_SINGLE: {
     index: metrics.Dimension_.MACHINE_USE,
     value: 'Single'
@@ -137,31 +133,8 @@
 
 /** @enum {!analytics.EventBuilder} */
 metrics.ImportEvents = {
-  DEVICE_YANKED: metrics.event.Builders_.IMPORT
-      .action('Device Yanked'),
-
-  ERRORS: metrics.event.Builders_.IMPORT
-      .action('Import Error Count'),
-
-  FILES_DEDUPLICATED: metrics.event.Builders_.IMPORT
-      .action('Files Deduplicated'),
-
-  FILES_IMPORTED: metrics.event.Builders_.IMPORT
-      .action('Files Imported'),
-
   HISTORY_LOADED: metrics.event.Builders_.IMPORT
       .action('History Loaded'),
-
-  IMPORT_CANCELLED: metrics.event.Builders_.IMPORT
-      .action('Import Cancelled'),
-
-  MEGABYTES_IMPORTED: metrics.event.Builders_.IMPORT
-      .action('Megabytes Imported'),
-
-  STARTED: metrics.event.Builders_.IMPORT
-      .action('Import Started')
-      .dimension(metrics.Dimensions.SESSION_TYPE_IMPORT)
-      .dimension(metrics.Dimensions.CONSUMER_TYPE_IMPORTER)
 };
 
 /** @enum {!analytics.EventBuilder} */
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 190512c..fcf964a 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -643,12 +643,9 @@
             if (enabled) {
               this.importController_ = new importer.ImportController(
                   new importer.RuntimeControllerEnvironment(
-                      this,
-                      assert(this.selectionHandler_)),
-                  assert(this.mediaScanner_),
-                  assert(this.mediaImportHandler_),
-                  new importer.RuntimeCommandWidget(),
-                  assert(this.tracker_));
+                      this, assert(this.selectionHandler_)),
+                  assert(this.mediaScanner_), assert(this.mediaImportHandler_),
+                  new importer.RuntimeCommandWidget());
             }
           }.bind(this));
     }
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 3fa93a9..8122337 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -29,10 +29,9 @@
  * @param {!importer.MediaScanner} scanner
  * @param {!importer.ImportRunner} importRunner
  * @param {!importer.CommandWidget} commandWidget
- * @param {!analytics.Tracker} tracker
  */
-importer.ImportController =
-    function(environment, scanner, importRunner, commandWidget, tracker) {
+importer.ImportController = function(
+    environment, scanner, importRunner, commandWidget) {
 
   /** @private {!importer.ControllerEnvironment} */
   this.environment_ = environment;
@@ -52,9 +51,6 @@
   /** @type {!importer.ScanManager} */
   this.scanManager_ = new importer.ScanManager(environment, scanner);
 
-  /** @private {!analytics.Tracker} */
-  this.tracker_ = tracker;
-
   /**
    * The active import task, if any.
    * @private {?importer.TaskDetails_}
@@ -160,7 +156,7 @@
   if (this.activeImport_) {
     this.activeImport_.task.requestCancel();
     this.finalizeActiveImport_();
-    this.tracker_.send(metrics.ImportEvents.DEVICE_YANKED);
+    metrics.recordBoolean('ImportController.DeviceYanked');
   }
   this.scanManager_.reset();
   this.checkState_();
@@ -330,7 +326,7 @@
   if (this.activeImport_) {
     this.activeImport_.task.requestCancel();
     this.finalizeActiveImport_();
-    this.tracker_.send(metrics.ImportEvents.IMPORT_CANCELLED);
+    metrics.recordBoolean('ImportController.ImportCancelled');
   }
 
   this.scanManager_.reset();
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
index 6c2be86..d0558749 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
@@ -23,7 +23,6 @@
   <script src="../../common/js/util.js"></script>
   <script src="../../common/js/metrics_base.js"></script>
   <script src="../../common/js/metrics_events.js"></script>
-  <script src="../../common/js/test_tracker.js"></script>
   <script src="../../../base/js/volume_manager_types.js"></script>
   <script src="../../common/js/file_type.js"></script>
   <script src="../../common/js/importer_common.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
index 7e851664..4191f327 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
@@ -47,6 +47,13 @@
 // Set up string assets.
 loadTimeData.data = MESSAGES;
 
+window.metrics = {
+  recordSmallCount: function() {},
+  recordUserAction: function() {},
+  recordValue: function() {},
+  recordBoolean: function() {},
+};
+
 function setUp() {
   new MockChromeStorageAPI();
   new MockCommandLinePrivate();
@@ -701,11 +708,7 @@
       sourceVolume.fileSystem.entries[currentDirectory]);
 
   return new importer.ImportController(
-      environment,
-      mediaScanner,
-      mediaImporter,
-      widget,
-      new TestTracker());
+      environment, mediaScanner, mediaImporter, widget);
 }
 
 /**
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 183f1c52..085a670 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -156,7 +156,7 @@
 bool g_egl_create_context_client_arrays_supported = false;
 bool g_egl_android_native_fence_sync_supported = false;
 
-const char kSwapEventTraceCategories[] = "gpu";
+constexpr const char kSwapEventTraceCategories[] = "gpu";
 
 constexpr size_t kMaxTimestampsSupportable = 9;
 
diff --git a/ui/latency/frame_metrics.cc b/ui/latency/frame_metrics.cc
index d4ccf32..9cd0c25 100644
--- a/ui/latency/frame_metrics.cc
+++ b/ui/latency/frame_metrics.cc
@@ -100,7 +100,7 @@
     RatioThreshold(0), RatioThreshold(1), RatioThreshold(2), RatioThreshold(4),
 };
 
-const char kTraceCategories[] = "gpu,benchmark";
+constexpr const char kTraceCategories[] = "gpu,benchmark";
 
 // uint32_t should be plenty of range for real world values, but clip individual
 // entries to make sure no single value dominates and also to avoid overflow
diff --git a/ui/latency/latency_info.cc b/ui/latency/latency_info.cc
index aa09366e..e20d080f 100644
--- a/ui/latency/latency_info.cc
+++ b/ui/latency/latency_info.cc
@@ -98,7 +98,8 @@
     : value_(value) {
 }
 
-const char kTraceCategoriesForAsyncEvents[] = "benchmark,latencyInfo,rail";
+constexpr const char kTraceCategoriesForAsyncEvents[] =
+    "benchmark,latencyInfo,rail";
 
 struct LatencyInfoEnabledInitializer {
   LatencyInfoEnabledInitializer() :
diff --git a/ui/latency/skipped_frame_tracker_unittest.cc b/ui/latency/skipped_frame_tracker_unittest.cc
index 3387792..06812c5c 100644
--- a/ui/latency/skipped_frame_tracker_unittest.cc
+++ b/ui/latency/skipped_frame_tracker_unittest.cc
@@ -394,9 +394,14 @@
   EXPECT_FALSE(tracker_.IsActive());
 }
 
+// Simulate that SetNeedsRedraw is called, then the client realized that it
+// doesn't need a new BeginFrame.
 TEST_F(SkippedFrameTrackerTest, NoFrameProduced) {
   EXPECT_TRUE(WillProduceFrame());
   EXPECT_TRUE(WillNotProduceFrame());
+
+  // Since no BeginFrame is needed, number of frames produced and the number
+  // of skipped frames should all be 0.
   EXPECT_EQ(0, client_.amount_produced_);
   EXPECT_EQ(0, client_.amount_skipped_);
 }
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 08eb8aa52..b0f4756 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2156,7 +2156,7 @@
       // Menu fits above anchor bounds.
       menu_bounds.set_y(above_anchor);
       item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS);
-    } else {
+    } else if (item->GetDelegate()->ShouldTryPositioningBesideAnchor()) {
       const int left_of_anchor = anchor_bounds.x() - menu_bounds.width();
       const int right_of_anchor = anchor_bounds.right();
 
@@ -2174,6 +2174,11 @@
         if (menu_bounds.x() < monitor_bounds.x())
           menu_bounds.set_x(right_of_anchor);
       }
+    } else {
+      // The delegate doesn't want the menu repositioned to the side, and it
+      // doesn't fit on the screen in any orientation - just clip the menu to
+      // the screen and let the scrolling arrows appear.
+      menu_bounds.Intersect(monitor_bounds);
     }
   }
 
diff --git a/ui/views/controls/menu/menu_delegate.cc b/ui/views/controls/menu/menu_delegate.cc
index 56c3c51c..f8a91bc 100644
--- a/ui/views/controls/menu/menu_delegate.cc
+++ b/ui/views/controls/menu/menu_delegate.cc
@@ -152,4 +152,8 @@
   return true;
 }
 
+bool MenuDelegate::ShouldTryPositioningBesideAnchor() const {
+  return true;
+}
+
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_delegate.h b/ui/views/controls/menu/menu_delegate.h
index 70660518..69a8ed5 100644
--- a/ui/views/controls/menu/menu_delegate.h
+++ b/ui/views/controls/menu/menu_delegate.h
@@ -233,6 +233,11 @@
   // Returns true if the labels should reserve additional spacing for e.g.
   // submenu indicators at the end of the line.
   virtual bool ShouldReserveSpaceForSubmenuIndicator() const;
+
+  // Returns true if menus should fall back to positioning beside the anchor,
+  // rather than directly above or below it, when the menu is too tall to fit
+  // within the screen.
+  virtual bool ShouldTryPositioningBesideAnchor() const;
 };
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 2c378f14..9fd46f65 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -17,6 +17,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/skia/include/core/SkPath.h"
+#include "ui/accessibility/platform/atk_util_auralinux.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/focus_client.h"
@@ -2045,8 +2046,11 @@
       break;
     }
     case KeyPress: {
-      ui::KeyEvent keydown_event(xev);
-      DispatchKeyEvent(&keydown_event);
+      if (ui::AtkUtilAuraLinux::HandleKeyEvent(xev) !=
+          ui::DiscardAtkKeyEvent::Discard) {
+        ui::KeyEvent keydown_event(xev);
+        DispatchKeyEvent(&keydown_event);
+      }
       break;
     }
     case KeyRelease: {
@@ -2055,8 +2059,11 @@
       if (!IsActive() && !HasCapture())
         break;
 
-      ui::KeyEvent key_event(xev);
-      DispatchKeyEvent(&key_event);
+      if (ui::AtkUtilAuraLinux::HandleKeyEvent(xev) !=
+          ui::DiscardAtkKeyEvent::Discard) {
+        ui::KeyEvent key_event(xev);
+        DispatchKeyEvent(&key_event);
+      }
       break;
     }
     case ButtonPress:
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 8db6db8..563575a 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -175,7 +175,8 @@
 
   window_->Init(params.layer_type);
   // Set name after layer init so it propagates to layer.
-  window_->SetName(params.name);
+  if (!params.name.empty())
+    window_->SetName(params.name);
   if (params.type == Widget::InitParams::TYPE_CONTROL)
     window_->Show();