diff --git a/AUTHORS b/AUTHORS
index fe18810..93e9f00 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -599,7 +599,7 @@
 Miran Karic <miran.karic@imgtec.com>
 Mirela Budaes <mbudaes@adobe.com>
 Mirela Budaes <mbudaes@gmail.com>
-Miyoung Shin <myid.shin@samsung.com>
+Miyoung Shin <myid.shin@navercorp.com>
 Mohamed I. Hammad <ibraaaa@gmail.com>
 Mohamed Mansour <m0.interactive@gmail.com>
 Mohammad Azam <m.azam@samsung.com>
diff --git a/DEPS b/DEPS
index fb00c6d3..82ea8a7 100644
--- a/DEPS
+++ b/DEPS
@@ -105,7 +105,7 @@
   # 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': '76c775f48decb142549215319ae9868a769d9902',
+  'skia_revision': 'be8193c9313a7192947b10b7ba6f192245b4f704',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -129,7 +129,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '7f47c50227fb4af93b58c6687786130c42b76333',
+  'pdfium_revision': 'a1c846c4cf3f8c08edfffa1cc6b60860c011000b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -165,7 +165,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': 'cb192dee19d43e5a17bdfa8b7397122aad312bc3',
+  'catapult_revision': '2dd914402ebcc44ecb60034e5c7b9bfa3d0c9d89',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -620,7 +620,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0daedf7758e157ee22a64e9dabcb089c0da5ef4c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '22300e1fb562291b55eb702fe73b164cb1a2317d',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -954,7 +954,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'a80f9bc11bcbea9fc552654281354fb696ac7b6b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '2cbceb0be548f4d58fadf95381220e13aaf55a73',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1106,7 +1106,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd2650d1a2851ab4fb14b86f04ccc4c2c09c237f5',
+    Var('webrtc_git') + '/src.git' + '@' + '086cac5c439cf9f820b11b52859c03279c750989',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1137,7 +1137,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@64ba5a302bf0da7647c98f2aabccc42c072e1364',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4b969b6f368a5a46a06b2e79aea50494eb1bb3e3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 351e1f6..96358824 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2750,7 +2750,7 @@
 Use of => (arrow) operator detected in:
 %s
 Please ensure your code does not run on iOS9 (=> (arrow) does not work there).
-https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#Arrow-Functions
+https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#Arrow-Functions
 """ % f.LocalPath()
           ])))
 
@@ -2764,8 +2764,8 @@
 %s
 Please ensure your code does not run on iOS9 because const/let is not fully
 supported.
-https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#let-Block_Scoped-Variables
-https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#const-Block_Scoped-Constants
+https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#let-Block_Scoped-Variables
+https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/es6.md#const-Block_Scoped-Constants
 """ % f.LocalPath()
           ])))
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 49fb774..ed829fd2 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1787,7 +1787,6 @@
              'tfarina@chromium.org'],
     'arc': ['elijahtaylor+arcwatch@chromium.org',
             'hidehiko+watch@chromium.org',
-            'lhchavez+watch@chromium.org',
             'victorhsieh+watch@chromium.org',
             'yusukes+watch@chromium.org',
             'arc-reviews+chromium@google.com'],
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b493d5b..bd69a39f 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -731,10 +731,16 @@
     "system/message_center/fullscreen_notification_blocker.h",
     "system/message_center/inactive_user_notification_blocker.cc",
     "system/message_center/inactive_user_notification_blocker.h",
+    "system/message_center/new_unified_message_center_view.cc",
+    "system/message_center/new_unified_message_center_view.h",
     "system/message_center/notification_tray.cc",
     "system/message_center/notification_tray.h",
     "system/message_center/session_state_notification_blocker.cc",
     "system/message_center/session_state_notification_blocker.h",
+    "system/message_center/unified_message_center_view.cc",
+    "system/message_center/unified_message_center_view.h",
+    "system/message_center/unified_message_list_view.cc",
+    "system/message_center/unified_message_list_view.h",
     "system/model/clock_model.cc",
     "system/model/clock_model.h",
     "system/model/enterprise_domain_model.cc",
@@ -747,6 +753,8 @@
     "system/model/tracing_model.h",
     "system/model/update_model.cc",
     "system/model/update_model.h",
+    "system/model/virtual_keyboard_model.cc",
+    "system/model/virtual_keyboard_model.h",
     "system/network/auto_connect_notifier.cc",
     "system/network/auto_connect_notifier.h",
     "system/network/network_feature_pod_button.cc",
@@ -1013,8 +1021,6 @@
     "system/unified/top_shortcuts_view.h",
     "system/unified/unified_detailed_view_delegate.cc",
     "system/unified/unified_detailed_view_delegate.h",
-    "system/unified/unified_message_center_view.cc",
-    "system/unified/unified_message_center_view.h",
     "system/unified/unified_notifier_settings_controller.cc",
     "system/unified/unified_notifier_settings_controller.h",
     "system/unified/unified_slider_bubble_controller.cc",
@@ -1848,6 +1854,7 @@
     "system/message_center/inactive_user_notification_blocker_unittest.cc",
     "system/message_center/notification_tray_unittest.cc",
     "system/message_center/session_state_notification_blocker_unittest.cc",
+    "system/message_center/unified_message_list_view_unittest.cc",
     "system/network/auto_connect_notifier_unittest.cc",
     "system/network/network_icon_unittest.cc",
     "system/network/sms_observer_unittest.cc",
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index a021d9a..a899cff 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -73,13 +73,8 @@
 
 void AssistantUiController::OnWidgetActivationChanged(views::Widget* widget,
                                                       bool active) {
-  if (active) {
+  if (active)
     container_view_->RequestFocus();
-  } else {
-    // When the widget is deactivated the UI should hide. Interacting with
-    // the metalayer does not cause widget deactivation.
-    HideUi(AssistantSource::kUnspecified);
-  }
 }
 
 void AssistantUiController::OnWidgetVisibilityChanged(views::Widget* widget,
@@ -227,16 +222,39 @@
           ? mojom::VoiceInteractionState::RUNNING
           : mojom::VoiceInteractionState::STOPPED);
 
-  if (new_visibility == AssistantVisibility::kHidden) {
-    // When hiding the UI, start a timer to automatically close ourselves after
-    // |kAutoCloseThreshold|. This is to give the user an opportunity to resume
-    // their previous session before it is automatically finished.
-    auto_close_timer_.Start(FROM_HERE, kAutoCloseThreshold,
-                            base::BindRepeating(&AssistantUiController::CloseUi,
-                                                weak_factory_.GetWeakPtr(),
-                                                AssistantSource::kUnspecified));
-  } else {
-    auto_close_timer_.Stop();
+  switch (new_visibility) {
+    case AssistantVisibility::kClosed:
+      // When the UI is closed, we stop the auto close timer as it may be
+      // running and also stop monitoring events.
+      auto_close_timer_.Stop();
+      event_monitor_.reset();
+      break;
+    case AssistantVisibility::kHidden:
+      // When hiding the UI, we start a timer to automatically close ourselves
+      // after |kAutoCloseThreshold|. This is to give the user an opportunity to
+      // resume their previous session before it is automatically finished.
+      auto_close_timer_.Start(
+          FROM_HERE, kAutoCloseThreshold,
+          base::BindRepeating(&AssistantUiController::CloseUi,
+                              weak_factory_.GetWeakPtr(),
+                              AssistantSource::kUnspecified));
+
+      // Because the UI is not visible we needn't monitor events.
+      event_monitor_.reset();
+      break;
+    case AssistantVisibility::kVisible:
+      // Upon becoming visible, we stop the auto close timer.
+      auto_close_timer_.Stop();
+
+      // We need to monitor events for the root window while we're visible to
+      // give us an opportunity to dismiss Assistant UI when the user starts an
+      // interaction outside of our bounds. TODO(dmblack): Investigate how this
+      // behaves in a multi-display environment.
+      gfx::NativeWindow root_window =
+          container_view_->GetWidget()->GetNativeWindow()->GetRootWindow();
+      event_monitor_ =
+          views::EventMonitor::CreateWindowMonitor(this, root_window);
+      break;
   }
 
   // Metalayer should not be sticky. Disable when the UI is no longer visible.
@@ -381,6 +399,34 @@
   }
 }
 
+void AssistantUiController::OnMouseEvent(ui::MouseEvent* event) {
+  if (event->type() == ui::ET_MOUSE_PRESSED)
+    OnPressedEvent(*event);
+}
+
+void AssistantUiController::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->type() == ui::ET_TOUCH_PRESSED)
+    OnPressedEvent(*event);
+}
+
+void AssistantUiController::OnPressedEvent(const ui::LocatedEvent& event) {
+  DCHECK(event.type() == ui::ET_MOUSE_PRESSED ||
+         event.type() == ui::ET_TOUCH_PRESSED);
+
+  const gfx::Point screen_location =
+      event.target() ? event.target()->GetScreenLocation(event)
+                     : event.root_location();
+
+  const gfx::Rect screen_bounds =
+      container_view_->GetWidget()->GetWindowBoundsInScreen();
+
+  // Pressed events outside our widget bounds should result in hiding of
+  // Assistant UI. This event does not fire during a Metalayer session so we
+  // needn't enforce logic to prevent hiding when using the stylus.
+  if (!screen_bounds.Contains(screen_location))
+    HideUi(AssistantSource::kUnspecified);
+}
+
 void AssistantUiController::UpdateUsableWorkArea(aura::Window* root_window) {
   gfx::Rect usable_work_area;
   gfx::Rect screen_bounds = root_window->GetBoundsInScreen();
diff --git a/ash/assistant/assistant_ui_controller.h b/ash/assistant/assistant_ui_controller.h
index 9e23fe4..48b092a 100644
--- a/ash/assistant/assistant_ui_controller.h
+++ b/ash/assistant/assistant_ui_controller.h
@@ -21,8 +21,10 @@
 #include "base/macros.h"
 #include "base/timer/timer.h"
 #include "ui/display/display_observer.h"
+#include "ui/events/event_handler.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
+#include "ui/views/event_monitor.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace chromeos {
@@ -53,7 +55,8 @@
       public DialogPlateObserver,
       public HighlighterController::Observer,
       public keyboard::KeyboardControllerObserver,
-      public display::DisplayObserver {
+      public display::DisplayObserver,
+      public ui::EventHandler {
  public:
   explicit AssistantUiController(AssistantController* assistant_controller);
   ~AssistantUiController() override;
@@ -115,6 +118,10 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
+  // ui::EventHandler:
+  void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
+
   void ShowUi(AssistantSource source);
   void HideUi(AssistantSource source);
   void CloseUi(AssistantSource source);
@@ -123,6 +130,9 @@
   AssistantContainerView* GetViewForTest();
 
  private:
+  // Invoked on either a mouse or touch pressed event.
+  void OnPressedEvent(const ui::LocatedEvent& event);
+
   // Updates UI mode to |ui_mode| if specified. Otherwise UI mode is updated on
   // the basis of interaction/widget visibility state.
   void UpdateUiMode(base::Optional<AssistantUiMode> ui_mode = base::nullopt);
@@ -146,6 +156,8 @@
   AssistantContainerView* container_view_ =
       nullptr;  // Owned by view hierarchy.
 
+  std::unique_ptr<views::EventMonitor> event_monitor_;
+
   gfx::Rect keyboard_workspace_occluded_bounds_;
 
   // When hidden, Assistant automatically closes itself to finish the previous
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index 17ef63f5..33fe6991 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -121,16 +121,18 @@
     const std::string& new_host_device_name) {
   base::string16 title = l10n_util::GetStringFUTF16(
       IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_TITLE,
-      base::UTF8ToUTF16(new_host_device_name));
+      base::ASCIIToUTF16(new_host_device_name));
   base::string16 message = l10n_util::GetStringUTF16(
       IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_MESSAGE);
   ShowNotification(Status::kExistingUserHostSwitchedNotificationVisible, title,
                    message);
 }
 
-void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser() {
-  base::string16 title = l10n_util::GetStringUTF16(
-      IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_TITLE);
+void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser(
+    const std::string& new_host_device_name) {
+  base::string16 title = l10n_util::GetStringFUTF16(
+      IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_TITLE,
+      base::ASCIIToUTF16(new_host_device_name));
   base::string16 message = l10n_util::GetStringUTF16(
       IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_MESSAGE);
   ShowNotification(Status::kExistingUserNewChromebookNotificationVisible, title,
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.h b/ash/multi_device_setup/multi_device_notification_presenter.h
index 3a001650..2e4f158 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.h
+++ b/ash/multi_device_setup/multi_device_notification_presenter.h
@@ -61,7 +61,8 @@
   void OnPotentialHostExistsForNewUser() override;
   void OnConnectedHostSwitchedForExistingUser(
       const std::string& new_host_device_name) override;
-  void OnNewChromebookAddedForExistingUser() override;
+  void OnNewChromebookAddedForExistingUser(
+      const std::string& new_host_device_name) override;
 
   // SessionObserver:
   void OnUserSessionAdded(const AccountId& account_id) override;
diff --git a/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc b/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
index 987d252..65bf5b6 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter_unittest.cc
@@ -184,7 +184,8 @@
 
   void ShowExistingUserNewChromebookNotification() {
     EXPECT_TRUE(fake_multidevice_setup_->delegate().is_bound());
-    fake_multidevice_setup_->delegate()->OnNewChromebookAddedForExistingUser();
+    fake_multidevice_setup_->delegate()->OnNewChromebookAddedForExistingUser(
+        kTestHostDeviceName);
     InvokePendingMojoCalls();
   }
 
@@ -285,8 +286,9 @@
         break;
       case MultiDeviceNotificationPresenter::Status::
           kExistingUserNewChromebookNotificationVisible:
-        title = l10n_util::GetStringUTF16(
-            IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_TITLE);
+        title = l10n_util::GetStringFUTF16(
+            IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_TITLE,
+            base::ASCIIToUTF16(kTestHostDeviceName));
         message = l10n_util::GetStringUTF16(
             IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_MESSAGE);
         break;
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 78dff3c..91b56e8 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -35,6 +35,9 @@
     "LockScreenHideSensitiveNotificationsSupport",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kNewMessageListView{"NewMessageListView",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kNewWallpaperPicker{"NewWallpaperPicker",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -85,6 +88,10 @@
       kLockScreenHideSensitiveNotificationsSupport);
 }
 
+bool IsNewMessageListViewEnabled() {
+  return base::FeatureList::IsEnabled(kNewMessageListView);
+}
+
 bool IsNewWallpaperPickerEnabled() {
   static bool use_new_wallpaper_picker =
       base::FeatureList::IsEnabled(kNewWallpaperPicker);
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index a290c07..8d8891d 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -48,6 +48,9 @@
 ASH_PUBLIC_EXPORT extern const base::Feature
     kLockScreenHideSensitiveNotificationsSupport;
 
+// Enables new message list view. https://crbug.com/769219
+ASH_PUBLIC_EXPORT extern const base::Feature kNewMessageListView;
+
 // Enables the new wallpaper picker.
 // TODO(wzang): Remove this after the feature is fully launched.
 // https://crbug.com/777293.
@@ -91,6 +94,8 @@
 
 ASH_PUBLIC_EXPORT bool IsLockScreenHideSensitiveNotificationsSupported();
 
+ASH_PUBLIC_EXPORT bool IsNewMessageListViewEnabled();
+
 ASH_PUBLIC_EXPORT bool IsNewWallpaperPickerEnabled();
 
 ASH_PUBLIC_EXPORT bool IsNightLightEnabled();
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 0f91ddd..55ac89de 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -31,6 +31,8 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/model/virtual_keyboard_model.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/root_window_finder.h"
@@ -351,6 +353,7 @@
   DCHECK(shelf_);
   DCHECK(shelf_widget_);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
+  Shell::Get()->system_tray_model()->virtual_keyboard()->AddObserver(this);
   bounds_animator_->AddObserver(this);
   set_context_menu_controller(this);
 }
@@ -359,6 +362,7 @@
   // Shell destroys the TabletModeController before destroying all root windows.
   if (Shell::Get()->tablet_mode_controller())
     Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
+  Shell::Get()->system_tray_model()->virtual_keyboard()->RemoveObserver(this);
   bounds_animator_->RemoveObserver(this);
   model_->RemoveObserver(this);
 }
@@ -651,6 +655,10 @@
     shelf_menu_model_adapter_->Cancel();
 }
 
+void ShelfView::OnVirtualKeyboardVisibilityChanged() {
+  LayoutToIdealBounds();
+}
+
 void ShelfView::CreateDragIconProxyByLocationWithNoAnimation(
     const gfx::Point& origin_in_screen_coordinates,
     const gfx::ImageSkia& icon,
@@ -990,10 +998,13 @@
 
   const int available_size = shelf_->PrimaryAxisValue(width(), height());
   const int separator_index = GetSeparatorIndex();
+  const bool virtual_keyboard_visible =
+      Shell::Get()->system_tray_model()->virtual_keyboard()->visible();
   // Don't show the separator if it isn't needed, or would appear after all
   // visible items.
   separator_->SetVisible(separator_index != -1 &&
-                         separator_index < last_visible_index_);
+                         separator_index < last_visible_index_ &&
+                         !virtual_keyboard_visible);
   int app_list_button_position;
 
   int x = 0;
@@ -1097,7 +1108,13 @@
     // FinalizeRipOffDrag().
     if (dragged_off_shelf_ && view_model_->view_at(i) == drag_view_)
       continue;
-    view_model_->view_at(i)->SetVisible(i <= last_visible_index_);
+    // If virtual keyboard is visible, only back button and app list button are
+    // shown.
+    const bool is_visible_item = !virtual_keyboard_visible ||
+                                 i == kBackButtonIndex ||
+                                 i == kAppListButtonIndex;
+    view_model_->view_at(i)->SetVisible(i <= last_visible_index_ &&
+                                        is_visible_item);
   }
 
   overflow_button_->SetVisible(show_overflow);
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 81febaa..ee22859 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -16,6 +16,7 @@
 #include "ash/shelf/ink_drop_button_listener.h"
 #include "ash/shelf/shelf_button_pressed_metric_tracker.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
+#include "ash/system/model/virtual_keyboard_model.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -112,7 +113,8 @@
                              public views::FocusTraversable,
                              public views::BoundsAnimatorObserver,
                              public app_list::ApplicationDragAndDropHost,
-                             public ash::TabletModeObserver {
+                             public ash::TabletModeObserver,
+                             public VirtualKeyboardModel::Observer {
  public:
   ShelfView(ShelfModel* model, Shelf* shelf, ShelfWidget* shelf_widget);
   ~ShelfView() override;
@@ -189,6 +191,9 @@
   void OnTabletModeStarted() override;
   void OnTabletModeEnded() override;
 
+  // Overridden from VirtualKeyboardModel::Observer:
+  void OnVirtualKeyboardVisibilityChanged() override;
+
   void CreateDragIconProxyByLocationWithNoAnimation(
       const gfx::Point& origin_in_screen_coordinates,
       const gfx::ImageSkia& icon,
diff --git a/ash/shell.cc b/ash/shell.cc
index ba19a96..113f7a4b 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -423,9 +423,21 @@
 void Shell::InitWaylandServer(std::unique_ptr<exo::FileHelper> file_helper) {
   wayland_server_controller_ = WaylandServerController::CreateIfNecessary(
       std::move(file_helper), aura_env_);
+  if (wayland_server_controller_) {
+    system_tray_model()
+        ->virtual_keyboard()
+        ->SetInputMethodSurfaceManagerObserver(
+            wayland_server_controller_->arc_input_method_surface_manager());
+  }
 }
 
 void Shell::DestroyWaylandServer() {
+  if (wayland_server_controller_) {
+    system_tray_model()
+        ->virtual_keyboard()
+        ->RemoveInputMethodSurfaceManagerObserver(
+            wayland_server_controller_->arc_input_method_surface_manager());
+  }
   wayland_server_controller_.reset();
 }
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 2ec45ce..be674e9d 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -343,6 +343,10 @@
   SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier();
   tray_notifier->AddIMEObserver(this);
   tray_notifier->AddVirtualKeyboardObserver(this);
+
+  // Show the tray even if virtual keyboard is shown. (Other tray buttons will
+  // be hidden).
+  set_show_with_virtual_keyboard(true);
 }
 
 ImeMenuTray::~ImeMenuTray() {
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc
index fe064d7..fa3a1b52d 100644
--- a/ash/system/message_center/arc/arc_notification_content_view.cc
+++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -683,7 +683,7 @@
     canvas->DrawImageInt(
         item_->GetSnapshot(), 0, 0, item_->GetSnapshot().width(),
         item_->GetSnapshot().height(), contents_bounds.x(), contents_bounds.y(),
-        contents_bounds.width(), contents_bounds.height(), false);
+        contents_bounds.width(), contents_bounds.height(), true /* filter */);
   } else {
     // Draw a blank background otherwise. The height of the view and surface are
     // not exactly synced and user may see the blank area out of the surface.
diff --git a/ash/system/message_center/new_unified_message_center_view.cc b/ash/system/message_center/new_unified_message_center_view.cc
new file mode 100644
index 0000000..24089f5f
--- /dev/null
+++ b/ash/system/message_center/new_unified_message_center_view.cc
@@ -0,0 +1,51 @@
+// 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 "ash/system/message_center/new_unified_message_center_view.h"
+
+#include "ash/message_center/message_center_scroll_bar.h"
+#include "ash/system/message_center/unified_message_list_view.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+NewUnifiedMessageCenterView::NewUnifiedMessageCenterView()
+    : scroller_(new views::ScrollView()),
+      message_list_view_(new UnifiedMessageListView(this)) {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  // Need to set the transparent background explicitly, since ScrollView has
+  // set the default opaque background color.
+  scroller_->SetContents(message_list_view_);
+  scroller_->SetBackgroundColor(SK_ColorTRANSPARENT);
+  scroller_->SetVerticalScrollBar(new MessageCenterScrollBar(this));
+  scroller_->set_draw_overflow_indicator(false);
+  AddChildView(scroller_);
+}
+
+NewUnifiedMessageCenterView::~NewUnifiedMessageCenterView() = default;
+
+void NewUnifiedMessageCenterView::SetMaxHeight(int max_height) {
+  scroller_->ClipHeightTo(0, max_height);
+}
+
+void NewUnifiedMessageCenterView::ListPreferredSizeChanged() {
+  SetVisible(message_list_view_->child_count() > 0);
+  PreferredSizeChanged();
+
+  if (GetWidget())
+    GetWidget()->SynthesizeMouseMoveEvent();
+}
+
+void NewUnifiedMessageCenterView::ConfigureMessageView(
+    message_center::MessageView* message_view) {
+  message_view->set_scroller(scroller_);
+}
+
+void NewUnifiedMessageCenterView::OnMessageCenterScrolled() {}
+
+}  // namespace ash
diff --git a/ash/system/message_center/new_unified_message_center_view.h b/ash/system/message_center/new_unified_message_center_view.h
new file mode 100644
index 0000000..284b6343
--- /dev/null
+++ b/ash/system/message_center/new_unified_message_center_view.h
@@ -0,0 +1,54 @@
+// 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_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/message_center/message_center_scroll_bar.h"
+#include "ash/system/message_center/unified_message_list_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+class ScrollView;
+
+}  // namespace views
+
+namespace ash {
+
+// Manages scrolling of notification list.
+// TODO(tetsui): Rename to UnifiedMessageCenterView after old code is removed.
+class ASH_EXPORT NewUnifiedMessageCenterView
+    : public views::View,
+      public MessageCenterScrollBar::Observer {
+ public:
+  NewUnifiedMessageCenterView();
+  ~NewUnifiedMessageCenterView() override;
+
+  // Sets the maximum height that the view can take.
+  void SetMaxHeight(int max_height);
+
+  // Called from UnifiedMessageListView when the preferred size is changed.
+  void ListPreferredSizeChanged();
+
+  // Configures MessageView to forward scroll events. Called from
+  // UnifiedMessageListView.
+  void ConfigureMessageView(message_center::MessageView* message_view);
+
+  // MessageCenterScrollBar::Observer:
+  void OnMessageCenterScrolled() override;
+
+ private:
+  views::ScrollView* const scroller_;
+  UnifiedMessageListView* const message_list_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(NewUnifiedMessageCenterView);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
diff --git a/ash/system/unified/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc
similarity index 99%
rename from ash/system/unified/unified_message_center_view.cc
rename to ash/system/message_center/unified_message_center_view.cc
index 02fd7fc..53ba0746 100644
--- a/ash/system/unified/unified_message_center_view.cc
+++ b/ash/system/message_center/unified_message_center_view.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/unified/unified_message_center_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 
 #include "ash/message_center/message_center_scroll_bar.h"
 #include "ash/strings/grit/ash_strings.h"
diff --git a/ash/system/unified/unified_message_center_view.h b/ash/system/message_center/unified_message_center_view.h
similarity index 95%
rename from ash/system/unified/unified_message_center_view.h
rename to ash/system/message_center/unified_message_center_view.h
index b6ca272..433992c 100644
--- a/ash/system/unified/unified_message_center_view.h
+++ b/ash/system/message_center/unified_message_center_view.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 ASH_SYSTEM_UNIFIED_UNIFIED_MESSAGE_CENTER_VIEW_H_
-#define ASH_SYSTEM_UNIFIED_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
 
 #include <stddef.h>
 
@@ -137,4 +137,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_UNIFIED_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
new file mode 100644
index 0000000..c3d85df
--- /dev/null
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -0,0 +1,121 @@
+// 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 "ash/system/message_center/unified_message_list_view.h"
+
+#include "ash/system/message_center/new_unified_message_center_view.h"
+#include "base/auto_reset.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/message_center/views/message_view_factory.h"
+#include "ui/views/border.h"
+#include "ui/views/layout/box_layout.h"
+
+using message_center::Notification;
+using message_center::MessageCenter;
+using message_center::MessageView;
+
+namespace ash {
+
+UnifiedMessageListView::UnifiedMessageListView(
+    NewUnifiedMessageCenterView* message_center_view)
+    : message_center_view_(message_center_view) {
+  MessageCenter::Get()->AddObserver(this);
+
+  SetLayoutManager(
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
+
+  bool is_latest = true;
+  for (auto* notification : MessageCenter::Get()->GetVisibleNotifications()) {
+    auto* view = CreateMessageView(*notification);
+    // Expand the latest notification, and collapse all other notifications.
+    view->SetExpanded(is_latest && view->IsAutoExpandingAllowed());
+    is_latest = false;
+    AddChildViewAt(view, 0);
+    MessageCenter::Get()->DisplayedNotification(
+        notification->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER);
+  }
+}
+
+UnifiedMessageListView::~UnifiedMessageListView() {
+  MessageCenter::Get()->RemoveObserver(this);
+}
+
+void UnifiedMessageListView::ChildPreferredSizeChanged(views::View* child) {
+  if (ignore_size_change_)
+    return;
+  PreferredSizeChanged();
+}
+
+void UnifiedMessageListView::PreferredSizeChanged() {
+  views::View::PreferredSizeChanged();
+  if (message_center_view_)
+    message_center_view_->ListPreferredSizeChanged();
+}
+
+void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
+  auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
+  if (!notification)
+    return;
+
+  // Collapse all notifications before adding new one.
+  CollapseAllNotifications();
+
+  auto* view = CreateMessageView(*notification);
+  // Expand the latest notification.
+  view->SetExpanded(view->IsAutoExpandingAllowed());
+  AddChildView(view);
+  PreferredSizeChanged();
+}
+
+void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
+                                                   bool by_user) {
+  for (int i = 0; i < child_count(); ++i) {
+    auto* view = static_cast<message_center::MessageView*>(child_at(i));
+    if (view->notification_id() == id) {
+      delete view;
+      break;
+    }
+  }
+
+  PreferredSizeChanged();
+}
+
+void UnifiedMessageListView::OnNotificationUpdated(const std::string& id) {
+  auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
+  if (!notification)
+    return;
+
+  for (int i = 0; i < child_count(); ++i) {
+    auto* view = static_cast<message_center::MessageView*>(child_at(i));
+    if (view->notification_id() == id) {
+      view->UpdateWithNotification(*notification);
+      break;
+    }
+  }
+
+  PreferredSizeChanged();
+}
+
+message_center::MessageView* UnifiedMessageListView::CreateMessageView(
+    const Notification& notification) const {
+  auto* view = message_center::MessageViewFactory::Create(notification);
+  view->SetIsNested();
+  view->UpdateCornerRadius(0, 0);
+  view->SetBorder(views::NullBorder());
+  if (message_center_view_)
+    message_center_view_->ConfigureMessageView(view);
+  return view;
+}
+
+void UnifiedMessageListView::CollapseAllNotifications() {
+  base::AutoReset<bool> auto_reset(&ignore_size_change_, true);
+  for (int i = 0; i < child_count(); ++i) {
+    auto* view = static_cast<message_center::MessageView*>(child_at(i));
+    if (!view->IsManuallyExpandedOrCollapsed())
+      view->SetExpanded(false);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/message_center/unified_message_list_view.h b/ash/system/message_center/unified_message_list_view.h
new file mode 100644
index 0000000..3b4a0d1
--- /dev/null
+++ b/ash/system/message_center/unified_message_list_view.h
@@ -0,0 +1,58 @@
+// 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_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ui/message_center/message_center_observer.h"
+#include "ui/views/view.h"
+
+namespace message_center {
+class MessageView;
+class Notification;
+}  // namespace message_center
+
+namespace ash {
+
+class NewUnifiedMessageCenterView;
+
+// Manages list of notifications. The class doesn't know about the ScrollView
+// it's enclosed. This class is used only from NewUnifiedMessageCenterView.
+class ASH_EXPORT UnifiedMessageListView
+    : public views::View,
+      public message_center::MessageCenterObserver {
+ public:
+  // |message_center_view| can be null in unit tests.
+  explicit UnifiedMessageListView(
+      NewUnifiedMessageCenterView* message_center_view);
+  ~UnifiedMessageListView() override;
+
+  // views::View:
+  void ChildPreferredSizeChanged(views::View* child) override;
+  void PreferredSizeChanged() override;
+
+  // message_center::MessageCenterObserver:
+  void OnNotificationAdded(const std::string& id) override;
+  void OnNotificationRemoved(const std::string& id, bool by_user) override;
+  void OnNotificationUpdated(const std::string& id) override;
+
+ private:
+  message_center::MessageView* CreateMessageView(
+      const message_center::Notification& notification) const;
+  void CollapseAllNotifications();
+
+  NewUnifiedMessageCenterView* const message_center_view_;
+
+  // If true, ChildPreferredSizeChanged() will be ignored. This is used in
+  // CollapseAllNotifications() to prevent PreferredSizeChanged() triggered
+  // multiple times because of sequential SetExpanded() calls.
+  bool ignore_size_change_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListView);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_LIST_VIEW_H_
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/message_center/unified_message_list_view_unittest.cc
new file mode 100644
index 0000000..25e4185
--- /dev/null
+++ b/ash/system/message_center/unified_message_list_view_unittest.cc
@@ -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.
+
+#include "ash/system/message_center/unified_message_list_view.h"
+
+#include "ash/test/ash_test_base.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/views/message_view.h"
+
+using message_center::MessageCenter;
+using message_center::MessageView;
+using message_center::Notification;
+
+namespace ash {
+
+class UnifiedMessageListViewTest : public AshTestBase,
+                                   public views::ViewObserver {
+ public:
+  UnifiedMessageListViewTest() = default;
+  ~UnifiedMessageListViewTest() override = default;
+
+  void TearDown() override {
+    message_list_view_.reset();
+    AshTestBase::TearDown();
+  }
+
+  // views::ViewObserver:
+  void OnViewPreferredSizeChanged(views::View* view) override {
+    view->SetBoundsRect(gfx::Rect(view->GetPreferredSize()));
+    view->Layout();
+    ++size_changed_count_;
+  }
+
+ protected:
+  std::string AddNotification() {
+    std::string id = base::IntToString(id_++);
+    MessageCenter::Get()->AddNotification(std::make_unique<Notification>(
+        message_center::NOTIFICATION_TYPE_BASE_FORMAT, id,
+        base::UTF8ToUTF16("test title"), base::UTF8ToUTF16("test message"),
+        gfx::Image(), base::string16() /* display_source */, GURL(),
+        message_center::NotifierId(), message_center::RichNotificationData(),
+        new message_center::NotificationDelegate()));
+    return id;
+  }
+
+  void CreateMessageListView() {
+    message_list_view_ = std::make_unique<UnifiedMessageListView>(nullptr);
+    message_list_view_->AddObserver(this);
+    OnViewPreferredSizeChanged(message_list_view_.get());
+    size_changed_count_ = 0;
+  }
+
+  MessageView* GetMessageViewAt(int index) const {
+    return static_cast<MessageView*>(message_list_view()->child_at(index));
+  }
+
+  UnifiedMessageListView* message_list_view() const {
+    return message_list_view_.get();
+  }
+
+  int size_changed_count() const { return size_changed_count_; }
+
+ private:
+  int id_ = 0;
+  int size_changed_count_ = 0;
+
+  std::unique_ptr<UnifiedMessageListView> message_list_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnifiedMessageListViewTest);
+};
+
+TEST_F(UnifiedMessageListViewTest, Open) {
+  auto id0 = AddNotification();
+  auto id1 = AddNotification();
+  auto id2 = AddNotification();
+  CreateMessageListView();
+
+  EXPECT_EQ(3, message_list_view()->child_count());
+  EXPECT_EQ(id0, GetMessageViewAt(0)->notification_id());
+  EXPECT_EQ(id1, GetMessageViewAt(1)->notification_id());
+  EXPECT_EQ(id2, GetMessageViewAt(2)->notification_id());
+
+  EXPECT_FALSE(GetMessageViewAt(0)->IsExpanded());
+  EXPECT_FALSE(GetMessageViewAt(1)->IsExpanded());
+  EXPECT_TRUE(GetMessageViewAt(2)->IsExpanded());
+
+  EXPECT_EQ(GetMessageViewAt(0)->bounds().bottom(),
+            GetMessageViewAt(1)->bounds().y());
+  EXPECT_EQ(GetMessageViewAt(1)->bounds().bottom(),
+            GetMessageViewAt(2)->bounds().y());
+
+  EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
+}
+
+TEST_F(UnifiedMessageListViewTest, AddNotifications) {
+  CreateMessageListView();
+  EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
+
+  auto id0 = AddNotification();
+  EXPECT_EQ(1, size_changed_count());
+  EXPECT_EQ(1, message_list_view()->child_count());
+  EXPECT_EQ(id0, GetMessageViewAt(0)->notification_id());
+
+  int previous_height = message_list_view()->GetPreferredSize().height();
+  EXPECT_LT(0, previous_height);
+
+  gfx::Rect previous_bounds = GetMessageViewAt(0)->bounds();
+
+  auto id1 = AddNotification();
+  EXPECT_EQ(2, size_changed_count());
+  EXPECT_EQ(2, message_list_view()->child_count());
+  EXPECT_EQ(id1, GetMessageViewAt(1)->notification_id());
+
+  EXPECT_LT(previous_height, message_list_view()->GetPreferredSize().height());
+  EXPECT_EQ(previous_bounds, GetMessageViewAt(0)->bounds());
+  EXPECT_EQ(GetMessageViewAt(0)->bounds().bottom(),
+            GetMessageViewAt(1)->bounds().y());
+}
+
+TEST_F(UnifiedMessageListViewTest, RemoveNotification) {
+  auto id0 = AddNotification();
+  auto id1 = AddNotification();
+
+  CreateMessageListView();
+  int previous_height = message_list_view()->GetPreferredSize().height();
+
+  gfx::Rect previous_bounds = GetMessageViewAt(0)->bounds();
+  MessageCenter::Get()->RemoveNotification(id0, true /* by_user */);
+  EXPECT_EQ(1, size_changed_count());
+  EXPECT_EQ(previous_bounds.y(), GetMessageViewAt(0)->bounds().y());
+  EXPECT_LT(0, message_list_view()->GetPreferredSize().height());
+  EXPECT_GT(previous_height, message_list_view()->GetPreferredSize().height());
+
+  MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
+  EXPECT_EQ(2, size_changed_count());
+  EXPECT_EQ(0, message_list_view()->GetPreferredSize().height());
+}
+
+TEST_F(UnifiedMessageListViewTest, CollapseOlderNotifications) {
+  AddNotification();
+  CreateMessageListView();
+  EXPECT_TRUE(GetMessageViewAt(0)->IsExpanded());
+
+  AddNotification();
+  EXPECT_FALSE(GetMessageViewAt(0)->IsExpanded());
+  EXPECT_TRUE(GetMessageViewAt(1)->IsExpanded());
+
+  AddNotification();
+  EXPECT_FALSE(GetMessageViewAt(0)->IsExpanded());
+  EXPECT_FALSE(GetMessageViewAt(1)->IsExpanded());
+  EXPECT_TRUE(GetMessageViewAt(2)->IsExpanded());
+
+  GetMessageViewAt(1)->SetExpanded(true);
+  GetMessageViewAt(1)->SetManuallyExpandedOrCollapsed(true);
+
+  AddNotification();
+  EXPECT_FALSE(GetMessageViewAt(0)->IsExpanded());
+  EXPECT_TRUE(GetMessageViewAt(1)->IsExpanded());
+  EXPECT_FALSE(GetMessageViewAt(2)->IsExpanded());
+  EXPECT_TRUE(GetMessageViewAt(3)->IsExpanded());
+}
+
+}  // namespace ash
diff --git a/ash/system/model/system_tray_model.cc b/ash/system/model/system_tray_model.cc
index 48feced0..66b4b4fc 100644
--- a/ash/system/model/system_tray_model.cc
+++ b/ash/system/model/system_tray_model.cc
@@ -13,6 +13,7 @@
 #include "ash/system/model/session_length_limit_model.h"
 #include "ash/system/model/tracing_model.h"
 #include "ash/system/model/update_model.h"
+#include "ash/system/model/virtual_keyboard_model.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/unified/unified_system_tray.h"
@@ -25,7 +26,8 @@
       enterprise_domain_(std::make_unique<EnterpriseDomainModel>()),
       session_length_limit_(std::make_unique<SessionLengthLimitModel>()),
       tracing_(std::make_unique<TracingModel>()),
-      update_model_(std::make_unique<UpdateModel>()) {}
+      update_model_(std::make_unique<UpdateModel>()),
+      virtual_keyboard_(std::make_unique<VirtualKeyboardModel>()) {}
 
 SystemTrayModel::~SystemTrayModel() = default;
 
diff --git a/ash/system/model/system_tray_model.h b/ash/system/model/system_tray_model.h
index 32509bd4..de9525433 100644
--- a/ash/system/model/system_tray_model.h
+++ b/ash/system/model/system_tray_model.h
@@ -18,6 +18,7 @@
 class SessionLengthLimitModel;
 class TracingModel;
 class UpdateModel;
+class VirtualKeyboardModel;
 
 // Top level model of SystemTray.
 class SystemTrayModel : public mojom::SystemTray {
@@ -56,6 +57,7 @@
   }
   TracingModel* tracing() { return tracing_.get(); }
   UpdateModel* update_model() { return update_model_.get(); }
+  VirtualKeyboardModel* virtual_keyboard() { return virtual_keyboard_.get(); }
 
   const mojom::SystemTrayClientPtr& client_ptr() { return client_ptr_; }
 
@@ -65,6 +67,7 @@
   std::unique_ptr<SessionLengthLimitModel> session_length_limit_;
   std::unique_ptr<TracingModel> tracing_;
   std::unique_ptr<UpdateModel> update_model_;
+  std::unique_ptr<VirtualKeyboardModel> virtual_keyboard_;
 
   // TODO(tetsui): Add following as a sub-model of SystemTrayModel:
   // * BluetoothModel
diff --git a/ash/system/model/virtual_keyboard_model.cc b/ash/system/model/virtual_keyboard_model.cc
new file mode 100644
index 0000000..084aa5e
--- /dev/null
+++ b/ash/system/model/virtual_keyboard_model.cc
@@ -0,0 +1,48 @@
+// 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 "ash/system/model/virtual_keyboard_model.h"
+
+namespace ash {
+
+VirtualKeyboardModel::VirtualKeyboardModel() = default;
+VirtualKeyboardModel::~VirtualKeyboardModel() = default;
+
+void VirtualKeyboardModel::AddObserver(
+    VirtualKeyboardModel::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void VirtualKeyboardModel::RemoveObserver(
+    VirtualKeyboardModel::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void VirtualKeyboardModel::SetInputMethodSurfaceManagerObserver(
+    ArcInputMethodSurfaceManager* input_method_surface_manager) {
+  DCHECK(input_method_surface_manager);
+  input_method_surface_manager->AddObserver(this);
+}
+
+void VirtualKeyboardModel::RemoveInputMethodSurfaceManagerObserver(
+    ArcInputMethodSurfaceManager* input_method_surface_manager) {
+  DCHECK(input_method_surface_manager);
+  input_method_surface_manager->RemoveObserver(this);
+}
+
+void VirtualKeyboardModel::OnArcInputMethodSurfaceBoundsChanged(
+    const gfx::Rect& bounds) {
+  const bool new_visible = !bounds.IsEmpty();
+  if (visible_ == new_visible)
+    return;
+  visible_ = new_visible;
+  NotifyChanged();
+}
+
+void VirtualKeyboardModel::NotifyChanged() {
+  for (auto& observer : observers_)
+    observer.OnVirtualKeyboardVisibilityChanged();
+}
+
+}  // namespace ash
diff --git a/ash/system/model/virtual_keyboard_model.h b/ash/system/model/virtual_keyboard_model.h
new file mode 100644
index 0000000..91a7f82
--- /dev/null
+++ b/ash/system/model/virtual_keyboard_model.h
@@ -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.
+
+#ifndef ASH_SYSTEM_MODEL_VIRTUAL_KEYBOARD_MODEL_H_
+#define ASH_SYSTEM_MODEL_VIRTUAL_KEYBOARD_MODEL_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/keyboard/arc/arc_input_method_surface_manager.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+
+namespace ash {
+
+class ArcInputMethodSurfaceManager;
+
+// Model to store virtual keyboard visibility state.
+class ASH_EXPORT VirtualKeyboardModel
+    : public ArcInputMethodSurfaceManager::Observer {
+ public:
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    virtual void OnVirtualKeyboardVisibilityChanged() = 0;
+  };
+
+  VirtualKeyboardModel();
+  ~VirtualKeyboardModel() override;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Start/stop observing ArcInputMethodSurfaceManager.
+  void SetInputMethodSurfaceManagerObserver(
+      ArcInputMethodSurfaceManager* input_method_surface_manager);
+  void RemoveInputMethodSurfaceManagerObserver(
+      ArcInputMethodSurfaceManager* input_method_surface_manager);
+
+  // ArcInputMethodSurfaceManager::Observer:
+  void OnArcInputMethodSurfaceBoundsChanged(const gfx::Rect& bounds) override;
+
+  bool visible() const { return visible_; }
+
+ private:
+  void NotifyChanged();
+
+  // The visibility of virtual keyboard.
+  bool visible_ = false;
+
+  base::ObserverList<Observer>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardModel);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_MODEL_VIRTUAL_KEYBOARD_MODEL_H_
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index bb18d0b..ec4ca5c 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -96,6 +96,8 @@
   if (notification_tray_) {
     system_tray_->InitializeTrayItems(notification_tray_.get());
     notification_tray_->Initialize();
+  } else {
+    unified_system_tray_->Initialize();
   }
   palette_tray_->Initialize();
   virtual_keyboard_tray_->Initialize();
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 343ea0b..1e1cf35 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -17,6 +17,7 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/model/system_tray_model.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
@@ -177,6 +178,8 @@
       background_(new TrayBackground(this)),
       is_active_(false),
       separator_visible_(true),
+      visible_preferred_(false),
+      show_with_virtual_keyboard_(false),
       widget_observer_(new TrayWidgetObserver(this)) {
   DCHECK(shelf_);
   set_notify_enter_exit_on_child(true);
@@ -197,6 +200,7 @@
 }
 
 TrayBackgroundView::~TrayBackgroundView() {
+  Shell::Get()->system_tray_model()->virtual_keyboard()->RemoveObserver(this);
   if (GetWidget())
     GetWidget()->RemoveObserver(widget_observer_.get());
   StopObservingImplicitAnimations();
@@ -204,6 +208,7 @@
 
 void TrayBackgroundView::Initialize() {
   GetWidget()->AddObserver(widget_observer_.get());
+  Shell::Get()->system_tray_model()->virtual_keyboard()->AddObserver(this);
 }
 
 // static
@@ -218,6 +223,16 @@
 }
 
 void TrayBackgroundView::SetVisible(bool visible) {
+  visible_preferred_ = visible;
+
+  // If virtual keyboard is visible and TrayBackgroundView is hidden because of
+  // that, ignore SetVisible() call. |visible_preferred_|  will be restored
+  // in OnVirtualKeyboardVisibilityChanged() when virtual keyboard is hidden.
+  if (!show_with_virtual_keyboard_ &&
+      Shell::Get()->system_tray_model()->virtual_keyboard()->visible()) {
+    return;
+  }
+
   if (visible == layer()->GetTargetVisibility())
     return;
 
@@ -393,6 +408,18 @@
     drag_controller_->ProcessGestureEvent(event, this);
 }
 
+void TrayBackgroundView::OnVirtualKeyboardVisibilityChanged() {
+  if (show_with_virtual_keyboard_)
+    return;
+
+  // If virtual keyboard is hidden and current preferred visibility is true,
+  // set the visibility to true. We call base class' SetVisible because we don't
+  // want |visible_preferred_| to be updated here.
+  views::View::SetVisible(
+      !Shell::Get()->system_tray_model()->virtual_keyboard()->visible() &&
+      visible_preferred_);
+}
+
 TrayBubbleView* TrayBackgroundView::GetBubbleView() {
   return nullptr;
 }
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index 7b26151..8827124 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -9,6 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/shelf/shelf_background_animator_observer.h"
+#include "ash/system/model/virtual_keyboard_model.h"
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/tray_bubble_view.h"
 #include "ash/system/tray_drag_controller.h"
@@ -29,7 +30,8 @@
 class ASH_EXPORT TrayBackgroundView : public ActionableView,
                                       public ui::ImplicitAnimationObserver,
                                       public ShelfBackgroundAnimatorObserver,
-                                      public TrayBubbleView::Delegate {
+                                      public TrayBubbleView::Delegate,
+                                      public VirtualKeyboardModel::Observer {
  public:
   static const char kViewClassName[];
 
@@ -60,6 +62,9 @@
   // TrayBubbleView::Delegate:
   void ProcessGestureEventForBubble(ui::GestureEvent* event) override;
 
+  // VirtualKeyboardModel::Observer:
+  void OnVirtualKeyboardVisibilityChanged() override;
+
   // Returns the associated tray bubble view, if one exists. Otherwise returns
   // nullptr.
   virtual TrayBubbleView* GetBubbleView();
@@ -156,6 +161,10 @@
     drag_controller_ = std::move(drag_controller);
   }
 
+  void set_show_with_virtual_keyboard(bool show_with_virtual_keyboard) {
+    show_with_virtual_keyboard_ = show_with_virtual_keyboard;
+  }
+
  private:
   class TrayWidgetObserver;
 
@@ -188,6 +197,14 @@
   // right of tray.
   bool separator_visible_;
 
+  // During virtual keyboard is shown, visibility changes to TrayBackgroundView
+  // are ignored. In such case, preferred visibility is reflected after the
+  // virtual keyboard is hidden.
+  bool visible_preferred_;
+
+  // If true, ignores virtual keyboard visibility changes.
+  bool show_with_virtual_keyboard_;
+
   // Handles touch drag gestures on the tray area and its associated bubble.
   std::unique_ptr<TrayDragController> drag_controller_;
 
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 7f6d63f9..c25f867 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -9,13 +9,14 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/new_unified_message_center_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/tray/interacted_by_tap_recorder.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/feature_pods_container_view.h"
 #include "ash/system/unified/notification_hidden_view.h"
 #include "ash/system/unified/top_shortcuts_view.h"
-#include "ash/system/unified/unified_message_center_view.h"
 #include "ash/system/unified/unified_system_info_view.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/unified/unified_system_tray_model.h"
@@ -220,10 +221,15 @@
       system_info_view_(new UnifiedSystemInfoView(controller_)),
       system_tray_container_(new SystemTrayContainer()),
       detailed_view_container_(new DetailedViewContainer()),
-      message_center_view_(
-          new UnifiedMessageCenterView(controller,
-                                       this,
-                                       message_center::MessageCenter::Get())),
+      message_center_view_(features::IsNewMessageListViewEnabled()
+                               ? nullptr
+                               : new UnifiedMessageCenterView(
+                                     controller,
+                                     this,
+                                     message_center::MessageCenter::Get())),
+      new_message_center_view_(features::IsNewMessageListViewEnabled()
+                                   ? new NewUnifiedMessageCenterView()
+                                   : nullptr),
       focus_search_(std::make_unique<FocusSearch>(this)),
       interacted_by_tap_recorder_(
           std::make_unique<InteractedByTapRecorder>(this)) {
@@ -238,11 +244,17 @@
 
   SessionController* session_controller = Shell::Get()->session_controller();
 
-  message_center_view_->SetVisible(
+  views::View* message_center_view;
+  if (features::IsNewMessageListViewEnabled())
+    message_center_view = new_message_center_view_;
+  else
+    message_center_view = message_center_view_;
+
+  message_center_view->SetVisible(
       session_controller->ShouldShowNotificationTray() &&
       !session_controller->IsScreenLocked());
-  AddChildView(message_center_view_);
-  layout->SetFlexForView(message_center_view_, 1);
+  AddChildView(message_center_view);
+  layout->SetFlexForView(message_center_view, 1);
 
   notification_hidden_view_->SetVisible(
       session_controller->GetUserSession(0) &&
@@ -265,8 +277,8 @@
   // |message_center_view_| next to |detailed_view_container_|.
   // Also, SetNextFocusableView does not support loop as mentioned in the doc,
   // we have to set null to |message_center_view_|.
-  message_center_view_->SetNextFocusableView(nullptr);
-  detailed_view_container_->SetNextFocusableView(message_center_view_);
+  message_center_view->SetNextFocusableView(nullptr);
+  detailed_view_container_->SetNextFocusableView(message_center_view);
 
   top_shortcuts_view_->SetExpandedAmount(expanded_amount_);
 }
@@ -274,7 +286,10 @@
 UnifiedSystemTrayView::~UnifiedSystemTrayView() = default;
 
 void UnifiedSystemTrayView::SetMaxHeight(int max_height) {
-  message_center_view_->SetMaxHeight(max_height);
+  if (message_center_view_)
+    message_center_view_->SetMaxHeight(max_height);
+  else if (new_message_center_view_)
+    new_message_center_view_->SetMaxHeight(max_height);
 }
 
 void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
@@ -355,11 +370,13 @@
   // TODO(tetsui): Support animation by transform even when
   // UnifiedMessageCenterview is visible.
   return expanded_amount_ != 0.0 && expanded_amount_ != 1.0 &&
-         !message_center_view_->visible();
+         (message_center_view_ ? !message_center_view_->visible()
+                               : !new_message_center_view_->visible());
 }
 
 void UnifiedSystemTrayView::ShowClearAllAnimation() {
-  message_center_view_->ShowClearAllAnimation();
+  if (message_center_view_)
+    message_center_view_->ShowClearAllAnimation();
 }
 
 void UnifiedSystemTrayView::SetNotificationHeightBelowScroll(
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index 33a8311..a3ac488 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -15,6 +15,7 @@
 class FeaturePodsContainerView;
 class TopShortcutsView;
 class UnifiedMessageCenterView;
+class NewUnifiedMessageCenterView;
 class UnifiedSystemInfoView;
 class UnifiedSystemTrayController;
 
@@ -127,6 +128,7 @@
   views::View* const system_tray_container_;
   views::View* const detailed_view_container_;
   UnifiedMessageCenterView* const message_center_view_;
+  NewUnifiedMessageCenterView* const new_message_center_view_;
 
   const std::unique_ptr<FocusSearch> focus_search_;
   const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
diff --git a/ash/wayland/wayland_server_controller.h b/ash/wayland/wayland_server_controller.h
index 75509fd..b14851eb 100644
--- a/ash/wayland/wayland_server_controller.h
+++ b/ash/wayland/wayland_server_controller.h
@@ -37,6 +37,10 @@
 
   ~WaylandServerController();
 
+  ArcInputMethodSurfaceManager* arc_input_method_surface_manager() {
+    return arc_input_method_surface_manager_.get();
+  }
+
  private:
   WaylandServerController(std::unique_ptr<exo::FileHelper> file_helper,
                           aura::Env* env);
diff --git a/base/command_line.cc b/base/command_line.cc
index aec89f5..7825952 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -371,7 +371,7 @@
   // Gather all arguments after the last switch (may include kSwitchTerminator).
   StringVector args(argv_.begin() + begin_args_, argv_.end());
   // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
-  StringVector::iterator switch_terminator =
+  auto switch_terminator =
       std::find(args.begin(), args.end(), kSwitchTerminator);
   if (switch_terminator != args.end())
     args.erase(switch_terminator);
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
index 3718cd9..93ccd7c 100644
--- a/base/command_line_unittest.cc
+++ b/base/command_line_unittest.cc
@@ -84,7 +84,7 @@
   const CommandLine::StringVector& args = cl.GetArgs();
   ASSERT_EQ(8U, args.size());
 
-  std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
+  auto iter = args.begin();
   EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
   ++iter;
   EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter);
diff --git a/base/containers/flat_tree_unittest.cc b/base/containers/flat_tree_unittest.cc
index 5b788d5..6482684a 100644
--- a/base/containers/flat_tree_unittest.cc
+++ b/base/containers/flat_tree_unittest.cc
@@ -613,8 +613,8 @@
   EXPECT_EQ(size, std::distance(cont.crbegin(), cont.crend()));
 
   {
-    IntTree::iterator it = cont.begin();
-    IntTree::const_iterator c_it = cont.cbegin();
+    auto it = cont.begin();
+    auto c_it = cont.cbegin();
     EXPECT_EQ(it, c_it);
     for (int j = 1; it != cont.end(); ++it, ++c_it, ++j) {
       EXPECT_EQ(j, *it);
@@ -622,8 +622,8 @@
     }
   }
   {
-    IntTree::reverse_iterator rit = cont.rbegin();
-    IntTree::const_reverse_iterator c_rit = cont.crbegin();
+    auto rit = cont.rbegin();
+    auto c_rit = cont.crbegin();
     EXPECT_EQ(rit, c_rit);
     for (int j = static_cast<int>(size); rit != cont.rend();
          ++rit, ++c_rit, --j) {
@@ -705,7 +705,7 @@
 TEST(FlatTree, InsertPositionLValue) {
   IntTree cont;
 
-  IntTree::iterator result = cont.insert(cont.cend(), 2);
+  auto result = cont.insert(cont.cend(), 2);
   EXPECT_EQ(cont.begin(), result);
   EXPECT_EQ(1U, cont.size());
   EXPECT_EQ(2, *result);
@@ -731,7 +731,7 @@
 TEST(FlatTree, InsertPositionRValue) {
   MoveOnlyTree cont;
 
-  MoveOnlyTree::iterator result = cont.insert(cont.cend(), MoveOnlyInt(2));
+  auto result = cont.insert(cont.cend(), MoveOnlyInt(2));
   EXPECT_EQ(cont.begin(), result);
   EXPECT_EQ(1U, cont.size());
   EXPECT_EQ(2, result->data());
@@ -893,7 +893,7 @@
   {
     EmplaceableTree cont;
 
-    EmplaceableTree::iterator result = cont.emplace_hint(cont.cend());
+    auto result = cont.emplace_hint(cont.cend());
     EXPECT_EQ(cont.begin(), result);
     EXPECT_EQ(1U, cont.size());
     EXPECT_EQ(Emplaceable(), *cont.begin());
@@ -911,7 +911,7 @@
   {
     IntTree cont;
 
-    IntTree::iterator result = cont.emplace_hint(cont.cend(), 2);
+    auto result = cont.emplace_hint(cont.cend(), 2);
     EXPECT_EQ(cont.begin(), result);
     EXPECT_EQ(1U, cont.size());
     EXPECT_EQ(2, *result);
@@ -927,7 +927,7 @@
   {
     IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
 
-    IntTree::iterator it = cont.erase(std::next(cont.cbegin(), 3));
+    auto it = cont.erase(std::next(cont.cbegin(), 3));
     EXPECT_EQ(std::next(cont.begin(), 3), it);
     EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
 
@@ -979,7 +979,7 @@
 TEST(FlatTree, EraseRange) {
   IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
 
-  IntTree::iterator it =
+  auto it =
       cont.erase(std::next(cont.cbegin(), 5), std::next(cont.cbegin(), 5));
   EXPECT_EQ(std::next(cont.begin(), 5), it);
   EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
diff --git a/base/containers/mru_cache_unittest.cc b/base/containers/mru_cache_unittest.cc
index 28e6f0d..07b0144 100644
--- a/base/containers/mru_cache_unittest.cc
+++ b/base/containers/mru_cache_unittest.cc
@@ -52,12 +52,12 @@
 
   static const int kItem1Key = 5;
   CachedItem item1(10);
-  Cache::iterator inserted_item = cache.Put(kItem1Key, item1);
+  auto inserted_item = cache.Put(kItem1Key, item1);
   EXPECT_EQ(1U, cache.size());
 
   // Check that item1 was properly inserted.
   {
-    Cache::iterator found = cache.Get(kItem1Key);
+    auto found = cache.Get(kItem1Key);
     EXPECT_TRUE(inserted_item == cache.begin());
     EXPECT_TRUE(found != cache.end());
 
@@ -75,7 +75,7 @@
 
   // Check that item1 is the oldest since item2 was added afterwards.
   {
-    Cache::reverse_iterator oldest = cache.rbegin();
+    auto oldest = cache.rbegin();
     ASSERT_TRUE(oldest != cache.rend());
     EXPECT_EQ(kItem1Key, oldest->first);
     EXPECT_EQ(item1.value, oldest->second.value);
@@ -83,7 +83,7 @@
 
   // Check that item1 is still accessible by key.
   {
-    Cache::iterator test_item = cache.Get(kItem1Key);
+    auto test_item = cache.Get(kItem1Key);
     ASSERT_TRUE(test_item != cache.end());
     EXPECT_EQ(kItem1Key, test_item->first);
     EXPECT_EQ(item1.value, test_item->second.value);
@@ -91,7 +91,7 @@
 
   // Check that retrieving item1 pushed item2 to oldest.
   {
-    Cache::reverse_iterator oldest = cache.rbegin();
+    auto oldest = cache.rbegin();
     ASSERT_TRUE(oldest != cache.rend());
     EXPECT_EQ(kItem2Key, oldest->first);
     EXPECT_EQ(item2.value, oldest->second.value);
@@ -99,7 +99,7 @@
 
   // Remove the oldest item and check that item1 is now the only member.
   {
-    Cache::reverse_iterator next = cache.Erase(cache.rbegin());
+    auto next = cache.Erase(cache.rbegin());
 
     EXPECT_EQ(1U, cache.size());
 
@@ -136,7 +136,7 @@
 
   // Check that item1 starts out as oldest
   {
-    Cache::reverse_iterator iter = cache.rbegin();
+    auto iter = cache.rbegin();
     ASSERT_TRUE(iter != cache.rend());
     EXPECT_EQ(kItem1Key, iter->first);
     EXPECT_EQ(item1.value, iter->second.value);
@@ -144,10 +144,10 @@
 
   // Check that Peek doesn't change ordering
   {
-    Cache::iterator peekiter = cache.Peek(kItem1Key);
+    auto peekiter = cache.Peek(kItem1Key);
     ASSERT_TRUE(peekiter != cache.end());
 
-    Cache::reverse_iterator iter = cache.rbegin();
+    auto iter = cache.rbegin();
     ASSERT_TRUE(iter != cache.rend());
     EXPECT_EQ(kItem1Key, iter->first);
     EXPECT_EQ(item1.value, iter->second.value);
@@ -179,14 +179,14 @@
 
   EXPECT_EQ(4U, cache.size());
   for (int i = 0; i < 3; ++i) {
-    Cache::reverse_iterator iter = cache.rbegin();
+    auto iter = cache.rbegin();
     ASSERT_TRUE(iter != cache.rend());
   }
 
   // Make it so only the most important element is there.
   cache.ShrinkToSize(1);
 
-  Cache::iterator iter = cache.begin();
+  auto iter = cache.begin();
   EXPECT_EQ(kItem3Key, iter->first);
   EXPECT_EQ(item5.value, iter->second.value);
 }
@@ -204,7 +204,7 @@
   cache.Put(kItem1Key, WrapUnique(new CachedItem(22)));
 
   // There should still be one item, and one extra live item.
-  Cache::iterator iter = cache.Get(kItem1Key);
+  auto iter = cache.Get(kItem1Key);
   EXPECT_EQ(1U, cache.size());
   EXPECT_TRUE(iter != cache.end());
   EXPECT_EQ(initial_count + 1, cached_item_live_count);
@@ -284,7 +284,7 @@
   // Insert two items into cache1.
   static const int kItem1Key = 1;
   CachedItem item1(2);
-  Cache::iterator inserted_item = cache1.Put(kItem1Key, item1);
+  auto inserted_item = cache1.Put(kItem1Key, item1);
   EXPECT_EQ(1U, cache1.size());
 
   static const int kItem2Key = 3;
@@ -294,7 +294,7 @@
 
   // Verify cache1's elements.
   {
-    Cache::iterator iter = cache1.begin();
+    auto iter = cache1.begin();
     ASSERT_TRUE(iter != cache1.end());
     EXPECT_EQ(kItem2Key, iter->first);
     EXPECT_EQ(item2.value, iter->second.value);
@@ -326,7 +326,7 @@
 
   // Verify cache2's elements.
   {
-    Cache::iterator iter = cache2.begin();
+    auto iter = cache2.begin();
     ASSERT_TRUE(iter != cache2.end());
     EXPECT_EQ(kItem5Key, iter->first);
     EXPECT_EQ(item5.value, iter->second.value);
@@ -351,7 +351,7 @@
 
   // Verify cache1's elements.
   {
-    Cache::iterator iter = cache1.begin();
+    auto iter = cache1.begin();
     ASSERT_TRUE(iter != cache1.end());
     EXPECT_EQ(kItem5Key, iter->first);
     EXPECT_EQ(item5.value, iter->second.value);
@@ -369,7 +369,7 @@
 
   // Verify cache2's elements.
   {
-    Cache::iterator iter = cache2.begin();
+    auto iter = cache2.begin();
     ASSERT_TRUE(iter != cache2.end());
     EXPECT_EQ(kItem2Key, iter->first);
     EXPECT_EQ(item2.value, iter->second.value);
diff --git a/base/containers/stack_container_unittest.cc b/base/containers/stack_container_unittest.cc
index b6bb9b6..e652cf9 100644
--- a/base/containers/stack_container_unittest.cc
+++ b/base/containers/stack_container_unittest.cc
@@ -79,7 +79,6 @@
 TEST(StackContainer, VectorDoubleDelete) {
   // Regression testing for double-delete.
   typedef StackVector<scoped_refptr<Dummy>, 2> Vector;
-  typedef Vector::ContainerType Container;
   Vector vect;
 
   int alive = 0;
@@ -93,7 +92,7 @@
   dummy = nullptr;
   EXPECT_EQ(alive, 1);
 
-  Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref);
+  auto itr = std::find(vect->begin(), vect->end(), dummy_unref);
   EXPECT_EQ(itr->get(), dummy_unref);
   vect->erase(itr);
   EXPECT_EQ(alive, 0);
diff --git a/base/deferred_sequenced_task_runner.cc b/base/deferred_sequenced_task_runner.cc
index f88170c..156369a 100644
--- a/base/deferred_sequenced_task_runner.cc
+++ b/base/deferred_sequenced_task_runner.cc
@@ -111,9 +111,8 @@
   DCHECK(!started_);
   started_ = true;
   DCHECK(target_task_runner_);
-  for (std::vector<DeferredTask>::iterator i = deferred_tasks_queue_.begin();
-      i != deferred_tasks_queue_.end();
-      ++i) {
+  for (auto i = deferred_tasks_queue_.begin(); i != deferred_tasks_queue_.end();
+       ++i) {
     DeferredTask& task = *i;
     if (task.is_non_nestable) {
       target_task_runner_->PostNonNestableDelayedTask(
diff --git a/base/environment.cc b/base/environment.cc
index cdea53c8..9eccc78 100644
--- a/base/environment.cc
+++ b/base/environment.cc
@@ -195,7 +195,7 @@
     size_t line_length = ParseEnvLine(env[i], &key);
 
     // Keep only values not specified in the change vector.
-    EnvironmentMap::const_iterator found_change = changes.find(key);
+    auto found_change = changes.find(key);
     if (found_change == changes.end()) {
       result_indices.push_back(value_storage.size());
       value_storage.append(env[i], line_length);
@@ -203,8 +203,7 @@
   }
 
   // Now append all modified and new values.
-  for (EnvironmentMap::const_iterator i = changes.begin();
-       i != changes.end(); ++i) {
+  for (auto i = changes.begin(); i != changes.end(); ++i) {
     if (!i->second.empty()) {
       result_indices.push_back(value_storage.size());
       value_storage.append(i->first);
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc
index 182a762..b22421d 100644
--- a/base/files/file_path_watcher_linux.cc
+++ b/base/files/file_path_watcher_linux.cc
@@ -551,9 +551,8 @@
       recursive_paths_by_watch_[fired_watch] :
       target_;
 
-  std::map<FilePath, InotifyReader::Watch>::iterator start_it =
-      recursive_watches_by_path_.lower_bound(changed_dir);
-  std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it;
+  auto start_it = recursive_watches_by_path_.lower_bound(changed_dir);
+  auto end_it = start_it;
   for (; end_it != recursive_watches_by_path_.end(); ++end_it) {
     const FilePath& cur_path = end_it->first;
     if (!changed_dir.IsParent(cur_path))
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index d0af7f3..53cc974 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -715,8 +715,7 @@
   }
 
   // Iterate through the parents and create the missing ones.
-  for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
-       i != subpaths.rend(); ++i) {
+  for (auto i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
     if (DirectoryExists(*i))
       continue;
     if (mkdir(i->value().c_str(), 0700) == 0)
diff --git a/base/i18n/build_utf8_validator_tables.cc b/base/i18n/build_utf8_validator_tables.cc
index 0cdcc35..b349c5f 100644
--- a/base/i18n/build_utf8_validator_tables.cc
+++ b/base/i18n/build_utf8_validator_tables.cc
@@ -263,12 +263,9 @@
 // algorithm is working. Use the command-line option
 // --vmodule=build_utf8_validator_tables=1 to see this output.
 void LogStringSets(const PairVector& pairs) {
-  for (PairVector::const_iterator pair_it = pairs.begin();
-       pair_it != pairs.end();
-       ++pair_it) {
+  for (auto pair_it = pairs.begin(); pair_it != pairs.end(); ++pair_it) {
     std::string set_as_string;
-    for (StringSet::const_iterator set_it = pair_it->set.begin();
-         set_it != pair_it->set.end();
+    for (auto set_it = pair_it->set.begin(); set_it != pair_it->set.end();
          ++set_it) {
       set_as_string += base::StringPrintf("[\\x%02x-\\x%02x]",
                                           static_cast<int>(set_it->from()),
@@ -333,7 +330,7 @@
   std::vector<State> states(2, GenerateInvalidState());
   StateMap state_map;
   state_map.insert(std::make_pair(StringSet(), 0));
-  for (PairVector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
+  for (auto it = pairs.begin(); it != pairs.end(); ++it) {
     DCHECK(it->character.empty());
     DCHECK(!it->set.empty());
     const Range& range = it->set.front();
@@ -376,16 +373,13 @@
   std::vector<uint8_t> shifts;
   uint8_t pos = 0;
 
-  for (std::vector<State>::const_iterator state_it = states.begin();
-       state_it != states.end();
-       ++state_it) {
+  for (auto state_it = states.begin(); state_it != states.end(); ++state_it) {
     // We want to set |shift| to the (0-based) index of the least-significant
     // set bit in any of the ranges for this state, since this tells us how many
     // bits we can discard and still determine what range a byte lies in. Sadly
     // it appears that ffs() is not portable, so we do it clumsily.
     uint8_t shift = 7;
-    for (State::const_iterator range_it = state_it->begin();
-         range_it != state_it->end();
+    for (auto range_it = state_it->begin(); range_it != state_it->end();
          ++range_it) {
       while (shift > 0 && range_it->from % (1 << shift) != 0) {
         --shift;
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index b0e7a1d..ffb4744 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -510,7 +510,7 @@
 FieldTrialList::~FieldTrialList() {
   AutoLock auto_lock(lock_);
   while (!registered_.empty()) {
-    RegistrationMap::iterator it = registered_.begin();
+    auto it = registered_.begin();
     it->second->Release();
     registered_.erase(it->first);
   }
@@ -728,8 +728,8 @@
     return;
   AutoLock auto_lock(global_->lock_);
 
-  for (RegistrationMap::iterator it = global_->registered_.begin();
-       it != global_->registered_.end(); ++it) {
+  for (auto it = global_->registered_.begin(); it != global_->registered_.end();
+       ++it) {
     FieldTrial::ActiveGroup active_group;
     if (it->second->GetActiveGroup(&active_group))
       active_groups->push_back(active_group);
@@ -1497,7 +1497,7 @@
 }
 
 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
-  RegistrationMap::iterator it = registered_.find(name);
+  auto it = registered_.find(name);
   if (registered_.end() == it)
     return nullptr;
   return it->second;
diff --git a/base/metrics/field_trial_params.cc b/base/metrics/field_trial_params.cc
index 7195f4a..f01db0f 100644
--- a/base/metrics/field_trial_params.cc
+++ b/base/metrics/field_trial_params.cc
@@ -41,7 +41,7 @@
                                     const std::string& param_name) {
   std::map<std::string, std::string> params;
   if (GetFieldTrialParams(trial_name, &params)) {
-    std::map<std::string, std::string>::iterator it = params.find(param_name);
+    auto it = params.find(param_name);
     if (it != params.end())
       return it->second;
   }
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 4166adf..bf7d7c9e 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -1094,7 +1094,7 @@
 
 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const {
   int range = ranges(i);
-  BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
+  auto it = bucket_description_.find(range);
   if (it == bucket_description_.end())
     return Histogram::GetAsciiBucketRange(i);
   return it->second;
diff --git a/base/metrics/histogram_delta_serialization.cc b/base/metrics/histogram_delta_serialization.cc
index a74b87f..7214c9af 100644
--- a/base/metrics/histogram_delta_serialization.cc
+++ b/base/metrics/histogram_delta_serialization.cc
@@ -57,8 +57,8 @@
 // static
 void HistogramDeltaSerialization::DeserializeAndAddSamples(
     const std::vector<std::string>& serialized_deltas) {
-  for (std::vector<std::string>::const_iterator it = serialized_deltas.begin();
-       it != serialized_deltas.end(); ++it) {
+  for (auto it = serialized_deltas.begin(); it != serialized_deltas.end();
+       ++it) {
     Pickle pickle(it->data(), checked_cast<int>(it->size()));
     PickleIterator iter(pickle);
     DeserializeHistogramAndAddSamples(&iter);
diff --git a/base/metrics/sample_map.cc b/base/metrics/sample_map.cc
index c6dce293..b441afa 100644
--- a/base/metrics/sample_map.cc
+++ b/base/metrics/sample_map.cc
@@ -91,7 +91,7 @@
 }
 
 Count SampleMap::GetCount(Sample value) const {
-  std::map<Sample, Count>::const_iterator it = sample_counts_.find(value);
+  auto it = sample_counts_.find(value);
   if (it == sample_counts_.end())
     return 0;
   return it->second;
diff --git a/base/observer_list_threadsafe_unittest.cc b/base/observer_list_threadsafe_unittest.cc
index 9920aab..4983d7c 100644
--- a/base/observer_list_threadsafe_unittest.cc
+++ b/base/observer_list_threadsafe_unittest.cc
@@ -286,7 +286,7 @@
   void Observe(int x) override {
     std::vector<Foo*> tmp;
     tmp.swap(foos_);
-    for (std::vector<Foo*>::iterator it = tmp.begin(); it != tmp.end(); ++it) {
+    for (auto it = tmp.begin(); it != tmp.end(); ++it) {
       list_->RemoveObserver(*it);
     }
   }
diff --git a/base/path_service.cc b/base/path_service.cc
index 6ac501eaf..cc29cab 100644
--- a/base/path_service.cc
+++ b/base/path_service.cc
@@ -148,7 +148,7 @@
   if (path_data->cache_disabled)
     return false;
   // check for a cached version
-  PathMap::const_iterator it = path_data->cache.find(key);
+  auto it = path_data->cache.find(key);
   if (it != path_data->cache.end()) {
     *result = it->second;
     return true;
diff --git a/base/posix/global_descriptors.cc b/base/posix/global_descriptors.cc
index 738d14e3..c198899 100644
--- a/base/posix/global_descriptors.cc
+++ b/base/posix/global_descriptors.cc
@@ -38,8 +38,7 @@
 }
 
 int GlobalDescriptors::MaybeGet(Key key) const {
-  for (Mapping::const_iterator
-       i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+  for (auto i = descriptors_.begin(); i != descriptors_.end(); ++i) {
     if (i->key == key)
       return i->fd;
   }
@@ -51,8 +50,7 @@
     Key key,
     base::MemoryMappedFile::Region* region) {
   base::ScopedFD fd;
-  for (Mapping::iterator i = descriptors_.begin(); i != descriptors_.end();
-       ++i) {
+  for (auto i = descriptors_.begin(); i != descriptors_.end(); ++i) {
     if (i->key == key) {
       *region = i->region;
       fd.reset(i->fd);
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 2bace768ec..793241d 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -651,7 +651,7 @@
 
   // Check that the stack contains a frame for
   // TargetThread::SignalAndWaitUntilSignaled().
-  Frames::const_iterator end_frame = FindFirstFrameWithinFunction(
+  auto end_frame = FindFirstFrameWithinFunction(
       frames, &TargetThread::SignalAndWaitUntilSignaled);
   ASSERT_TRUE(end_frame != frames.end())
       << "Function at "
@@ -684,7 +684,7 @@
 
     // Check that the stack contains a frame for
     // TargetThread::CallThroughOtherLibrary().
-    Frames::const_iterator other_library_frame = FindFirstFrameWithinFunction(
+    auto other_library_frame = FindFirstFrameWithinFunction(
         frames, &TargetThread::CallThroughOtherLibrary);
     ASSERT_TRUE(other_library_frame != frames.end())
         << "Function at "
@@ -753,7 +753,7 @@
 
   // Check that the stack contains a frame for
   // TargetThread::SignalAndWaitUntilSignaled().
-  Frames::const_iterator loc = FindFirstFrameWithinFunction(
+  auto loc = FindFirstFrameWithinFunction(
       frames, &TargetThread::SignalAndWaitUntilSignaled);
   ASSERT_TRUE(loc != frames.end())
       << "Function at "
@@ -800,7 +800,7 @@
 
   // Check that the stack contains a frame for
   // TargetThread::SignalAndWaitUntilSignaled().
-  Frames::const_iterator end_frame = FindFirstFrameWithinFunction(
+  auto end_frame = FindFirstFrameWithinFunction(
       frames, &TargetThread::SignalAndWaitUntilSignaled);
   ASSERT_TRUE(end_frame != frames.end())
       << "Function at "
@@ -810,7 +810,7 @@
       << FormatSampleForDiagnosticOutput(frames);
 
   // Check that the stack contains a frame for TargetThread::CallWithAlloca().
-  Frames::const_iterator alloca_frame =
+  auto alloca_frame =
       FindFirstFrameWithinFunction(frames, &TargetThread::CallWithAlloca);
   ASSERT_TRUE(alloca_frame != frames.end())
       << "Function at "
@@ -1314,7 +1314,7 @@
 
   // Check that the stack contains a frame for
   // TargetThread::CallThroughOtherLibrary().
-  Frames::const_iterator other_library_frame = FindFirstFrameWithinFunction(
+  auto other_library_frame = FindFirstFrameWithinFunction(
       frames, &TargetThread::CallThroughOtherLibrary);
   ASSERT_TRUE(other_library_frame != frames.end())
       << "Function at "
@@ -1325,7 +1325,7 @@
 
   // Check that the stack contains a frame for
   // TargetThread::SignalAndWaitUntilSignaled().
-  Frames::const_iterator end_frame = FindFirstFrameWithinFunction(
+  auto end_frame = FindFirstFrameWithinFunction(
       frames, &TargetThread::SignalAndWaitUntilSignaled);
   ASSERT_TRUE(end_frame != frames.end())
       << "Function at "
diff --git a/base/strings/utf_offset_string_conversions.cc b/base/strings/utf_offset_string_conversions.cc
index b91ee03..2875663 100644
--- a/base/strings/utf_offset_string_conversions.cc
+++ b/base/strings/utf_offset_string_conversions.cc
@@ -28,7 +28,7 @@
                                    std::vector<size_t>* offsets_for_adjustment,
                                    size_t limit) {
   DCHECK(offsets_for_adjustment);
-  for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin());
+  for (auto i(offsets_for_adjustment->begin());
        i != offsets_for_adjustment->end(); ++i)
     AdjustOffset(adjustments, &(*i), limit);
 }
@@ -41,8 +41,7 @@
   if (*offset == string16::npos)
     return;
   int adjustment = 0;
-  for (Adjustments::const_iterator i = adjustments.begin();
-       i != adjustments.end(); ++i) {
+  for (auto i = adjustments.begin(); i != adjustments.end(); ++i) {
     if (*offset <= i->original_offset)
       break;
     if (*offset < (i->original_offset + i->original_length)) {
@@ -63,7 +62,7 @@
     std::vector<size_t>* offsets_for_unadjustment) {
   if (!offsets_for_unadjustment || adjustments.empty())
     return;
-  for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin());
+  for (auto i(offsets_for_unadjustment->begin());
        i != offsets_for_unadjustment->end(); ++i)
     UnadjustOffset(adjustments, &(*i));
 }
@@ -74,8 +73,7 @@
   if (*offset == string16::npos)
     return;
   int adjustment = 0;
-  for (Adjustments::const_iterator i = adjustments.begin();
-       i != adjustments.end(); ++i) {
+  for (auto i = adjustments.begin(); i != adjustments.end(); ++i) {
     if (*offset + adjustment <= i->original_offset)
       break;
     adjustment += static_cast<int>(i->original_length - i->output_length);
@@ -92,8 +90,8 @@
 void OffsetAdjuster::MergeSequentialAdjustments(
     const Adjustments& first_adjustments,
     Adjustments* adjustments_on_adjusted_string) {
-  Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin();
-  Adjustments::const_iterator first_iter = first_adjustments.begin();
+  auto adjusted_iter = adjustments_on_adjusted_string->begin();
+  auto first_iter = first_adjustments.begin();
   // Simultaneously iterate over all |adjustments_on_adjusted_string| and
   // |first_adjustments|, adding adjustments to or correcting the adjustments
   // in |adjustments_on_adjusted_string| as we go.  |shift| keeps track of the
diff --git a/base/strings/utf_offset_string_conversions_unittest.cc b/base/strings/utf_offset_string_conversions_unittest.cc
index c5ce647..ed43348 100644
--- a/base/strings/utf_offset_string_conversions_unittest.cc
+++ b/base/strings/utf_offset_string_conversions_unittest.cc
@@ -86,8 +86,7 @@
     OffsetAdjuster::AdjustOffset(kNoAdjustments, &size_ts.back(), kLimit);
   }
   size_t unlimited_count = 0;
-  for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
-       ++ti) {
+  for (auto ti = size_ts.begin(); ti != size_ts.end(); ++ti) {
     if (*ti != kNpos)
       ++unlimited_count;
   }
@@ -100,8 +99,7 @@
     OffsetAdjuster::AdjustOffset(kNoAdjustments, &size_ts.back(), kLimit);
   }
   unlimited_count = 0;
-  for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
-       ++ti) {
+  for (auto ti = size_ts.begin(); ti != size_ts.end(); ++ti) {
     if (*ti != kNpos)
       ++unlimited_count;
   }
diff --git a/base/supports_user_data.cc b/base/supports_user_data.cc
index 43ab21a..2eb93f4 100644
--- a/base/supports_user_data.cc
+++ b/base/supports_user_data.cc
@@ -16,7 +16,7 @@
   DCHECK(sequence_checker_.CalledOnValidSequence());
   // Avoid null keys; they are too vulnerable to collision.
   DCHECK(key);
-  DataMap::const_iterator found = user_data_.find(key);
+  auto found = user_data_.find(key);
   if (found != user_data_.end())
     return found->second.get();
   return nullptr;
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index fc382c0..e5d58034 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -382,8 +382,7 @@
 bool WaitableEvent::SignalAll() {
   bool signaled_at_least_one = false;
 
-  for (std::list<Waiter*>::iterator
-       i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) {
+  for (auto i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) {
     if ((*i)->Fire(this))
       signaled_at_least_one = true;
   }
@@ -420,8 +419,7 @@
 // actually removed. Called with lock held.
 // -----------------------------------------------------------------------------
 bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) {
-  for (std::list<Waiter*>::iterator
-       i = waiters_.begin(); i != waiters_.end(); ++i) {
+  for (auto i = waiters_.begin(); i != waiters_.end(); ++i) {
     if (*i == waiter && (*i)->Compare(tag)) {
       waiters_.erase(i);
       return true;
diff --git a/base/test/gtest_util.cc b/base/test/gtest_util.cc
index e5d38f4..4905e566 100644
--- a/base/test/gtest_util.cc
+++ b/base/test/gtest_util.cc
@@ -82,7 +82,7 @@
     return false;
 
   std::vector<base::TestIdentifier> result;
-  for (base::ListValue::iterator i = tests->begin(); i != tests->end(); ++i) {
+  for (auto i = tests->begin(); i != tests->end(); ++i) {
     base::DictionaryValue* test = nullptr;
     if (!i->GetAsDictionary(&test))
       return false;
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
index a7e590c..94343a4 100644
--- a/base/test/launcher/test_results_tracker.cc
+++ b/base/test/launcher/test_results_tracker.cc
@@ -337,10 +337,8 @@
     std::unique_ptr<DictionaryValue> current_iteration_data(
         new DictionaryValue);
 
-    for (PerIterationData::ResultsMap::const_iterator j =
-             per_iteration_data_[i].results.begin();
-         j != per_iteration_data_[i].results.end();
-         ++j) {
+    for (auto j = per_iteration_data_[i].results.begin();
+         j != per_iteration_data_[i].results.end(); ++j) {
       std::unique_ptr<ListValue> test_results(new ListValue);
 
       for (size_t k = 0; k < j->second.test_results.size(); k++) {
@@ -488,10 +486,8 @@
 
 void TestResultsTracker::GetTestStatusForIteration(
     int iteration, TestStatusMap* map) const {
-  for (PerIterationData::ResultsMap::const_iterator j =
-           per_iteration_data_[iteration].results.begin();
-       j != per_iteration_data_[iteration].results.end();
-       ++j) {
+  for (auto j = per_iteration_data_[iteration].results.begin();
+       j != per_iteration_data_[iteration].results.end(); ++j) {
     // Use the last test result as the final one.
     const TestResult& result = j->second.test_results.back();
     (*map)[result.status].insert(result.full_name);
diff --git a/base/test/metrics/user_action_tester.cc b/base/test/metrics/user_action_tester.cc
index a845bca7..29fee46 100644
--- a/base/test/metrics/user_action_tester.cc
+++ b/base/test/metrics/user_action_tester.cc
@@ -23,7 +23,7 @@
 }
 
 int UserActionTester::GetActionCount(const std::string& user_action) const {
-  UserActionCountMap::const_iterator iter = count_map_.find(user_action);
+  auto iter = count_map_.find(user_action);
   return iter == count_map_.end() ? 0 : iter->second;
 }
 
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index 5b3b2f65..94ec16a3 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -182,8 +182,7 @@
 
   // Add the field trial overrides. This assumes that |enabled_features| are at
   // the begining of |merged_features.enabled_feature_list|, in the same order.
-  std::vector<FieldTrial*>::const_iterator trial_it =
-      trials_for_enabled_features.begin();
+  auto trial_it = trials_for_enabled_features.begin();
   auto feature_it = merged_features.enabled_feature_list.begin();
   std::vector<std::unique_ptr<std::string>> features_with_trial;
   features_with_trial.reserve(trials_for_enabled_features.size());
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index 4eafa0d2..5ff93b4 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -531,8 +531,7 @@
     case OTHER_ARG:
     case PREV_ARG: {
       // Search for the argument name and return its value if found.
-      std::map<std::string, double>::const_iterator num_i =
-          the_event->arg_numbers.find(string_);
+      auto num_i = the_event->arg_numbers.find(string_);
       if (num_i == the_event->arg_numbers.end())
         return false;
       *num = num_i->second;
@@ -582,8 +581,7 @@
     case OTHER_ARG:
     case PREV_ARG: {
       // Search for the argument name and return its value if found.
-      std::map<std::string, std::string>::const_iterator str_i =
-          the_event->arg_strings.find(string_);
+      auto str_i = the_event->arg_strings.find(string_);
       if (str_i == the_event->arg_strings.end())
         return false;
       *str = str_i->second;
diff --git a/base/threading/thread_id_name_manager.cc b/base/threading/thread_id_name_manager.cc
index ca1979d..a0ced2c5 100644
--- a/base/threading/thread_id_name_manager.cc
+++ b/base/threading/thread_id_name_manager.cc
@@ -63,7 +63,7 @@
   std::string* leaked_str = nullptr;
   {
     AutoLock locked(lock_);
-    NameToInternedNameMap::iterator iter = name_to_interned_name_.find(name);
+    auto iter = name_to_interned_name_.find(name);
     if (iter != name_to_interned_name_.end()) {
       leaked_str = iter->second;
     } else {
@@ -71,8 +71,7 @@
       name_to_interned_name_[name] = leaked_str;
     }
 
-    ThreadIdToHandleMap::iterator id_to_handle_iter =
-        thread_id_to_handle_.find(id);
+    auto id_to_handle_iter = thread_id_to_handle_.find(id);
 
     GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str()));
     if (set_name_callback_) {
@@ -104,12 +103,11 @@
   if (id == main_process_id_)
     return main_process_name_->c_str();
 
-  ThreadIdToHandleMap::iterator id_to_handle_iter =
-      thread_id_to_handle_.find(id);
+  auto id_to_handle_iter = thread_id_to_handle_.find(id);
   if (id_to_handle_iter == thread_id_to_handle_.end())
     return name_to_interned_name_[kDefaultName]->c_str();
 
-  ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
+  auto handle_to_name_iter =
       thread_handle_to_interned_name_.find(id_to_handle_iter->second);
   return handle_to_name_iter->second->c_str();
 }
@@ -122,14 +120,12 @@
 void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle,
                                      PlatformThreadId id) {
   AutoLock locked(lock_);
-  ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
-      thread_handle_to_interned_name_.find(handle);
+  auto handle_to_name_iter = thread_handle_to_interned_name_.find(handle);
 
   DCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end());
   thread_handle_to_interned_name_.erase(handle_to_name_iter);
 
-  ThreadIdToHandleMap::iterator id_to_handle_iter =
-      thread_id_to_handle_.find(id);
+  auto id_to_handle_iter = thread_id_to_handle_.find(id);
   DCHECK((id_to_handle_iter!= thread_id_to_handle_.end()));
   // The given |id| may have been re-used by the system. Make sure the
   // mapping points to the provided |handle| before removal.
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index f3d42a2e..071a145 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -1052,8 +1052,7 @@
            "If this happens stably for some thread, please call "
            "TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop() from "
            "the thread to avoid its trace events from being lost.";
-    for (hash_set<MessageLoop*>::const_iterator it =
-             thread_message_loops_.begin();
+    for (auto it = thread_message_loops_.begin();
          it != thread_message_loops_.end(); ++it) {
       LOG(WARNING) << "Thread: " << (*it)->GetThreadName();
     }
diff --git a/base/vlog.cc b/base/vlog.cc
index fbe1897..9100d4ff 100644
--- a/base/vlog.cc
+++ b/base/vlog.cc
@@ -105,8 +105,7 @@
 int VlogInfo::GetVlogLevel(const base::StringPiece& file) const {
   if (!vmodule_levels_.empty()) {
     base::StringPiece module(GetModule(file));
-    for (std::vector<VmodulePattern>::const_iterator it =
-             vmodule_levels_.begin(); it != vmodule_levels_.end(); ++it) {
+    for (auto it = vmodule_levels_.begin(); it != vmodule_levels_.end(); ++it) {
       base::StringPiece target(
           (it->match_target == VmodulePattern::MATCH_FILE) ? file : module);
       if (MatchVlogPattern(target, it->pattern))
diff --git a/blink/tools/run_layout_tests.py b/blink/tools/run_layout_tests.py
index 1d216fb..33cc6965 100755
--- a/blink/tools/run_layout_tests.py
+++ b/blink/tools/run_layout_tests.py
@@ -5,18 +5,10 @@
 
 """Wrapper around
    third_party/blink/tools/run_web_tests.py"""
-import os
-import subprocess
+
 import sys
 
-def main():
+if __name__ == '__main__':
     print '\n    Please use third_party/blink/tools/run_web_tests.*.  ' \
         'This command will be removed.\n'
-    src_dir = os.path.abspath(os.path.join(sys.path[0], '..', '..'))
-    script_dir=os.path.join(src_dir, "third_party", "blink", "tools")
-    script = os.path.join(script_dir, 'run_web_tests.py')
-    cmd = [sys.executable, script] + sys.argv[1:]
-    return subprocess.call(cmd)
-
-if __name__ == '__main__':
-    sys.exit(main())
+    sys.exit(1)
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
index b31f96d..38e6257 100644
--- a/build/check_gn_headers_whitelist.txt
+++ b/build/check_gn_headers_whitelist.txt
@@ -157,7 +157,6 @@
 gpu/command_buffer/service/gl_stream_texture_image.h
 gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
 gpu/command_buffer/service/memory_tracking.h
-gpu/command_buffer/service/progress_reporter.h
 gpu/config/gpu_lists_version.h
 gpu/gles2_conform_support/gtf/gtf_stubs.h
 gpu/gpu_export.h
@@ -360,5 +359,6 @@
 ui/gl/gl_bindings_api_autogen_glx.h
 ui/gl/gpu_preference.h
 ui/gl/gpu_switching_observer.h
+ui/gl/progress_reporter.h
 ui/ozone/ozone_base_export.h
 ui/ozone/public/ozone_switches.h
diff --git a/cc/base/list_container_unittest.cc b/cc/base/list_container_unittest.cc
index c58597b..e1da298 100644
--- a/cc/base/list_container_unittest.cc
+++ b/cc/base/list_container_unittest.cc
@@ -572,9 +572,8 @@
 
   {
     ListContainer<NonDerivedElement>::ReverseIterator iter = list.rbegin();
-    for (std::vector<NonDerivedElement*>::reverse_iterator nde_iter =
-             nde_list.rbegin();
-         nde_iter != nde_list.rend(); ++nde_iter) {
+    for (auto nde_iter = nde_list.rbegin(); nde_iter != nde_list.rend();
+         ++nde_iter) {
       EXPECT_EQ(*nde_iter, *iter);
       ++iter;
     }
diff --git a/cc/base/rolling_time_delta_history.cc b/cc/base/rolling_time_delta_history.cc
index a79722cc..dde20a7 100644
--- a/cc/base/rolling_time_delta_history.cc
+++ b/cc/base/rolling_time_delta_history.cc
@@ -24,7 +24,7 @@
     chronological_sample_deque_.pop_front();
   }
 
-  TimeDeltaMultiset::iterator it = sample_set_.insert(time);
+  auto it = sample_set_.insert(time);
   chronological_sample_deque_.push_back(it);
   percentile_cache_.clear();
 }
@@ -63,13 +63,13 @@
 
   if (num_smaller_samples > sample_set_.size() / 2) {
     size_t num_larger_samples = sample_set_.size() - num_smaller_samples - 1;
-    TimeDeltaMultiset::const_reverse_iterator it = sample_set_.rbegin();
+    auto it = sample_set_.rbegin();
     for (size_t i = 0; i < num_larger_samples; i++)
       it++;
     return *it;
   }
 
-  TimeDeltaMultiset::const_iterator it = sample_set_.begin();
+  auto it = sample_set_.begin();
   for (size_t i = 0; i < num_smaller_samples; i++)
     it++;
   return *it;
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 01c693ac..5019dff 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -245,8 +245,8 @@
     return;
   }
 
-  for (LayerList::iterator iter = inputs_.children.begin();
-       iter != inputs_.children.end(); ++iter) {
+  for (auto iter = inputs_.children.begin(); iter != inputs_.children.end();
+       ++iter) {
     if (iter->get() != child)
       continue;
 
@@ -1469,7 +1469,7 @@
 void Layer::RemoveFromClipTree() {
   if (clip_children_.get()) {
     std::set<Layer*> copy = *clip_children_;
-    for (std::set<Layer*>::iterator it = copy.begin(); it != copy.end(); ++it)
+    for (auto it = copy.begin(); it != copy.end(); ++it)
       (*it)->SetClipParent(nullptr);
   }
 
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 542fefaa..00674d1 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -3095,8 +3095,7 @@
 
   std::vector<Tile*> high_res_tiles =
       pending_layer()->HighResTiling()->AllTilesForTesting();
-  for (std::vector<Tile*>::iterator tile_it = high_res_tiles.begin();
-       tile_it != high_res_tiles.end();
+  for (auto tile_it = high_res_tiles.begin(); tile_it != high_res_tiles.end();
        ++tile_it) {
     Tile* tile = *tile_it;
     TileDrawInfo& draw_info = tile->draw_info();
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 63ffe595..031d23cf 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -62,7 +62,7 @@
 
   // Deletes a UI resource.  May safely be called more than once.
   void DeleteUIResource(UIResourceId id) override {
-    UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
+    auto iter = ui_resource_bitmap_map_.find(id);
     if (iter != ui_resource_bitmap_map_.end()) {
       ui_resource_bitmap_map_.erase(iter);
       total_ui_resource_deleted_++;
@@ -74,14 +74,14 @@
   int TotalUIResourceCreated() { return total_ui_resource_created_; }
 
   gfx::Size ui_resource_size(UIResourceId id) {
-    UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
+    auto iter = ui_resource_bitmap_map_.find(id);
     if (iter != ui_resource_bitmap_map_.end())
       return iter->second.GetSize();
     return gfx::Size();
   }
 
   UIResourceBitmap* ui_resource_bitmap(UIResourceId id) {
-    UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
+    auto iter = ui_resource_bitmap_map_.find(id);
     if (iter != ui_resource_bitmap_map_.end())
       return &iter->second;
     return nullptr;
diff --git a/cc/raster/raster_buffer_provider_perftest.cc b/cc/raster/raster_buffer_provider_perftest.cc
index 69a2d47..bbdd6f5 100644
--- a/cc/raster/raster_buffer_provider_perftest.cc
+++ b/cc/raster/raster_buffer_provider_perftest.cc
@@ -311,7 +311,7 @@
 
       for (auto& decode_task : raster_task->dependencies()) {
         // Add decode task if it doesn't already exist in graph.
-        TaskGraph::Node::Vector::iterator decode_it =
+        auto decode_it =
             std::find_if(graph->nodes.begin(), graph->nodes.end(),
                          [decode_task](const TaskGraph::Node& node) {
                            return node.task == decode_task;
diff --git a/cc/raster/task_graph_work_queue.cc b/cc/raster/task_graph_work_queue.cc
index 06b1f54..2d04f52 100644
--- a/cc/raster/task_graph_work_queue.cc
+++ b/cc/raster/task_graph_work_queue.cc
@@ -80,11 +80,11 @@
     } while (graph_->edges[current_index_].task != task_);
 
     // Now find the node for the dependent of this edge.
-    TaskGraph::Node::Vector::iterator it = std::find_if(
-        graph_->nodes.begin(), graph_->nodes.end(),
-        [this](const TaskGraph::Node& node) {
-          return node.task == graph_->edges[current_index_].dependent;
-        });
+    auto it = std::find_if(graph_->nodes.begin(), graph_->nodes.end(),
+                           [this](const TaskGraph::Node& node) {
+                             return node.task ==
+                                    graph_->edges[current_index_].dependent;
+                           });
     DCHECK(it != graph_->nodes.end());
     current_node_ = &(*it);
 
@@ -152,11 +152,11 @@
     // Remove any old nodes that are associated with this task. The result is
     // that the old graph is left with all nodes not present in this graph,
     // which we use below to determine what tasks need to be canceled.
-    TaskGraph::Node::Vector::iterator old_it = std::find_if(
-        task_namespace.graph.nodes.begin(), task_namespace.graph.nodes.end(),
-        [&node](const TaskGraph::Node& other) {
-          return node.task == other.task;
-        });
+    auto old_it = std::find_if(task_namespace.graph.nodes.begin(),
+                               task_namespace.graph.nodes.end(),
+                               [&node](const TaskGraph::Node& other) {
+                                 return node.task == other.task;
+                               });
     if (old_it != task_namespace.graph.nodes.end()) {
       std::swap(*old_it, task_namespace.graph.nodes.back());
       // If old task is scheduled to run again and not yet started running,
@@ -200,8 +200,7 @@
   task_namespace.graph.Swap(graph);
 
   // Determine what tasks in old graph need to be canceled.
-  for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
-       it != graph->nodes.end(); ++it) {
+  for (auto it = graph->nodes.begin(); it != graph->nodes.end(); ++it) {
     TaskGraph::Node& node = *it;
 
     // Skip if already finished running task.
@@ -353,7 +352,7 @@
 
 void TaskGraphWorkQueue::CollectCompletedTasks(NamespaceToken token,
                                                Task::Vector* completed_tasks) {
-  TaskNamespaceMap::iterator it = namespaces_.find(token);
+  auto it = namespaces_.find(token);
   if (it == namespaces_.end())
     return;
 
diff --git a/cc/test/task_graph_runner_test_template.cc b/cc/test/task_graph_runner_test_template.cc
index f201e932..2825c0e 100644
--- a/cc/test/task_graph_runner_test_template.cc
+++ b/cc/test/task_graph_runner_test_template.cc
@@ -60,8 +60,7 @@
   Task::Vector new_dependents;
   TaskGraph new_graph;
 
-  for (std::vector<TaskInfo>::const_iterator it = tasks.begin();
-       it != tasks.end(); ++it) {
+  for (auto it = tasks.begin(); it != tasks.end(); ++it) {
     scoped_refptr<FakeTaskImpl> new_task(
         new FakeTaskImpl(this, it->namespace_index, it->id));
     new_graph.nodes.push_back(
diff --git a/cc/tiles/picture_layer_tiling.cc b/cc/tiles/picture_layer_tiling.cc
index 4901cdbf..13b5f93 100644
--- a/cc/tiles/picture_layer_tiling.cc
+++ b/cc/tiles/picture_layer_tiling.cc
@@ -94,7 +94,7 @@
                                  include_borders);
        iter; ++iter) {
     TileMapKey key(iter.index());
-    TileMap::iterator find = tiles_.find(key);
+    auto find = tiles_.find(key);
     if (find != tiles_.end())
       continue;
 
@@ -552,7 +552,7 @@
 }
 
 std::unique_ptr<Tile> PictureLayerTiling::TakeTileAt(int i, int j) {
-  TileMap::iterator found = tiles_.find(TileMapKey(i, j));
+  auto found = tiles_.find(TileMapKey(i, j));
   if (found == tiles_.end())
     return nullptr;
   std::unique_ptr<Tile> result = std::move(found->second);
@@ -976,7 +976,7 @@
 
 size_t PictureLayerTiling::GPUMemoryUsageInBytes() const {
   size_t amount = 0;
-  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
+  for (auto it = tiles_.begin(); it != tiles_.end(); ++it) {
     const Tile* tile = it->second.get();
     amount += tile->GPUMemoryUsageInBytes();
   }
diff --git a/cc/tiles/picture_layer_tiling_unittest.cc b/cc/tiles/picture_layer_tiling_unittest.cc
index 7f46861a..7931cbb 100644
--- a/cc/tiles/picture_layer_tiling_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_unittest.cc
@@ -129,9 +129,7 @@
     tiling_->SetLiveTilesRect(live_tiles_rect);
 
     std::vector<Tile*> tiles = tiling_->AllTilesForTesting();
-    for (std::vector<Tile*>::iterator iter = tiles.begin();
-         iter != tiles.end();
-         ++iter) {
+    for (auto iter = tiles.begin(); iter != tiles.end(); ++iter) {
       EXPECT_TRUE(live_tiles_rect.Intersects((*iter)->content_rect()));
     }
   }
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 6761f8d6..7dc254a 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -253,8 +253,7 @@
   size_t dependencies = 0u;
 
   // Insert image decode tasks.
-  for (TileTask::Vector::const_iterator it = decode_tasks.begin();
-       it != decode_tasks.end(); ++it) {
+  for (auto it = decode_tasks.begin(); it != decode_tasks.end(); ++it) {
     TileTask* decode_task = it->get();
 
     // Skip if already decoded.
@@ -264,11 +263,10 @@
     dependencies++;
 
     // Add decode task if it doesn't already exist in graph.
-    TaskGraph::Node::Vector::iterator decode_it =
-        std::find_if(graph->nodes.begin(), graph->nodes.end(),
-                     [decode_task](const TaskGraph::Node& node) {
-                       return node.task == decode_task;
-                     });
+    auto decode_it = std::find_if(graph->nodes.begin(), graph->nodes.end(),
+                                  [decode_task](const TaskGraph::Node& node) {
+                                    return node.task == decode_task;
+                                  });
 
     // In rare circumstances, a background category task may come in before a
     // foreground category task. In these cases, upgrade any background category
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 56f03bf..925069e0 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -213,8 +213,8 @@
     bool* layer_is_new) {
   LayerRectMapData data(layer_id);
 
-  SortedRectMapForLayers::iterator it = std::lower_bound(
-      rect_history_for_layers_.begin(), rect_history_for_layers_.end(), data);
+  auto it = std::lower_bound(rect_history_for_layers_.begin(),
+                             rect_history_for_layers_.end(), data);
 
   if (it == rect_history_for_layers_.end() || it->layer_id_ != layer_id) {
     *layer_is_new = true;
@@ -229,9 +229,8 @@
     bool* surface_is_new) {
   SurfaceRectMapData data(surface_id);
 
-  SortedRectMapForSurfaces::iterator it =
-      std::lower_bound(rect_history_for_surfaces_.begin(),
-                       rect_history_for_surfaces_.end(), data);
+  auto it = std::lower_bound(rect_history_for_surfaces_.begin(),
+                             rect_history_for_surfaces_.end(), data);
 
   if (it == rect_history_for_surfaces_.end() || it->surface_id_ != surface_id) {
     *surface_is_new = true;
@@ -271,12 +270,10 @@
   // So, these regions are now exposed on the target surface.
 
   DamageAccumulator damage;
-  SortedRectMapForLayers::iterator layer_cur_pos =
-      rect_history_for_layers_.begin();
-  SortedRectMapForLayers::iterator layer_copy_pos = layer_cur_pos;
-  SortedRectMapForSurfaces::iterator surface_cur_pos =
-      rect_history_for_surfaces_.begin();
-  SortedRectMapForSurfaces::iterator surface_copy_pos = surface_cur_pos;
+  auto layer_cur_pos = rect_history_for_layers_.begin();
+  auto layer_copy_pos = layer_cur_pos;
+  auto surface_cur_pos = rect_history_for_surfaces_.begin();
+  auto surface_copy_pos = surface_cur_pos;
 
   // Loop below basically implements std::remove_if loop with and extra
   // processing (adding deleted rect to damage) for deleted items.
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index d5d6073..9952d74 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -5397,8 +5397,7 @@
 }
 
 void LayerTreeHostImpl::MarkUIResourceNotEvicted(UIResourceId uid) {
-  std::set<UIResourceId>::iterator found_in_evicted =
-      evicted_ui_resources_.find(uid);
+  auto found_in_evicted = evicted_ui_resources_.find(uid);
   if (found_in_evicted == evicted_ui_resources_.end())
     return;
   evicted_ui_resources_.erase(found_in_evicted);
@@ -5420,13 +5419,13 @@
 }
 
 void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfSetNeedsRedraw() {
-  std::set<SwapPromiseMonitor*>::iterator it = swap_promise_monitor_.begin();
+  auto it = swap_promise_monitor_.begin();
   for (; it != swap_promise_monitor_.end(); it++)
     (*it)->OnSetNeedsRedrawOnImpl();
 }
 
 void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfForwardingToMainThread() {
-  std::set<SwapPromiseMonitor*>::iterator it = swap_promise_monitor_.begin();
+  auto it = swap_promise_monitor_.begin();
   for (; it != swap_promise_monitor_.end(); it++)
     (*it)->OnForwardScrollUpdateToMainThreadOnImpl();
 }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 018455c..6054dd3 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1402,7 +1402,7 @@
 }
 
 LayerImpl* LayerTreeImpl::LayerById(int id) const {
-  LayerImplMap::const_iterator iter = layer_id_map_.find(id);
+  auto iter = layer_id_map_.find(id);
   return iter != layer_id_map_.end() ? iter->second : nullptr;
 }
 
@@ -1834,8 +1834,7 @@
 }
 
 void LayerTreeImpl::UnregisterPictureLayerImpl(PictureLayerImpl* layer) {
-  std::vector<PictureLayerImpl*>::iterator it =
-      std::find(picture_layers_.begin(), picture_layers_.end(), layer);
+  auto it = std::find(picture_layers_.begin(), picture_layers_.end(), layer);
   DCHECK(it != picture_layers_.end());
   picture_layers_.erase(it);
 }
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 99b15dd..c4fa5bc 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1436,7 +1436,7 @@
 
 const gfx::ScrollOffset ScrollTree::current_scroll_offset(ElementId id) const {
   if (property_trees()->is_main_thread) {
-    ScrollOffsetMap::const_iterator it = scroll_offset_map_.find(id);
+    auto it = scroll_offset_map_.find(id);
     return it != scroll_offset_map_.end() ? it->second : gfx::ScrollOffset();
   }
   return GetSyncedScrollOffset(id)
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 29f43d30..b55d165 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -1404,7 +1404,7 @@
     return;
 
   if (layer->clip_children()) {
-    for (std::set<Layer*>::iterator it = layer->clip_children()->begin();
+    for (auto it = layer->clip_children()->begin();
          it != layer->clip_children()->end(); ++it) {
       DCHECK_EQ((*it)->clip_parent(), layer);
     }
diff --git a/chrome/VERSION b/chrome/VERSION
index 86eb119..dd8690c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=71
 MINOR=0
-BUILD=3569
+BUILD=3570
 PATCH=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
index 38e8b98..ed3d8d4 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java
@@ -47,7 +47,7 @@
     }
 
     /** Loads the journal and asynchronously returns the contents. */
-    public void loadJournal(String journalName, Callback<String[]> successCallback,
+    public void loadJournal(String journalName, Callback<byte[][]> successCallback,
             Callback<Void> failureCallback) {
         assert mNativeFeedJournalBridge != 0;
         nativeLoadJournal(mNativeFeedJournalBridge, journalName, successCallback, failureCallback);
@@ -67,8 +67,7 @@
             switch (operation.getType()) {
                 case Type.APPEND:
                     Append append = (Append) operation;
-                    nativeAddAppendOperation(
-                            mNativeFeedJournalBridge, new String(append.getValue()));
+                    nativeAddAppendOperation(mNativeFeedJournalBridge, append.getValue());
                     break;
                 case Type.COPY:
                     Copy copy = (Copy) operation;
@@ -112,13 +111,13 @@
     private native long nativeInit(Profile profile);
     private native void nativeDestroy(long nativeFeedJournalBridge);
     private native void nativeLoadJournal(long nativeFeedJournalBridge, String journalName,
-            Callback<String[]> successCallback, Callback<Void> failureCallback);
+            Callback<byte[][]> successCallback, Callback<Void> failureCallback);
     private native void nativeCommitJournalMutation(
             long nativeFeedJournalBridge, Callback<Boolean> callback);
     private native void nativeStartJournalMutation(
             long nativeFeedJournalBridge, String journalName);
     private native void nativeDeleteJournalMutation(long nativeFeedJournalBridge);
-    private native void nativeAddAppendOperation(long nativeFeedJournalBridge, String value);
+    private native void nativeAddAppendOperation(long nativeFeedJournalBridge, byte[] value);
     private native void nativeAddCopyOperation(long nativeFeedJournalBridge, String toJournalName);
     private native void nativeAddDeleteOperation(long nativeFeedJournalBridge);
     private native void nativeDoesJournalExist(long nativeFeedJournalBridge, String journalName,
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java
index ca2ab65..90bdd47 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java
@@ -13,7 +13,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.profiles.Profile;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -52,11 +51,8 @@
     @Override
     public void read(String journalName, Consumer < Result < List<byte[]>>> consumer) {
         assert mFeedJournalBridge != null;
-        mFeedJournalBridge.loadJournal(journalName, (String[] entries) -> {
-            List<byte[]> journal = new ArrayList<byte[]>();
-            for (String entry : entries) {
-                journal.add(entry.getBytes());
-            }
+        mFeedJournalBridge.loadJournal(journalName, (byte[][] entries) -> {
+            List<byte[]> journal = Arrays.asList(entries);
             consumer.accept(Result.success(journal));
         }, (Void ignored) -> consumer.accept(Result.failure()));
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java
index 2abbcc3..0a48b503 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java
@@ -47,9 +47,9 @@
     public static final String JOURNAL_KEY1 = "JOURNAL_KEY_1";
     public static final String JOURNAL_KEY2 = "JOURNAL_KEY_2";
     public static final String JOURNAL_KEY3 = "JOURNAL_KEY_3";
-    public static final String JOURNAL_DATA1 = "JOURNAL_DATA_1";
-    public static final String JOURNAL_DATA2 = "JOURNAL_DATA_2";
-    public static final String JOURNAL_DATA3 = "JOURNAL_DATA_3";
+    public static final byte[] JOURNAL_DATA1 = {24, -119, -10, -71, -35, 5};
+    public static final byte[] JOURNAL_DATA2 = {8, 3, 18, 53, 70, 69, 65, 84, 85, 82, 69};
+    public static final byte[] JOURNAL_DATA3 = {54, 55, 56, 48, 51, 57, 24, -119, -10};
 
     @Mock
     private FeedJournalBridge mBridge;
@@ -76,6 +76,8 @@
     @Captor
     private ArgumentCaptor<Callback<Boolean>> mBooleanSuccessCallbackArgument;
     @Captor
+    private ArgumentCaptor<Callback<byte[][]>> mArrayOfByteArraySuccessCallbackArgument;
+    @Captor
     private ArgumentCaptor<Callback<String[]>> mStringArraySuccessCallbackArgument;
     @Captor
     private ArgumentCaptor<Callback<Void>> mFailureCallbackArgument;
@@ -84,6 +86,16 @@
 
     private FeedJournalStorage mJournalStorage;
 
+    private Answer<Void> createArrayOfByteArraySuccessAnswer(byte[][] arrayOfByteArray) {
+        return new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                mArrayOfByteArraySuccessCallbackArgument.getValue().onResult(arrayOfByteArray);
+                return null;
+            }
+        };
+    }
+
     private Answer<Void> createStringArraySuccessAnswer(String[] stringArray) {
         return new Answer<Void>() {
             @Override
@@ -115,23 +127,21 @@
     }
 
     private void verifyListOfBytesResult(
-            List<String> expectedList, boolean expectedSuccess, Result<List<byte[]>> actualResult) {
-        assertEquals(expectedSuccess, actualResult.isSuccessful());
-        if (!expectedSuccess) return;
-
+            List<byte[]> expectedList, Result<List<byte[]>> actualResult) {
         List<byte[]> actualList = actualResult.getValue();
         assertEquals(expectedList.size(), actualList.size());
         for (byte[] actualString : actualList) {
-            assertTrue(expectedList.contains(new String(actualString)));
+            boolean foundSame = false;
+            for (byte[] expectedBytes : expectedList) {
+                if (Arrays.equals(expectedBytes, actualString)) {
+                    foundSame = true;
+                    break;
+                }
+            }
+            assertTrue(foundSame);
         }
     }
 
-    private void verifyListOfBytesResult(
-            String[] expectedString, boolean expectedSuccess, Result<List<byte[]>> actualResult) {
-        List<String> expectedList = Arrays.asList(expectedString);
-        verifyListOfBytesResult(expectedList, expectedSuccess, actualResult);
-    }
-
     private void verifyListOfStringResult(
             String[] expectedStrings, boolean expectedSuccess, Result<List<String>> actualResult) {
         assertEquals(expectedSuccess, actualResult.isSuccessful());
@@ -153,33 +163,40 @@
     @Test
     @SmallTest
     public void readTest() {
-        String[] answerStrings = {JOURNAL_DATA1, JOURNAL_DATA2, JOURNAL_DATA3};
-        Answer<Void> answer = createStringArraySuccessAnswer(answerStrings);
+        byte[][] answerStrings = {JOURNAL_DATA1, JOURNAL_DATA2, JOURNAL_DATA3};
+        Answer<Void> answer = createArrayOfByteArraySuccessAnswer(answerStrings);
         doAnswer(answer).when(mBridge).loadJournal(mStringArgument.capture(),
-                mStringArraySuccessCallbackArgument.capture(), mFailureCallbackArgument.capture());
+                mArrayOfByteArraySuccessCallbackArgument.capture(),
+                mFailureCallbackArgument.capture());
 
         mJournalStorage.read(JOURNAL_KEY1, mListOfByteArrayConsumer);
         verify(mBridge, times(1))
-                .loadJournal(eq(JOURNAL_KEY1), mStringArraySuccessCallbackArgument.capture(),
+                .loadJournal(eq(JOURNAL_KEY1), mArrayOfByteArraySuccessCallbackArgument.capture(),
                         mFailureCallbackArgument.capture());
         verify(mListOfByteArrayConsumer, times(1)).accept(mListOfByteArrayCaptor.capture());
-        verifyListOfBytesResult(answerStrings, true, mListOfByteArrayCaptor.getValue());
+        assertEquals(true, mListOfByteArrayCaptor.getValue().isSuccessful());
+        List<byte[]> expectedStrings = new ArrayList<byte[]>();
+        expectedStrings.add(JOURNAL_DATA1);
+        expectedStrings.add(JOURNAL_DATA2);
+        expectedStrings.add(JOURNAL_DATA3);
+        verifyListOfBytesResult(expectedStrings, mListOfByteArrayCaptor.getValue());
     }
 
     @Test
     @SmallTest
     public void readFailureTest() {
-        String[] answerStrings = {};
+        byte[][] answerStrings = {};
         Answer<Void> answer = createFailureAnswer();
         doAnswer(answer).when(mBridge).loadJournal(mStringArgument.capture(),
-                mStringArraySuccessCallbackArgument.capture(), mFailureCallbackArgument.capture());
+                mArrayOfByteArraySuccessCallbackArgument.capture(),
+                mFailureCallbackArgument.capture());
 
         mJournalStorage.read(JOURNAL_KEY1, mListOfByteArrayConsumer);
         verify(mBridge, times(1))
-                .loadJournal(eq(JOURNAL_KEY1), mStringArraySuccessCallbackArgument.capture(),
+                .loadJournal(eq(JOURNAL_KEY1), mArrayOfByteArraySuccessCallbackArgument.capture(),
                         mFailureCallbackArgument.capture());
         verify(mListOfByteArrayConsumer, times(1)).accept(mListOfByteArrayCaptor.capture());
-        verifyListOfBytesResult(answerStrings, false, mListOfByteArrayCaptor.getValue());
+        assertEquals(false, mListOfByteArrayCaptor.getValue().isSuccessful());
     }
 
     @Test
@@ -267,7 +284,7 @@
                         mBooleanSuccessCallbackArgument.capture());
 
         mJournalStorage.commit(new JournalMutation.Builder(JOURNAL_KEY1)
-                                       .append(JOURNAL_DATA1.getBytes())
+                                       .append(JOURNAL_DATA1)
                                        .copy(JOURNAL_KEY2)
                                        .delete()
                                        .build(),
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 7ebaa62..7185093 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-71.0.3568.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-71.0.3569.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index bcf9882..80109c4 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -14,7 +14,7 @@
 
     <uses-sdk
         android:minSdkVersion="16"
-        android:targetSdkVersion="26" />
+        android:targetSdkVersion="28" />
 
     <application
         android:icon="@mipmap/ic_launcher"
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index a8d3cc18..2a9c781 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 59
+current_shell_apk_version = 60
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 25edda30..d3c12a2 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3559,16 +3559,16 @@
     Google assistant logo
   </message>
   <message name="IDS_ASSISTANT_HOTWORD_TITLE" desc="Title for assistant hotword optin.">
-    Ok Google
+    "Ok Google"
   </message>
   <message name="IDS_ASSISTANT_HOTWORD_DESC" desc="Description for assistant hotword optin.">
-    Access your Assistant anytime you say "Ok Google" when your device is awake and unlocked.
+    Access your Assistant any time you say "Ok Google" when your screen is on
   </message>
   <message name="IDS_ASSISTANT_SCREEN_CONTEXT_TITLE" desc="Title for assistant screen context optin.">
-    Get info for what's on screen
+    Related info
   </message>
   <message name="IDS_ASSISTANT_SCREEN_CONTEXT_DESC" desc="Description for assistant screen context optin.">
-    See relevant suggestions from your Assistant related to what's on your screen.
+    Let the Assistant give suggestions based on what's on your screen
   </message>
   <message name="IDS_ASSISTANT_GET_MORE_SCREEN_TITLE" desc="Title for assistant get more screen.">
     Get the most out of your Assistant
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7b7ace0f..5119aa2d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4428,6 +4428,11 @@
          Stop using data on this page
       </message>
 
+      <!-- HTTPS Server Previews InfoBar -->
+      <message name="IDS_LITE_PAGE_PREVIEWS_MESSAGE" desc="The text of the infobar notifying the user that Chrome's Lite mode will now also apply to HTTPS pages.">
+        Lite Mode now makes browsing faster on all pages, including HTTPS.
+      </message>
+
       <!-- WebRTC logs -->
       <message name="IDS_WEBRTC_LOGS_TITLE" desc="Title for the chrome://webrtc-logs page.">
         WebRTC logs
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 0528f78..a044b41 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2660,28 +2660,28 @@
     Google Assistant
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_CONTEXT" desc="Title for a toggle that lets the assistant use whats on the screen.">
-    Let Assistant use what's on your screen
+    Related info
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_CONTEXT_DESCRIPTION" desc="Sub label for context-enable toggle.">
-    Enables the Assistant to show you related info, apps, and actions.
+    Let the Assistant give suggestions based on what's on your screen
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD" desc="Title for a toggle that lets the assistant detect hotword.">
-    "Ok Google" detection
+    "Ok Google"
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_DESCRIPTION" desc="Sub label for hotword-enable toggle.">
-    Access your Assistant any time you say "Ok Google" when your screen is on.
+    Access your Assistant any time you say "Ok Google" when your screen is on
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_NOTIFICATION" desc="Title for a toggle that lets the assistant show you notifications.">
-    Let Assistant show you notifications
+    Notifications
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_NOTIFICATION_DESCRIPTION" desc="Sub label for notification-enable toggle.">
-    Enables the Assistant to show you notifications.
+    Allow the Assistant to show you notifications
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_LAUNCH_WITH_MIC_OPEN" desc="Title for a toggle that determines whether to open the microphone when launching the Assistant.">
-    Launch Assistant with microphone open
+    Voice activation
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_LAUNCH_WITH_MIC_OPEN_DESCRIPTION" desc="Sub label for launch-with-microphone-open toggle.">
-    Always launch the Assistant with microphone open.
+    Use speech instead of keyboard when you start your Assistant
   </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_SETTINGS" desc="Title for a button that opens the Google Assistant app settings.">
     Google Assistant settings
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b9295e96..02f71b8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1209,6 +1209,8 @@
     "previews/previews_infobar_delegate.h",
     "previews/previews_lite_page_decider.cc",
     "previews/previews_lite_page_decider.h",
+    "previews/previews_lite_page_infobar_delegate.cc",
+    "previews/previews_lite_page_infobar_delegate.h",
     "previews/previews_lite_page_navigation_throttle.cc",
     "previews/previews_lite_page_navigation_throttle.h",
     "previews/previews_lite_page_navigation_throttle_manager.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ded4115..c4df5a7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1567,9 +1567,6 @@
     {"enable-javascript-harmony", flag_descriptions::kJavascriptHarmonyName,
      flag_descriptions::kJavascriptHarmonyDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kJavaScriptHarmony)},
-    {"enable-asm-webassembly", flag_descriptions::kEnableAsmWasmName,
-     flag_descriptions::kEnableAsmWasmDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kAsmJsToWebAssembly)},
     {"enable-webassembly", flag_descriptions::kEnableWasmName,
      flag_descriptions::kEnableWasmDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kWebAssembly)},
@@ -1687,6 +1684,10 @@
      flag_descriptions::kEnableNotificationScrollBarName,
      flag_descriptions::kEnableNotificationScrollBarDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kNotificationScrollBar)},
+    {"ash-enable-new-message-list-view",
+     flag_descriptions::kEnableNewMessageListViewName,
+     flag_descriptions::kEnableNewMessageListViewDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kNewMessageListView)},
     {"allow-touchpad-three-finger-click",
      flag_descriptions::kAllowTouchpadThreeFingerClickName,
      flag_descriptions::kAllowTouchpadThreeFingerClickDescription, kOsCrOS,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index ff87f10..d077e77 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -75,6 +75,7 @@
     &kAndroidPayIntegrationV2,
     &kAndroidPaymentApps,
     &kAutofillAssistant,
+    &kCastDeviceFilter,
     &kCCTBackgroundTab,
     &kCCTExternalLinkHandling,
     &kCCTModule,
@@ -201,6 +202,10 @@
 const base::Feature kBackgroundTaskComponentUpdate{
     "BackgroundTaskComponentUpdate", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Used in downstream code.
+const base::Feature kCastDeviceFilter{"CastDeviceFilter",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCCTBackgroundTab{"CCTBackgroundTab",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 0d72c510..52d953c7 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -18,6 +18,7 @@
 extern const base::Feature kAndroidPaymentApps;
 extern const base::Feature kAutofillAssistant;
 extern const base::Feature kBackgroundTaskComponentUpdate;
+extern const base::Feature kCastDeviceFilter;
 extern const base::Feature kCCTBackgroundTab;
 extern const base::Feature kCCTExternalLinkHandling;
 extern const base::Feature kCCTModule;
diff --git a/chrome/browser/android/download/download_media_parser.cc b/chrome/browser/android/download/download_media_parser.cc
index 9bbbed9..78e6cd6 100644
--- a/chrome/browser/android/download/download_media_parser.cc
+++ b/chrome/browser/android/download/download_media_parser.cc
@@ -229,8 +229,15 @@
 
 void DownloadMediaParser::RenderVideoFrame(
     scoped_refptr<media::VideoFrame> video_frame) {
+  media::Context3D context;
+  gpu::ContextSupport* context_support = nullptr;
   auto context_provider =
       gpu_factories_ ? gpu_factories_->GetMediaContextProvider() : nullptr;
+  if (context_provider) {
+    context = media::Context3D(context_provider->ContextGL(),
+                               context_provider->GrContext());
+    context_support = context_provider->ContextSupport();
+  }
 
   media::PaintCanvasVideoRenderer renderer;
   SkBitmap bitmap;
@@ -239,11 +246,7 @@
 
   // Draw the video frame to |bitmap|.
   cc::SkiaPaintCanvas canvas(bitmap);
-  media::Context3D context =
-      context_provider ? media::Context3D(context_provider->ContextGL(),
-                                          context_provider->GrContext())
-                       : media::Context3D();
-  renderer.Copy(video_frame, &canvas, context);
+  renderer.Copy(video_frame, &canvas, context, context_support);
 
   NotifyComplete(std::move(bitmap));
 }
diff --git a/chrome/browser/android/feed/feed_journal_bridge.cc b/chrome/browser/android/feed/feed_journal_bridge.cc
index 5fc8d67..1c32417d 100644
--- a/chrome/browser/android/feed/feed_journal_bridge.cc
+++ b/chrome/browser/android/feed/feed_journal_bridge.cc
@@ -26,10 +26,12 @@
 namespace feed {
 
 using base::android::AttachCurrentThread;
+using base::android::JavaByteArrayToByteVector;
 using base::android::JavaRef;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
+using base::android::ToJavaArrayOfByteArray;
 using base::android::ToJavaArrayOfStrings;
 
 namespace {
@@ -40,14 +42,29 @@
     bool success,
     std::vector<std::string> entries) {
   JNIEnv* env = AttachCurrentThread();
+
+  if (success) {
+    RunObjectCallbackAndroid(success_callback,
+                             ToJavaArrayOfByteArray(env, entries));
+  } else {
+    RunObjectCallbackAndroid(failure_callback, nullptr);
+  }
+}
+
+void OnLoadJournalKeyDone(
+    base::android::ScopedJavaGlobalRef<jobject> success_callback,
+    base::android::ScopedJavaGlobalRef<jobject> failure_callback,
+    bool success,
+    std::vector<std::string> entries) {
+  JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobjectArray> j_entries =
       ToJavaArrayOfStrings(env, entries);
 
-  if (!success) {
+  if (success) {
+    RunObjectCallbackAndroid(success_callback, j_entries);
+  } else {
     RunObjectCallbackAndroid(failure_callback, nullptr);
-    return;
   }
-  RunObjectCallbackAndroid(success_callback, j_entries);
 }
 
 void OnStorageCheckExistingCallbackDone(
@@ -55,11 +72,11 @@
     ScopedJavaGlobalRef<jobject> failure_callback,
     bool success,
     bool exists) {
-  if (!success) {
+  if (success) {
+    RunBooleanCallbackAndroid(success_callback, exists);
+  } else {
     RunObjectCallbackAndroid(failure_callback, nullptr);
-    return;
   }
-  RunBooleanCallbackAndroid(success_callback, exists);
 }
 
 void OnStorageBooleanCallbackDone(ScopedJavaGlobalRef<jobject> callback,
@@ -146,8 +163,8 @@
   ScopedJavaGlobalRef<jobject> success_callback(j_success_callback);
   ScopedJavaGlobalRef<jobject> failure_callback(j_failure_callback);
 
-  feed_journal_database_->LoadAllJournalKeys(
-      base::BindOnce(&OnLoadJournalDone, success_callback, failure_callback));
+  feed_journal_database_->LoadAllJournalKeys(base::BindOnce(
+      &OnLoadJournalKeyDone, success_callback, failure_callback));
 }
 
 void FeedJournalBridge::DeleteAllJournals(
@@ -180,10 +197,12 @@
 void FeedJournalBridge::AddAppendOperation(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jstring>& j_value) {
+    const base::android::JavaRef<jbyteArray>& j_value) {
   DCHECK(journal_mutation_);
-  std::string value = ConvertJavaStringToUTF8(j_env, j_value);
-  journal_mutation_->AddAppendOperation(std::move(value));
+  std::vector<uint8_t> bytes_vector;
+  JavaByteArrayToByteVector(j_env, j_value.obj(), &bytes_vector);
+  journal_mutation_->AddAppendOperation(
+      std::string(bytes_vector.begin(), bytes_vector.end()));
 }
 
 void FeedJournalBridge::AddCopyOperation(
diff --git a/chrome/browser/android/feed/feed_journal_bridge.h b/chrome/browser/android/feed/feed_journal_bridge.h
index c7f4a26..e84fb40 100644
--- a/chrome/browser/android/feed/feed_journal_bridge.h
+++ b/chrome/browser/android/feed/feed_journal_bridge.h
@@ -64,7 +64,7 @@
                              const base::android::JavaRef<jobject>& j_this);
   void AddAppendOperation(JNIEnv* j_env,
                           const base::android::JavaRef<jobject>& j_this,
-                          const base::android::JavaRef<jstring>& j_value);
+                          const base::android::JavaRef<jbyteArray>& j_value);
   void AddCopyOperation(
       JNIEnv* j_env,
       const base::android::JavaRef<jobject>& j_this,
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index cf9ba11..f645070fa 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -251,8 +251,16 @@
   ArHitResultList_getSize(arcore_session_.get(), arcore_hit_result_list.get(),
                           &arcore_hit_result_list_size);
 
-  for (int i = 0; i < arcore_hit_result_list_size; i++) {
+  // Go through the list in reverse order so the first hit we encounter is the
+  // furthest.
+  // We will accept the furthest hit and then for the rest require that the hit
+  // be within the actual polygon detected by ArCore. This heuristic allows us
+  // to get better results on floors w/o overestimating the size of tables etc.
+  // See https://crbug.com/872855.
+
+  for (int i = arcore_hit_result_list_size - 1; i >= 0; i--) {
     internal::ScopedArCoreObject<ArHitResult*> arcore_hit;
+
     ArHitResult_create(arcore_session_.get(), arcore_hit.receive());
     if (!arcore_hit.is_valid()) {
       DLOG(ERROR) << "ArHitResult_create failed!";
@@ -262,11 +270,48 @@
     ArHitResultList_getItem(arcore_session_.get(), arcore_hit_result_list.get(),
                             i, arcore_hit.get());
 
-    mojom::XRHitResultPtr mojo_hit = mojom::XRHitResult::New();
-    if (!ArHitResultToXRHitResult(arcore_hit.get(), mojo_hit.get())) {
+    internal::ScopedArCoreObject<ArTrackable*> ar_trackable;
+
+    ArHitResult_acquireTrackable(arcore_session_.get(), arcore_hit.get(),
+                                 ar_trackable.receive());
+    ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
+    ArTrackable_getType(arcore_session_.get(), ar_trackable.get(),
+                        &ar_trackable_type);
+
+    // Only consider hits with plane trackables
+    // TODO(874985): make this configurable or re-evaluate this decision
+    if (AR_TRACKABLE_PLANE != ar_trackable_type) {
+      continue;
+    }
+
+    internal::ScopedArCoreObject<ArPose*> arcore_pose;
+    ArPose_create(arcore_session_.get(), nullptr, arcore_pose.receive());
+    if (!arcore_pose.is_valid()) {
+      DLOG(ERROR) << "ArPose_create failed!";
       return false;
     }
-    hit_results->push_back(std::move(mojo_hit));
+
+    ArHitResult_getHitPose(arcore_session_.get(), arcore_hit.get(),
+                           arcore_pose.get());
+
+    // After the first (furthest) hit, only return hits that are within the
+    // actual detected polygon and not just within than the larger plane.
+    if (!hit_results->empty()) {
+      int32_t in_polygon = 0;
+      ArPlane* ar_plane = ArAsPlane(ar_trackable.get());
+      ArPlane_isPoseInPolygon(arcore_session_.get(), ar_plane,
+                              arcore_pose.get(), &in_polygon);
+      if (!in_polygon)
+        continue;
+    }
+
+    mojom::XRHitResultPtr mojo_hit = mojom::XRHitResult::New();
+    mojo_hit.get()->hit_matrix.resize(16);
+    ArPose_getMatrix(arcore_session_.get(), arcore_pose.get(),
+                     mojo_hit.get()->hit_matrix.data());
+
+    // Insert new results at head to preserver order from ArCore
+    hit_results->insert(hit_results->begin(), std::move(mojo_hit));
   }
   return true;
 }
@@ -325,26 +370,6 @@
   return true;
 }
 
-bool ARCoreImpl::ArHitResultToXRHitResult(ArHitResult* arcore_hit,
-                                          mojom::XRHitResult* hit_result) {
-  DCHECK(IsOnGlThread());
-  DCHECK(arcore_session_.is_valid());
-  DCHECK(arcore_frame_.is_valid());
-
-  internal::ScopedArCoreObject<ArPose*> arcore_pose;
-  ArPose_create(arcore_session_.get(), nullptr, arcore_pose.receive());
-  if (!arcore_pose.is_valid()) {
-    DLOG(ERROR) << "ArPose_create failed!";
-    return false;
-  }
-  ArHitResult_getHitPose(arcore_session_.get(), arcore_hit, arcore_pose.get());
-  hit_result->hit_matrix.resize(16);
-  ArPose_getMatrix(arcore_session_.get(), arcore_pose.get(),
-                   hit_result->hit_matrix.data());
-
-  return true;
-}
-
 bool ARCoreImpl::IsOnGlThread() {
   return gl_thread_task_runner_->BelongsToCurrentThread();
 }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.h b/chrome/browser/android/vr/arcore_device/arcore_impl.h
index 3aff1a7..e1ccc63 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.h
@@ -89,9 +89,6 @@
                                  const gfx::Size& image_size,
                                  gfx::PointF* screen_point);
 
-  bool ArHitResultToXRHitResult(ArHitResult* ar_hit,
-                                mojom::XRHitResult* hit_result);
-
   bool IsOnGlThread();
   base::WeakPtr<ARCoreImpl> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
diff --git a/chrome/browser/android/vr/arcore_device/arcore_shim.cc b/chrome/browser/android/vr/arcore_device/arcore_shim.cc
index 4dccc1e..38ab264 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_shim.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_shim.cc
@@ -42,6 +42,10 @@
   CALL(ArSession_resume)                 \
   CALL(ArSession_setCameraTextureName)   \
   CALL(ArSession_setDisplayGeometry)     \
+  CALL(ArHitResult_acquireTrackable)     \
+  CALL(ArTrackable_getType)              \
+  CALL(ArTrackable_release)              \
+  CALL(ArPlane_isPoseInPolygon)          \
   CALL(ArSession_update)
 
 #define CALL(fn) decltype(&fn) impl_##fn = nullptr;
@@ -176,6 +180,31 @@
   arcore_api->impl_ArHitResult_getHitPose(session, hit_result, out_pose);
 }
 
+void ArHitResult_acquireTrackable(const ArSession* session,
+                                  const ArHitResult* hit_result,
+                                  ArTrackable** out_trackable) {
+  arcore_api->impl_ArHitResult_acquireTrackable(session, hit_result,
+                                                out_trackable);
+}
+
+void ArTrackable_getType(const ArSession* session,
+                         const ArTrackable* trackable,
+                         ArTrackableType* out_trackable_type) {
+  arcore_api->impl_ArTrackable_getType(session, trackable, out_trackable_type);
+}
+
+void ArPlane_isPoseInPolygon(const ArSession* session,
+                             const ArPlane* plane,
+                             const ArPose* pose,
+                             int32_t* out_pose_in_polygon) {
+  arcore_api->impl_ArPlane_isPoseInPolygon(session, plane, pose,
+                                           out_pose_in_polygon);
+}
+
+void ArTrackable_release(ArTrackable* trackable) {
+  arcore_api->impl_ArTrackable_release(trackable);
+}
+
 void ArHitResultList_create(const ArSession* session,
                             ArHitResultList** out_hit_result_list) {
   arcore_api->impl_ArHitResultList_create(session, out_hit_result_list);
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 6c9658f..9564293 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1229,7 +1229,8 @@
           SessionServiceFactory::GetForProfileForSessionRestore(
               [self lastProfile]);
       if (sessionService &&
-          sessionService->RestoreIfNecessary(std::vector<GURL>()))
+          sessionService->RestoreIfNecessary(
+              *base::CommandLine::ForCurrentProcess(), std::vector<GURL>()))
         return NO;
     }
   }
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1b84a87..858136db 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -88,6 +88,7 @@
         <structure name="IDR_NUX_SET_AS_DEFAULT_JS" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default.js" type="chrome_html" />
         <structure name="IDR_NUX_SET_AS_DEFAULT_PROXY_HTML" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default_proxy.html" type="chrome_html" />
         <structure name="IDR_NUX_SET_AS_DEFAULT_PROXY_JS" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default_proxy.js" type="chrome_html" />
+        <structure name="IDR_NUX_CHOOSER_SHARED_CSS" file="resources\welcome\onboarding_welcome\shared\chooser_shared_css.html" type="chrome_html" />
       </if>
       <if expr="is_win">
         <structure name="IDR_WELCOME_WIN10_CSS" file="resources\welcome\welcome_win10.css" type="chrome_html" />
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 93496353..2fcd8a2 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -346,8 +346,7 @@
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 void AddFirstRunNewTabs(StartupBrowserCreator* browser_creator,
                         const std::vector<GURL>& new_tabs) {
-  for (std::vector<GURL>::const_iterator it = new_tabs.begin();
-       it != new_tabs.end(); ++it) {
+  for (auto it = new_tabs.begin(); it != new_tabs.end(); ++it) {
     if (it->is_valid())
       browser_creator->AddFirstRunTab(*it);
   }
diff --git a/chrome/browser/chromeos/arc/input_method_manager/DEPS b/chrome/browser/chromeos/arc/input_method_manager/DEPS
new file mode 100644
index 0000000..ec55aaa
--- /dev/null
+++ b/chrome/browser/chromeos/arc/input_method_manager/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+  # TODO(mash): Fix this. https://crbug.com/890677
+  "arc_input_method_manager_service\.cc": [
+    "+ash/shell.h",
+  ],
+}
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index 98a450d..6fc830b 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/shell.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/stl_util.h"
@@ -97,7 +98,17 @@
   ~ArcProxyInputMethodObserver() override = default;
 
   // input_method::InputMethodEngineBase::Observer overrides:
-  void OnActivate(const std::string& engine_id) override {}
+  void OnActivate(const std::string& engine_id) override {
+    // ash::Shell is not created in the unit tests.
+    if (!ash::Shell::HasInstance())
+      return;
+    const bool was_enabled = keyboard::IsKeyboardEnabled();
+    // Disable fallback virtual keyboard while Android IME is activated.
+    keyboard::SetKeyboardShowOverride(
+        keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
+    if (was_enabled)
+      ash::Shell::Get()->DisableKeyboard();
+  }
   void OnFocus(
       const ui::IMEEngineHandlerInterface::InputContext& context) override {
     owner_->Focus(context.id);
@@ -108,7 +119,18 @@
       const input_method::InputMethodEngineBase::KeyboardEvent& event,
       ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override {}
   void OnReset(const std::string& engine_id) override {}
-  void OnDeactivated(const std::string& engine_id) override {}
+  void OnDeactivated(const std::string& engine_id) override {
+    // ash::Shell is not created in the unit tests.
+    if (!ash::Shell::HasInstance())
+      return;
+    const bool was_enabled = keyboard::IsKeyboardEnabled();
+    // Stop overriding virtual keyboard availability.
+    keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
+    // If the device is still in tablet mode, virtual keyboard may be enabled.
+    const bool is_enabled = keyboard::IsKeyboardEnabled();
+    if (!was_enabled && is_enabled)
+      ash::Shell::Get()->EnableKeyboard();
+  }
   void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) override {
     owner_->UpdateTextInputState();
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
index 77907e18..7dd2fb9b 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/stl_util.h"
@@ -31,6 +32,7 @@
 #include "ui/base/ime/mock_ime_input_context_handler.h"
 #include "ui/base/ime/mock_input_method.h"
 #include "ui/keyboard/keyboard_controller.h"
+#include "ui/keyboard/keyboard_util.h"
 
 namespace arc {
 namespace {
@@ -200,7 +202,9 @@
   DISALLOW_COPY_AND_ASSIGN(TestInputMethodManagerBridge);
 };
 
-class ArcInputMethodManagerServiceTest : public testing::Test {
+// TODO(crbug.com/890677): Stop inheriting ash::AshTestBase once ash::Shell
+// dependency is removed from ArcInputMethodManagerService.
+class ArcInputMethodManagerServiceTest : public ash::AshTestBase {
  protected:
   ArcInputMethodManagerServiceTest()
       : arc_service_manager_(std::make_unique<ArcServiceManager>()) {}
@@ -219,12 +223,13 @@
   }
 
   void SetUp() override {
+    ash::AshTestBase::SetUp();
+    SetRunningOutsideAsh();
     ui::IMEBridge::Initialize();
     input_method_manager_ = new TestInputMethodManager();
     chromeos::input_method::InputMethodManager::Initialize(
         input_method_manager_);
     tablet_mode_client_ = std::make_unique<TabletModeClient>();
-    keyboard_controller_ = std::make_unique<keyboard::KeyboardController>();
     profile_ = std::make_unique<TestingProfile>();
     service_ = ArcInputMethodManagerService::GetForBrowserContextForTesting(
         profile_.get());
@@ -237,18 +242,16 @@
     test_bridge_ = nullptr;
     service_->Shutdown();
     profile_.reset(nullptr);
-    keyboard_controller_.reset(nullptr);
     tablet_mode_client_.reset(nullptr);
     chromeos::input_method::InputMethodManager::Shutdown();
     ui::IMEBridge::Shutdown();
+    ash::AshTestBase::TearDown();
   }
 
  private:
-  content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<ArcServiceManager> arc_service_manager_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<TabletModeClient> tablet_mode_client_;
-  std::unique_ptr<keyboard::KeyboardController> keyboard_controller_;
   TestInputMethodManager* input_method_manager_ = nullptr;
   TestInputMethodManagerBridge* test_bridge_ = nullptr;  // Owned by |service_|
 
@@ -783,4 +786,59 @@
   ui::IMEBridge::Get()->SetInputContextHandler(nullptr);
 }
 
+TEST_F(ArcInputMethodManagerServiceTest, DisableFallbackVirtualKeyboard) {
+  namespace ceiu = chromeos::extension_ime_util;
+  using crx_file::id_util::GenerateId;
+
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(kEnableInputMethodFeature);
+  ToggleTabletMode(true);
+
+  // Adding one ARC IME.
+  {
+    const std::string android_ime_id = "test.arc.ime";
+    const std::string display_name = "DisplayName";
+    const std::string settings_url = "url_to_settings";
+    mojom::ImeInfoPtr info = mojom::ImeInfo::New();
+    info->ime_id = android_ime_id;
+    info->display_name = display_name;
+    info->enabled = false;
+    info->settings_url = settings_url;
+
+    std::vector<mojom::ImeInfoPtr> info_array;
+    info_array.emplace_back(std::move(info));
+    service()->OnImeInfoChanged(std::move(info_array));
+  }
+  // The proxy IME engine should be added.
+  ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
+  ui::IMEEngineHandlerInterface* engine_handler =
+      std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
+  // Enable it
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
+
+  const std::string extension_ime_id =
+      ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
+  const std::string component_extension_ime_id =
+      ceiu::GetComponentInputMethodID(
+          GenerateId("test.component.extension.ime"), "us");
+
+  // Enable Chrome OS virtual keyboard
+  keyboard::SetTouchKeyboardEnabled(true);
+  keyboard::SetKeyboardShowOverride(keyboard::KEYBOARD_SHOW_OVERRIDE_NONE);
+  ASSERT_TRUE(keyboard::IsKeyboardEnabled());
+
+  // It's disabled when the ARC IME is activated.
+  ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
+  engine_handler->Enable(
+      chromeos::extension_ime_util::GetComponentIDByInputMethodID(
+          std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
+              .at(0)
+              .id()));
+  EXPECT_FALSE(keyboard::IsKeyboardEnabled());
+
+  // It's re-enabled when the ARC IME is deactivated.
+  engine_handler->Disable();
+  EXPECT_TRUE(keyboard::IsKeyboardEnabled());
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index f54eb7b..dffbd60 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -707,6 +707,8 @@
     if (metadata->type != drivefs::mojom::FileMetadata::Type::kDirectory) {
       properties_->thumbnail_url = std::make_unique<std::string>(
           base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()}));
+      properties_->cropped_thumbnail_url =
+          std::make_unique<std::string>(*properties_->thumbnail_url);
     }
 
     CompleteGetEntryProperties(drive::FILE_ERROR_OK);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 4e6d07a..57fecfd 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -180,6 +180,7 @@
         TestCase("fileDisplayDrive"),
         TestCase("fileDisplayDrive").TabletMode(),
         TestCase("fileDisplayDrive").EnableDriveFs(),
+        TestCase("fileDisplayDrive").TabletMode().EnableDriveFs(),
         TestCase("fileDisplayDriveOffline").Offline().EnableDriveFs(),
         TestCase("fileDisplayDriveOnline").EnableDriveFs(),
         TestCase("fileDisplayDriveOnline"),
@@ -323,6 +324,48 @@
                       TestCase("checkContextMenuForTeamDriveRoot")));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
+    ContextMenu2, /* context_menu.js */
+    FilesAppBrowserTest,
+    ::testing::Values(
+        TestCase("checkDeleteEnabledForReadWriteFile").EnableDriveFs(),
+        TestCase("checkDeleteDisabledForReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkDeleteDisabledForReadOnlyFile").EnableDriveFs(),
+        TestCase("checkDeleteDisabledForReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkRenameEnabledForReadWriteFile").EnableDriveFs(),
+        TestCase("checkRenameDisabledForReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkRenameDisabledForReadOnlyFile").EnableDriveFs(),
+        TestCase("checkRenameDisabledForReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkShareEnabledForReadWriteFile").EnableDriveFs(),
+        TestCase("checkShareEnabledForReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkShareDisabledForStrictReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkShareEnabledForReadOnlyFile").EnableDriveFs(),
+        TestCase("checkShareEnabledForReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadWriteFile").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkCopyDisabledForStrictReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadOnlyFile").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkCutEnabledForReadWriteFile").EnableDriveFs(),
+        TestCase("checkCutDisabledForReadOnlyDocument").EnableDriveFs(),
+        TestCase("checkCutDisabledForReadOnlyFile").EnableDriveFs(),
+        TestCase("checkCutDisabledForReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkPasteIntoFolderEnabledForReadWriteFolder")
+            .EnableDriveFs(),
+        TestCase("checkPasteIntoFolderDisabledForReadOnlyFolder")
+            .EnableDriveFs(),
+        TestCase("checkNewFolderEnabledInsideReadWriteFolder").EnableDriveFs(),
+        TestCase("checkNewFolderDisabledInsideReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkPasteEnabledInsideReadWriteFolder").EnableDriveFs(),
+        TestCase("checkPasteDisabledInsideReadOnlyFolder").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadWriteFolderInTree").EnableDriveFs(),
+        TestCase("checkCopyEnabledForReadOnlyFolderInTree").EnableDriveFs(),
+        TestCase("checkCutEnabledForReadWriteFolderInTree").EnableDriveFs(),
+        TestCase("checkCutDisabledForReadOnlyFolderInTree").EnableDriveFs(),
+        TestCase("checkPasteEnabledForReadWriteFolderInTree").EnableDriveFs(),
+        TestCase("checkPasteDisabledForReadOnlyFolderInTree").EnableDriveFs(),
+        TestCase("checkContextMenuForTeamDriveRoot").EnableDriveFs()));
+
+WRAPPED_INSTANTIATE_TEST_CASE_P(
     Delete, /* delete.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("deleteMenuItemNoEntrySelected"),
@@ -481,7 +524,9 @@
     FolderShortcuts, /* folder_shortcuts.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("traverseFolderShortcuts"),
-                      TestCase("addRemoveFolderShortcuts")));
+                      TestCase("traverseFolderShortcuts").EnableDriveFs(),
+                      TestCase("addRemoveFolderShortcuts"),
+                      TestCase("addRemoveFolderShortcuts").EnableDriveFs()));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
     SortColumns, /* sort_columns.js */
@@ -627,8 +672,11 @@
     FilesAppBrowserTest,
     ::testing::Values(TestCase("metadataDownloads"),
                       TestCase("metadataDrive"),
+                      TestCase("metadataDrive").EnableDriveFs(),
                       TestCase("metadataTeamDrives"),
-                      TestCase("metadataLargeDrive")));
+                      TestCase("metadataTeamDrives").EnableDriveFs(),
+                      TestCase("metadataLargeDrive"),
+                      TestCase("metadataLargeDrive").EnableDriveFs()));
 
 // Structure to describe an account info.
 struct TestAccountInfo {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index b3a7a0ef..995f237 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -856,7 +856,10 @@
         base::FilePath(entry.target_path).BaseName().value(), entry.pinned,
         entry.shared_option == AddEntriesMessage::SharedOption::SHARED ||
             entry.shared_option ==
-                AddEntriesMessage::SharedOption::SHARED_WITH_ME);
+                AddEntriesMessage::SharedOption::SHARED_WITH_ME,
+        {entry.capabilities.can_share, entry.capabilities.can_copy,
+         entry.capabilities.can_delete, entry.capabilities.can_rename,
+         entry.capabilities.can_add_children});
 
     switch (entry.type) {
       case AddEntriesMessage::FILE: {
@@ -1401,7 +1404,8 @@
                                    kPredefinedProfileSalt);
     drive_volumes_[profile->GetOriginalProfile()] =
         std::make_unique<DriveFsTestVolume>(profile->GetOriginalProfile());
-    if (!IsIncognitoModeTest()) {
+    if (!IsIncognitoModeTest() &&
+        profile->GetPath().BaseName().value() == "user") {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
           base::BindOnce(base::IgnoreResult(&LocalTestVolume::Mount),
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
index 772d2e6..e7ee855 100644
--- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -2418,8 +2418,9 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+// Fails on all chromeos builders https://crbug.com/891573
 TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
-       ReportingActivityTimesSessionTransistions) {
+       DISABLED_ReportingActivityTimesSessionTransistions) {
   DeviceStateTransitions test_states[] = {
       DeviceStateTransitions::kEnterSessionActive,
       DeviceStateTransitions::kPeriodicCheckTriggered,
@@ -2444,8 +2445,9 @@
             device_status_.active_period(0).user_email());
 }
 
+// Fails on all chromeos builders https://crbug.com/891573
 TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
-       ReportingActivityTimesSleepTransistions) {
+       DISABLED_ReportingActivityTimesSleepTransistions) {
   DeviceStateTransitions test_states[] = {
       DeviceStateTransitions::kEnterSessionActive,
       DeviceStateTransitions::kPeriodicCheckTriggered,
@@ -2469,8 +2471,9 @@
             device_status_.active_period(0).user_email());
 }
 
+// Fails on all chromeos builders https://crbug.com/891573
 TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
-       ReportingActivityTimesIdleTransitions) {
+       DISABLED_ReportingActivityTimesIdleTransitions) {
   DeviceStateTransitions test_states[] = {
       DeviceStateTransitions::kEnterSessionActive,
       DeviceStateTransitions::kPeriodicCheckTriggered,
@@ -2496,7 +2499,9 @@
             device_status_.active_period(0).user_email());
 }
 
-TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest, ActivityKeptInPref) {
+// Fails on all chromeos builders https://crbug.com/891573
+TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
+       DISABLED_ActivityKeptInPref) {
   EXPECT_TRUE(
       profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
 
@@ -2534,8 +2539,9 @@
       profile_pref_service_.GetInteger(prefs::kChildScreenTimeMilliseconds));
 }
 
+// Fails on all chromeos builders https://crbug.com/891573
 TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
-       ActivityNotWrittenToLocalState) {
+       DISABLED_ActivityNotWrittenToLocalState) {
   EXPECT_TRUE(local_state_.GetDictionary(prefs::kDeviceActivityTimes)->empty());
 
   DeviceStateTransitions test_states[] = {
diff --git a/chrome/browser/devtools/devtools_eye_dropper.h b/chrome/browser/devtools/devtools_eye_dropper.h
index aaa87d2..5c0c293 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.h
+++ b/chrome/browser/devtools/devtools_eye_dropper.h
@@ -12,7 +12,6 @@
 #include "components/viz/host/client_frame_sink_video_capturer.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "media/renderers/paint_canvas_video_renderer.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace blink {
@@ -59,7 +58,6 @@
   content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
   content::RenderWidgetHost* host_;
   std::unique_ptr<viz::ClientFrameSinkVideoCapturer> video_capturer_;
-  media::PaintCanvasVideoRenderer video_renderer_;
   base::WeakPtrFactory<DevToolsEyeDropper> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsEyeDropper);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 52c173c..d3e0fe37 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -346,12 +346,6 @@
     "Enables experimental support for Accessibility Object Model APIs "
     "that are in development.";
 
-const char kEnableAsmWasmName[] =
-    "Experimental Validate Asm.js and convert to WebAssembly when valid.";
-const char kEnableAsmWasmDescription[] =
-    R"*(Validate Asm.js when "use asm" is present and then convert to )*"
-    R"*(WebAssembly.)*";
-
 const char kEnableWebPaymentsSingleAppUiSkipName[] =
     "Enable Web Payments single app UI skip";
 const char kEnableWebPaymentsSingleAppUiSkipDescription[] =
@@ -643,6 +637,10 @@
 const char kEnableNotificationScrollBarDescription[] =
     "Enable the scroll bar of the notification list in Unified System Tray.";
 
+const char kEnableNewMessageListViewName[] = "Enable new message list view";
+const char kEnableNewMessageListViewDescription[] =
+    "Enable new implementation of message list view.";
+
 const char kEnableNupPrintingName[] = "Enable N-up printing";
 const char kEnableNupPrintingDescription[] =
     "Enable N-up printing in the print preview panel.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1cd3b69..5695b5f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -243,9 +243,6 @@
 extern const char kEnableAccessibilityObjectModelName[];
 extern const char kEnableAccessibilityObjectModelDescription[];
 
-extern const char kEnableAsmWasmName[];
-extern const char kEnableAsmWasmDescription[];
-
 extern const char kEnableAutofillAccountWalletStorageName[];
 extern const char kEnableAutofillAccountWalletStorageDescription[];
 
@@ -414,6 +411,9 @@
 extern const char kEnableNotificationScrollBarName[];
 extern const char kEnableNotificationScrollBarDescription[];
 
+extern const char kEnableNewMessageListViewName[];
+extern const char kEnableNewMessageListViewDescription[];
+
 extern const char kEnableNupPrintingName[];
 extern const char kEnableNupPrintingDescription[];
 
diff --git a/chrome/browser/font_family_cache.cc b/chrome/browser/font_family_cache.cc
index e273b0e..8002ffa 100644
--- a/chrome/browser/font_family_cache.cc
+++ b/chrome/browser/font_family_cache.cc
@@ -78,7 +78,7 @@
                                                   const char* map_name) {
   FontFamilyMap::const_iterator it = font_family_map_.find(map_name);
   if (it != font_family_map_.end()) {
-    ScriptFontMap::const_iterator it2 = it->second.find(script);
+    auto it2 = it->second.find(script);
     if (it2 != it->second.end())
       return it2->second;
   }
@@ -100,7 +100,7 @@
       continue;
 
     ScriptFontMap& map = it.second;
-    for (ScriptFontMap::iterator it2 = map.begin(); it2 != map.end(); ++it2) {
+    for (auto it2 = map.begin(); it2 != map.end(); ++it2) {
       const char* script = it2->first;
       size_t script_length = strlen(script);
 
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index ac8c8b7..bcbf0e43 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/plugins/plugin_observer.h"
 #include "chrome/browser/plugins/reload_plugin_infobar_delegate.h"
 #include "chrome/browser/previews/previews_infobar_delegate.h"
+#include "chrome/browser/previews/previews_lite_page_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -230,6 +231,7 @@
       {"automation", IBD::AUTOMATION_INFOBAR_DELEGATE},
       {"page_load_capping", IBD::PAGE_LOAD_CAPPING_INFOBAR_DELEGATE},
       {"bloated_renderer", IBD::BLOATED_RENDERER_INFOBAR_DELEGATE},
+      {"previews_lite_page", IBD::LITE_PAGE_PREVIEWS_INFOBAR},
   };
   auto id = kIdentifiers.find(name);
   expected_identifiers_.push_back((id == kIdentifiers.end()) ? IBD::INVALID
@@ -416,6 +418,10 @@
       BloatedRendererTabHelper::ShowInfoBar(GetInfoBarService());
       break;
 
+    case IBD::LITE_PAGE_PREVIEWS_INFOBAR:
+      PreviewsLitePageInfoBarDelegate::Create(GetWebContents());
+      break;
+
     default:
       break;
   }
@@ -584,6 +590,10 @@
   ShowAndVerifyUi();
 }
 
+IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_previews_lite_page) {
+  ShowAndVerifyUi();
+}
+
 IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_multiple_infobars) {
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/internal_auth.cc b/chrome/browser/internal_auth.cc
index 35552a5..cdbff42f7c 100644
--- a/chrome/browser/internal_auth.cc
+++ b/chrome/browser/internal_auth.cc
@@ -128,7 +128,7 @@
 bool IsVarValueMapSane(const VarValueMap& map) {
   if (map.size() > kVarsLimit)
     return false;
-  for (VarValueMap::const_iterator it = map.begin(); it != map.end(); ++it) {
+  for (auto it = map.begin(); it != map.end(); ++it) {
     const std::string& var = it->first;
     const std::string& value = it->second;
     if (!IsVarSane(var) || !IsValueSane(value))
@@ -140,7 +140,7 @@
 void ConvertVarValueMapToBlob(const VarValueMap& map, std::string* out) {
   out->clear();
   DCHECK(IsVarValueMapSane(map));
-  for (VarValueMap::const_iterator it = map.begin(); it != map.end(); ++it)
+  for (auto it = map.begin(); it != map.end(); ++it)
     *out += it->first + kVarValueSeparator + it->second + kItemSeparator;
 }
 
diff --git a/chrome/browser/media/cast_remoting_connector.cc b/chrome/browser/media/cast_remoting_connector.cc
index a632376..ba39f521 100644
--- a/chrome/browser/media/cast_remoting_connector.cc
+++ b/chrome/browser/media/cast_remoting_connector.cc
@@ -25,8 +25,7 @@
 #include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
-#if defined(TOOLKIT_VIEWS) && \
-    (!defined(OS_MACOSX) || defined(MAC_VIEWS_BROWSER))
+#if defined(TOOLKIT_VIEWS)
 #include "chrome/browser/ui/views/media_router/media_remoting_dialog_view.h"
 #endif
 
@@ -139,8 +138,7 @@
         media_router::MediaRouterFactory::GetApiForBrowserContext(
             contents->GetBrowserContext()),
         SessionTabHelper::IdForTab(contents),
-#if defined(TOOLKIT_VIEWS) && \
-    (!defined(OS_MACOSX) || defined(MAC_VIEWS_BROWSER))
+#if defined(TOOLKIT_VIEWS)
         base::BindRepeating(
             [](content::WebContents* contents,
                PermissionResultCallback result_callback) {
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index 3280b5e..8594ab1 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -162,10 +162,7 @@
   // Sort by memory consumption, low to high.
   std::sort(processes.begin(), processes.end());
   // Print from high to low.
-  for (ProcessMemoryInformationList::reverse_iterator iter1 =
-          processes.rbegin();
-       iter1 != processes.rend();
-       ++iter1) {
+  for (auto iter1 = processes.rbegin(); iter1 != processes.rend(); ++iter1) {
     log += ProcessMemoryInformation::GetFullTypeNameInEnglish(
             iter1->process_type, iter1->renderer_type);
     if (!iter1->titles.empty()) {
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
index e8c1ddf6bf..b001dab 100644
--- a/chrome/browser/memory_details_linux.cc
+++ b/chrome/browser/memory_details_linux.cc
@@ -124,8 +124,7 @@
   current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
   current_browser.process_name = base::ASCIIToUTF16("chrome");
 
-  for (std::vector<ProcessMemoryInformation>::iterator
-       i = current_browser.processes.begin();
+  for (auto i = current_browser.processes.begin();
        i != current_browser.processes.end(); ++i) {
     // Check if this is one of the child processes whose data we collected
     // on the IO thread, and if so copy over that data.
diff --git a/chrome/browser/pepper_flash_settings_manager.cc b/chrome/browser/pepper_flash_settings_manager.cc
index 0862dc88..ae29694 100644
--- a/chrome/browser/pepper_flash_settings_manager.cc
+++ b/chrome/browser/pepper_flash_settings_manager.cc
@@ -365,8 +365,7 @@
 
   std::vector<PendingRequest> temp_pending_requests;
   temp_pending_requests.swap(pending_requests_);
-  for (std::vector<PendingRequest>::iterator iter =
-           temp_pending_requests.begin();
+  for (auto iter = temp_pending_requests.begin();
        iter != temp_pending_requests.end(); ++iter) {
     switch (iter->type) {
       case INVALID_REQUEST_TYPE:
@@ -664,8 +663,8 @@
 
   state_ = STATE_ERROR;
   std::vector<std::pair<uint32_t, RequestType>> notifications;
-  for (std::vector<PendingRequest>::iterator iter = pending_requests_.begin();
-       iter != pending_requests_.end(); ++iter) {
+  for (auto iter = pending_requests_.begin(); iter != pending_requests_.end();
+       ++iter) {
     notifications.push_back(std::make_pair(iter->id, iter->type));
   }
   pending_requests_.clear();
@@ -749,9 +748,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   scoped_refptr<Core> protector(this);
-  for (std::vector<std::pair<uint32_t, RequestType>>::const_iterator iter =
-           notifications.begin();
-       iter != notifications.end(); ++iter) {
+  for (auto iter = notifications.begin(); iter != notifications.end(); ++iter) {
     // Check |manager_| for each iteration in case it is destroyed in one of
     // the callbacks.
     if (!manager_.get())
@@ -800,8 +797,7 @@
 
   DLOG_IF(ERROR, !success) << "DeauthorizeContentLicenses returned error";
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -825,8 +821,7 @@
 
   DLOG_IF(ERROR, !success) << "GetPermissionSettings returned error";
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -848,8 +843,7 @@
 
   DLOG_IF(ERROR, !success) << "SetDefaultPermission returned error";
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -871,8 +865,7 @@
 
   DLOG_IF(ERROR, !success) << "SetSitePermission returned error";
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -892,8 +885,7 @@
   if (state_ == STATE_DETACHED)
     return;
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -915,8 +907,7 @@
 
   DLOG_IF(ERROR, !success) << "ClearSiteData returned error";
 
-  std::map<uint32_t, RequestType>::iterator iter =
-      pending_responses_.find(request_id);
+  auto iter = pending_responses_.find(request_id);
   if (iter == pending_responses_.end())
     return;
 
@@ -957,8 +948,7 @@
   plugin_service->GetPluginInfoArray(
       GURL(), content::kFlashPluginSwfMimeType, false, &plugins, NULL);
 
-  for (std::vector<content::WebPluginInfo>::iterator iter = plugins.begin();
-       iter != plugins.end(); ++iter) {
+  for (auto iter = plugins.begin(); iter != plugins.end(); ++iter) {
     if (iter->is_pepper_plugin() && plugin_prefs->IsPluginEnabled(*iter)) {
       if (plugin_info)
         *plugin_info = *iter;
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 0c8febd9..cd51c6c 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -18,6 +18,9 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/infobars/mock_infobar_service.h"
+#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
+#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_service.h"
@@ -27,6 +30,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_test_utils.h"
+#include "components/infobars/core/infobar.h"
 #include "components/previews/core/previews_features.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
@@ -58,7 +62,7 @@
   }
 
   void SetUp() override {
-    SetUpLitePageTest(false);
+    SetUpLitePageTest(false /* use_timeout */);
 
     InProcessBrowserTest::SetUp();
   }
@@ -152,12 +156,24 @@
     g_browser_process->network_quality_tracker()
         ->ReportEffectiveConnectionTypeForTesting(
             net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+    // Some tests shouldn't bother with the notification InfoBar. Just set the
+    // state on the decider so it isn't required.
+    PreviewsService* previews_service =
+        PreviewsServiceFactory::GetForProfile(browser()->profile());
+    PreviewsLitePageDecider* decider =
+        previews_service->previews_lite_page_decider();
+    decider->SetUserHasSeenUINotification();
   }
 
   content::WebContents* GetWebContents() const {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
 
+  InfoBarService* GetInfoBarService() {
+    return InfoBarService::FromWebContents(GetWebContents());
+  }
+
   void ExecuteScript(const std::string& script) const {
     EXPECT_TRUE(content::ExecuteScript(GetWebContents(), script));
   }
@@ -650,7 +666,7 @@
   ~PreviewsLitePageServerTimeoutBrowserTest() override = default;
 
   void SetUp() override {
-    SetUpLitePageTest(true);
+    SetUpLitePageTest(true /* use_timeout */);
 
     InProcessBrowserTest::SetUp();
   }
@@ -799,3 +815,102 @@
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(200));
   VerifyPreviewNotLoaded();
 }
+
+class PreviewsLitePageNotificationDSEnabledBrowserTest
+    : public PreviewsLitePageServerBrowserTest {
+ public:
+  PreviewsLitePageNotificationDSEnabledBrowserTest() = default;
+
+  ~PreviewsLitePageNotificationDSEnabledBrowserTest() override = default;
+
+  void SetUp() override {
+    SetUpLitePageTest(false /* use_timeout */);
+
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    g_browser_process->network_quality_tracker()
+        ->ReportEffectiveConnectionTypeForTesting(
+            net::EFFECTIVE_CONNECTION_TYPE_2G);
+  }
+};
+
+// Previews InfoBar (which these tests trigger) does not work on Mac.
+// See https://crbug.com/782322 for detail.
+// Also occasional flakes on win7 (https://crbug.com/789542).
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+#define MAYBE_LitePagePreviewsInfoBarDataSaverUser \
+  LitePagePreviewsInfoBarDataSaverUser
+#else
+#define MAYBE_LitePagePreviewsInfoBarDataSaverUser \
+  DISABLED_LitePagePreviewsInfoBarDataSaverUser
+#endif
+IN_PROC_BROWSER_TEST_F(PreviewsLitePageNotificationDSEnabledBrowserTest,
+                       MAYBE_LitePagePreviewsInfoBarDataSaverUser) {
+  // Ensure the preview is not shown the first time before the infobar is shown
+  // for users who have DRP enabled.
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(200));
+
+  VerifyPreviewNotLoaded();
+  EXPECT_EQ(1U, GetInfoBarService()->infobar_count());
+  histogram_tester.ExpectBucketCount(
+      "Previews.ServerLitePage.IneligibleReasons",
+      PreviewsLitePageNavigationThrottle::IneligibleReason::kInfoBarNotSeen, 1);
+
+  ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(200));
+  EXPECT_EQ(0U, GetInfoBarService()->infobar_count());
+  VerifyPreviewLoaded();
+}
+
+class PreviewsLitePageNotificationDSDisabledBrowserTest
+    : public PreviewsLitePageServerBrowserTest {
+ public:
+  PreviewsLitePageNotificationDSDisabledBrowserTest() = default;
+
+  ~PreviewsLitePageNotificationDSDisabledBrowserTest() override = default;
+
+  void SetUp() override {
+    SetUpLitePageTest(false /* use_timeout */);
+
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    g_browser_process->network_quality_tracker()
+        ->ReportEffectiveConnectionTypeForTesting(
+            net::EFFECTIVE_CONNECTION_TYPE_2G);
+  }
+
+  // Overrides the cmd line in PreviewsLitePageServerBrowserTest and leave out
+  // the flag to enable DataSaver.
+  void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitchASCII("force-effective-connection-type", "Slow-2G");
+    // Resolve all localhost subdomains to plain localhost so that Chrome's Test
+    // DNS resolver doesn't get upset.
+    cmd->AppendSwitchASCII(
+        "host-rules", "MAP *.localhost 127.0.0.1, MAP *.127.0.0.1 127.0.0.1");
+  }
+};
+
+// Previews InfoBar (which these tests trigger) does not work on Mac.
+// See https://crbug.com/782322 for detail.
+// Also occasional flakes on win7 (https://crbug.com/789542).
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+#define MAYBE_LitePagePreviewsInfoBarNonDataSaverUser \
+  LitePagePreviewsInfoBarNonDataSaverUser
+#else
+#define MAYBE_LitePagePreviewsInfoBarNonDataSaverUser \
+  DISABLED_LitePagePreviewsInfoBarNonDataSaverUser
+#endif
+IN_PROC_BROWSER_TEST_F(PreviewsLitePageNotificationDSDisabledBrowserTest,
+                       MAYBE_LitePagePreviewsInfoBarNonDataSaverUser) {
+  ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(200));
+  VerifyPreviewNotLoaded();
+  EXPECT_EQ(0U, GetInfoBarService()->infobar_count());
+}
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
index 784f863..3a02baeb 100644
--- a/chrome/browser/previews/previews_lite_page_decider.cc
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -10,6 +10,7 @@
 #include "base/time/default_tick_clock.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
+#include "chrome/browser/previews/previews_lite_page_infobar_delegate.h"
 #include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -66,8 +67,7 @@
 
   void DidFinishNavigation(content::NavigationHandle* handle) override {
     if (ui_shown_callback_ && handle->GetNetErrorCode() == net::OK) {
-      // TODO(robertogden): Call PreviewsLitePageInfobarDelegate::Create and
-      // pass it the callback instead.
+      PreviewsLitePageInfoBarDelegate::Create(web_contents());
       std::move(ui_shown_callback_).Run();
     }
     DestroySelf();
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
index a6779d9f..54e6fda 100644
--- a/chrome/browser/previews/previews_lite_page_decider_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -141,6 +141,8 @@
     return decider_.get();
   }
 
+  PreviewsLitePageDecider* decider() { return decider_.get(); }
+
  private:
   std::unique_ptr<data_reduction_proxy::DataReductionProxyTestContext>
       drp_test_context_;
@@ -176,7 +178,8 @@
       GetManagerWithDRPEnabled(true);
   EXPECT_TRUE(manager->NeedsToNotifyUser());
 
-  manager->NotifyUser(web_contents());
+  // Simulate the callback being run.
+  decider()->SetUserHasSeenUINotification();
 
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL(kTestUrl));
diff --git a/chrome/browser/previews/previews_lite_page_infobar_delegate.cc b/chrome/browser/previews/previews_lite_page_infobar_delegate.cc
new file mode 100644
index 0000000..e24cb2c5
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_infobar_delegate.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/previews_lite_page_infobar_delegate.h"
+
+#include <memory>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/time/default_tick_clock.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/infobars/core/infobar.h"
+#include "components/previews/core/previews_experiments.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+void RecordInfoBarAction(
+    PreviewsLitePageInfoBarDelegate::PreviewsLitePageInfoBarAction action) {
+  UMA_HISTOGRAM_ENUMERATION("Previews.LitePageNotificationInfoBar", action);
+}
+
+}  // namespace
+
+// static
+void PreviewsLitePageInfoBarDelegate::Create(
+    content::WebContents* web_contents) {
+  InfoBarService* infobar_service =
+      InfoBarService::FromWebContents(web_contents);
+  if (!infobar_service)
+    return;
+
+  std::unique_ptr<PreviewsLitePageInfoBarDelegate> delegate(
+      new PreviewsLitePageInfoBarDelegate());
+
+  std::unique_ptr<infobars::InfoBar> infobar_ptr(
+      infobar_service->CreateConfirmInfoBar(std::move(delegate)));
+
+  RecordInfoBarAction(kInfoBarShown);
+  infobar_service->AddInfoBar(std::move(infobar_ptr));
+}
+
+PreviewsLitePageInfoBarDelegate::PreviewsLitePageInfoBarDelegate() {}
+PreviewsLitePageInfoBarDelegate::~PreviewsLitePageInfoBarDelegate() {}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+PreviewsLitePageInfoBarDelegate::GetIdentifier() const {
+  return LITE_PAGE_PREVIEWS_INFOBAR;
+}
+
+// TODO(robertogden): Add link on Android.
+
+int PreviewsLitePageInfoBarDelegate::GetIconId() const {
+  // TODO(robertogden): Add an Android icon.
+  return kNoIconID;
+}
+
+void PreviewsLitePageInfoBarDelegate::InfoBarDismissed() {
+  RecordInfoBarAction(kInfoBarDismissed);
+}
+
+base::string16 PreviewsLitePageInfoBarDelegate::GetMessageText() const {
+  return l10n_util::GetStringUTF16(IDS_LITE_PAGE_PREVIEWS_MESSAGE);
+}
+
+int PreviewsLitePageInfoBarDelegate::GetButtons() const {
+  return BUTTON_NONE;
+}
diff --git a/chrome/browser/previews/previews_lite_page_infobar_delegate.h b/chrome/browser/previews/previews_lite_page_infobar_delegate.h
new file mode 100644
index 0000000..1a361db
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_infobar_delegate.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_INFOBAR_DELEGATE_H_
+
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+// This InfoBar notifies the user that Data Saver now also applies to HTTPS
+// pages.
+class PreviewsLitePageInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+  ~PreviewsLitePageInfoBarDelegate() override;
+
+  // Actions taken on the infobar. This enum must remain synchronized with the
+  // enum of the same name in metrics/histograms/enums.xml.
+  enum PreviewsLitePageInfoBarAction {
+    kInfoBarShown = 0,
+    kInfoBarDismissed = 1,
+    kMaxValue = kInfoBarDismissed,
+  };
+
+  // Shows the InfoBar.
+  static void Create(content::WebContents* web_contents);
+
+ private:
+  PreviewsLitePageInfoBarDelegate();
+
+  // ConfirmInfoBarDelegate:
+  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+  void InfoBarDismissed() override;
+  int GetButtons() const override;
+  int GetIconId() const override;
+  base::string16 GetMessageText() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageInfoBarDelegate);
+};
+
+#endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/previews/previews_lite_page_infobar_delegate_unittest.cc b/chrome/browser/previews/previews_lite_page_infobar_delegate_unittest.cc
new file mode 100644
index 0000000..09a5815
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_infobar_delegate_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/previews_lite_page_infobar_delegate.h"
+
+#include "base/bind_helpers.h"
+#include "base/strings/string16.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "build/build_config.h"
+#include "chrome/browser/infobars/mock_infobar_service.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "components/infobars/core/infobar.h"
+#include "components/previews/core/previews_features.h"
+#include "ui/base/l10n/l10n_util.h"
+
+class PreviewsLitePageInfoBarDelegateUnitTest
+    : public ChromeRenderViewHostTestHarness {
+ protected:
+  PreviewsLitePageInfoBarDelegateUnitTest() = default;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    MockInfoBarService::CreateForWebContents(web_contents());
+  }
+
+  PreviewsLitePageInfoBarDelegate* CreateInfoBar() {
+    PreviewsLitePageInfoBarDelegate::Create(web_contents());
+    EXPECT_EQ(1U, infobar_service()->infobar_count());
+    return static_cast<PreviewsLitePageInfoBarDelegate*>(
+        infobar_service()->infobar_at(0)->delegate());
+  }
+
+  InfoBarService* infobar_service() {
+    return InfoBarService::FromWebContents(web_contents());
+  }
+};
+
+// TODO(crbug/782740): Test temporarily disabled on Windows because it crashes
+// on trybots.
+#if defined(OS_WIN)
+#define DISABLE_ON_WINDOWS(x) DISABLED_##x
+#else
+#define DISABLE_ON_WINDOWS(x) x
+#endif
+TEST_F(PreviewsLitePageInfoBarDelegateUnitTest,
+       DISABLE_ON_WINDOWS(InfoBarUserDismissal)) {
+  base::HistogramTester tester;
+  ConfirmInfoBarDelegate* infobar = CreateInfoBar();
+
+  // Simulate dismissing the infobar.
+  infobar->InfoBarDismissed();
+  infobar_service()->infobar_at(0)->RemoveSelf();
+  EXPECT_EQ(0U, infobar_service()->infobar_count());
+
+  tester.ExpectBucketCount("Previews.LitePageNotificationInfoBar",
+                           PreviewsLitePageInfoBarDelegate::kInfoBarDismissed,
+                           1);
+}
+
+TEST_F(PreviewsLitePageInfoBarDelegateUnitTest,
+       DISABLE_ON_WINDOWS(LitePagePreviewInfoBarTest)) {
+  base::HistogramTester tester;
+  ConfirmInfoBarDelegate* infobar = CreateInfoBar();
+
+  tester.ExpectUniqueSample("Previews.LitePageNotificationInfoBar",
+                            PreviewsLitePageInfoBarDelegate::kInfoBarShown, 1);
+
+  // Check the strings.
+  ASSERT_TRUE(infobar);
+  ASSERT_EQ(l10n_util::GetStringUTF16(IDS_LITE_PAGE_PREVIEWS_MESSAGE),
+            infobar->GetMessageText());
+  ASSERT_EQ(base::string16(), infobar->GetLinkText());
+  ASSERT_EQ(PreviewsLitePageInfoBarDelegate::kNoIconID, infobar->GetIconId());
+}
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 76bae81..4992c3e 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -213,7 +213,16 @@
                               reason);
   }
 
-  return blacklist_reasons.empty();
+  if (!blacklist_reasons.empty())
+    return false;
+
+  if (manager_->NeedsToNotifyUser()) {
+    manager_->NotifyUser(navigation_handle()->GetWebContents());
+    UMA_HISTOGRAM_ENUMERATION("Previews.ServerLitePage.IneligibleReasons",
+                              IneligibleReason::kInfoBarNotSeen);
+    return false;
+  }
+  return true;
 }
 
 // static
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
index 1501fa1..7162747 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -37,7 +37,8 @@
     kHttpPost = 1,
     kSubframeNavigation = 2,
     kServerUnavailable = 3,
-    kMaxValue = kServerUnavailable,
+    kInfoBarNotSeen = 4,
+    kMaxValue = kInfoBarNotSeen,
   };
 
   // The response type from the previews server. This enum must
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
index d36687e..7ea111f 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h
@@ -41,7 +41,6 @@
   // Note: |NeedsToToNotify| is intentionally separate from |NotifyUser| for
   // ease of testing and metrics collection without changing the notification
   // state.
-
   // Returns true if the UI notification needs to be shown to the user before
   // this preview can be shown.
   virtual bool NeedsToNotifyUser() = 0;
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 23da92f..3b6c0b6 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -868,8 +868,7 @@
   to_send.append(current_dir.value());
 
   const std::vector<std::string>& argv = cmd_line.argv();
-  for (std::vector<std::string>::const_iterator it = argv.begin();
-      it != argv.end(); ++it) {
+  for (auto it = argv.begin(); it != argv.end(); ++it) {
     to_send.push_back(kTokenDelimiter);
     to_send.append(*it);
   }
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
index 30bc448..133c387 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
@@ -536,9 +536,6 @@
 
 bool TabManagerDelegate::KillTab(LifecycleUnit* lifecycle_unit,
                                  ::mojom::LifecycleUnitDiscardReason reason) {
-  DecisionDetails decision_details;
-  if (!lifecycle_unit->CanDiscard(reason, &decision_details))
-    return false;
   bool did_discard = lifecycle_unit->Discard(reason);
   return did_discard;
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index 079d7af2..a676b7f 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -229,19 +229,18 @@
           .withoutHints()
           .withSpeechAndBraille(range, null, 'navigate');
 
-      checkSpeechOutput('play|Button|begin playback|audio|Tool bar',
+      checkSpeechOutput('play|Button|audio|Tool bar',
           [
             {value: new Output.EarconAction('BUTTON'), start: 0, end: 4},
-            {value: 'description', start: 12, end: 26},
-            {value: 'name', start: 27, end: 32},
-            {value: 'role', start: 33, end: 41}
+            {value: 'name', start: 12, end: 17},
+            {value: 'role', start: 18, end: 26}
           ],
           o);
 
       checkBrailleOutput(
-          'play btn begin playback audio tlbar',
-          [{value: new Output.NodeSpan(el), start: 0, end: 23},
-           {value: new Output.NodeSpan(el.parent), start: 24, end: 35}],
+          'play btn audio tlbar',
+          [{value: new Output.NodeSpan(el), start: 0, end: 8},
+           {value: new Output.NodeSpan(el.parent), start: 9, end: 20}],
           o);
 
       // TODO(dmazzoni/dtseng): Replace with a query.
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html
index 9d59c9e..1903cc45 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -18,11 +18,13 @@
         --paper-radio-group-item-padding: 0;
       }
 
-      :host([error-state_='0']) #pageSettingsCustomInput {
+      :host([error-state_='0']) #pageSettingsCustomInput,
+      :host([error-state_='3']) #pageSettingsCustomInput {
         --cr-input-error-display: none;
       }
 
-      :host(:not([error-state_='0'])) #customRadioButton {
+      :host([error-state_='1']) #customRadioButton,
+      :host([error-state_='2']) #customRadioButton {
         /* Margin = -1 * error height = -1 * (16px + 2 lines * line-height) */
         --cr-radio-button-disc: {
           margin-top: calc(-16px - 2 * .75rem);
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.js b/chrome/browser/resources/print_preview/new/pages_settings.js
index 5bc0f1a..07c2d9b 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -10,6 +10,7 @@
   NO_ERROR: 0,
   INVALID_SYNTAX: 1,
   OUT_OF_BOUNDS: 2,
+  EMPTY: 3,
 };
 
 /** @enum {string} */
@@ -152,9 +153,13 @@
    * @private
    */
   computePagesToPrint_: function() {
-    if (this.optionSelected_ === PagesValue.ALL || this.inputString_ === '') {
+    if (this.optionSelected_ === PagesValue.ALL) {
       this.errorState_ = PagesInputErrorState.NO_ERROR;
       return this.allPagesArray_;
+    } else if (this.inputString_ === '') {
+      if (this.errorState_ !== PagesInputErrorState.NO_ERROR)
+        this.errorState_ = PagesInputErrorState.EMPTY;
+      return this.pagesToPrint_;
     }
 
     const pages = [];
@@ -283,11 +288,18 @@
     if (this.settings === undefined || this.pagesToPrint_ === undefined)
       return;
 
-    if (this.errorState_ != PagesInputErrorState.NO_ERROR) {
+    if (this.errorState_ === PagesInputErrorState.EMPTY) {
+      this.setSettingValid('pages', false);
+      this.$.pageSettingsCustomInput.invalid = false;
+      return;
+    }
+
+    if (this.errorState_ !== PagesInputErrorState.NO_ERROR) {
       this.setSettingValid('pages', false);
       this.$.pageSettingsCustomInput.invalid = true;
       return;
     }
+
     this.$.pageSettingsCustomInput.invalid = false;
     this.setSettingValid('pages', true);
     const nupPages = this.getNupPages_();
@@ -312,8 +324,14 @@
 
   /** @private */
   resetIfEmpty_: function() {
-    if (this.inputString_ === '')
-      this.optionSelected_ = PagesValue.ALL;
+    if (this.inputString_ !== '')
+      return;
+
+    this.optionSelected_ = PagesValue.ALL;
+
+    // Manually set tab index to -1, so that this is not identified as the
+    // target for the radio group if the user navigates back.
+    this.$.customRadioButton.tabIndex = -1;
   },
 
   /**
@@ -337,9 +355,10 @@
    * @private
    */
   onCustomRadioBlur_: function(event) {
-    if (event.relatedTarget !=
-        /** @type {!CrInputElement} */
-        (this.$.pageSettingsCustomInput).inputElement) {
+    if (event.relatedTarget != this.$.pageSettingsCustomInput &&
+        event.relatedTarget !=
+            /** @type {!CrInputElement} */
+            (this.$.pageSettingsCustomInput).inputElement) {
       this.resetIfEmpty_();
     }
   },
@@ -353,10 +372,6 @@
 
     if (event.relatedTarget != this.$.customRadioButton) {
       this.resetIfEmpty_();
-
-      // Manually set tab index to -1, so that this is not identified as the
-      // target for the radio group if the user navigates back.
-      this.$.customRadioButton.tabIndex = -1;
     }
   },
 
@@ -390,8 +405,10 @@
    * @private
    */
   getHintMessage_: function() {
-    if (this.errorState_ == PagesInputErrorState.NO_ERROR)
+    if (this.errorState_ == PagesInputErrorState.NO_ERROR ||
+        this.errorState_ == PagesInputErrorState.EMPTY) {
       return '';
+    }
 
     let formattedMessage = '';
     if (this.errorState_ == PagesInputErrorState.INVALID_SYNTAX) {
@@ -413,7 +430,8 @@
    * @private
    */
   hintHidden_: function() {
-    return this.errorState_ == PagesInputErrorState.NO_ERROR;
+    return this.errorState_ == PagesInputErrorState.NO_ERROR ||
+        this.errorState_ == PagesInputErrorState.EMPTY;
   }
 });
 })();
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index ec867412..216ff5d5 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -21,7 +21,7 @@
 <link rel="import" href="../crostini_page/crostini_page.html">
 <link rel="import" href="../device_page/device_page.html">
 <link rel="import" href="../internet_page/internet_page.html">
-<link rel="import" href="../multidevice_page/multidevice_page_container.html">
+<link rel="import" href="../multidevice_page/multidevice_page.html">
 </if>
 
 <if expr="not chromeos">
@@ -122,12 +122,8 @@
             if="[[canShowMultideviceSection_(showMultidevice, pageVisibility)]]"
             restamp>
           <settings-section page-title="$i18n{multidevicePageTitle}"
-              section="multidevice"
-              hidden$="[[!doesChromebookSupportMultiDeviceSection_]]">
-            <settings-multidevice-page-container
-                does-chromebook-support-multi-device-features=
-                    "{{doesChromebookSupportMultiDeviceSection_}}">
-            </settings-multidevice-page-container>
+              section="multidevice">
+            <settings-multidevice-page></settings-multidevice-page>
           </settings-section>
         </template>
 </if>
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index b875e7f8..d1cfd958 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -89,16 +89,6 @@
       type: Boolean,
       computed: 'computeShowSecondaryUserBanner_(hasExpandedSection_)',
     },
-
-    /**
-     * Whether the account supports the features controlled in the multidevice
-     * section.
-     * @private {boolean}
-     */
-    doesChromebookSupportMultiDeviceSection_: {
-      type: Boolean,
-      value: false,
-    },
     // </if>
 
     /** @private {!settings.Route|undefined} */
diff --git a/chrome/browser/resources/settings/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
index 46e84a10..30149cc 100644
--- a/chrome/browser/resources/settings/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
@@ -12,7 +12,6 @@
     ":multidevice_feature_item",
     ":multidevice_feature_toggle",
     ":multidevice_page",
-    ":multidevice_page_container",
     ":multidevice_subpage",
     ":multidevice_tether_item",
   ]
@@ -62,15 +61,6 @@
     ":multidevice_feature_behavior",
     "../controls:password_prompt_dialog",
     "//ui/webui/resources/js:cr",
-  ]
-}
-
-js_library("multidevice_page_container") {
-  deps = [
-    ":multidevice_browser_proxy",
-    ":multidevice_constants",
-    ":multidevice_feature_behavior",
-    "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
index 4dc80bd..5001f6bc 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
index 4c5c871c..f722db6 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
@@ -11,7 +11,7 @@
 Polymer({
   is: 'settings-multidevice-page',
 
-  behaviors: [MultiDeviceFeatureBehavior],
+  behaviors: [MultiDeviceFeatureBehavior, WebUIListenerBehavior],
 
   properties: {
     /**
@@ -72,8 +72,15 @@
   browserProxy_: null,
 
   /** @override */
-  created: function() {
+  ready: function() {
     this.browserProxy_ = settings.MultiDeviceBrowserProxyImpl.getInstance();
+
+    this.addWebUIListener(
+        'settings.updateMultidevicePageContentData',
+        this.onPageContentDataChanged_.bind(this));
+
+    this.browserProxy_.getPageContentData().then(
+        this.onPageContentDataChanged_.bind(this));
   },
 
   /**
@@ -307,4 +314,12 @@
     this.browserProxy_.removeHostDevice();
     settings.navigateTo(settings.routes.MULTIDEVICE);
   },
+
+  /**
+   * @param {!MultiDevicePageContentData} newData
+   * @private
+   */
+  onPageContentDataChanged_: function(newData) {
+    this.pageContentData = newData;
+  },
 });
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html b/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html
deleted file mode 100644
index c804083..0000000
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="multidevice_browser_proxy.html">
-<link rel="import" href="multidevice_constants.html">
-<link rel="import" href="multidevice_feature_behavior.html">
-<link rel="import" href="multidevice_page.html">
-
-<dom-module id="settings-multidevice-page-container">
-  <template>
-    <template is="dom-if"
-        if="[[doesChromebookSupportMultiDeviceFeatures]]"
-        restamp>
-      <settings-multidevice-page page-content-data="[[pageContentData]]">
-      </settings-multidevice-page>
-    </template>
-  </template>
-  <script src="multidevice_page_container.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js b/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js
deleted file mode 100644
index dd74a92..0000000
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js
+++ /dev/null
@@ -1,87 +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 Container element that interfaces with the
- * MultiDeviceBrowserProxy to ensure that if there is not a host device or
- * potential host device
- *     (a) the entire multidevice settings-section is hidden and
- *     (b) the settings-multidevice-page is not attched to the DOM.
- * It also provides the settings-multidevice-page with the data it needs to
- * provide the user with the correct infomation and call(s) to action based on
- * the data retrieved from the browser proxy (i.e. the mode_ property).
- */
-
-cr.exportPath('settings');
-
-Polymer({
-  is: 'settings-multidevice-page-container',
-
-  behaviors: [MultiDeviceFeatureBehavior, WebUIListenerBehavior],
-
-  properties: {
-    /**
-     * Whether the Chromebook is capable of enabling Better Together features.
-     * @type {boolean}
-     */
-    doesChromebookSupportMultiDeviceFeatures: {
-      type: Boolean,
-      computed:
-          'computeDoesChromebookSupportMultiDeviceFeatures(pageContentData)',
-      notify: true,
-    },
-  },
-
-  /** @private {?settings.MultiDeviceBrowserProxy} */
-  browserProxy_: null,
-
-  /** @override */
-  ready: function() {
-    this.browserProxy_ = settings.MultiDeviceBrowserProxyImpl.getInstance();
-
-    this.addWebUIListener(
-        'settings.updateMultidevicePageContentData',
-        this.onPageContentDataChanged_.bind(this));
-
-    this.browserProxy_.getPageContentData().then(
-        this.onPageContentDataChanged_.bind(this));
-  },
-
-  /**
-   * @param {!MultiDevicePageContentData} newData
-   * @private
-   */
-  onPageContentDataChanged_: function(newData) {
-    if (!this.isStatusChangeValid_(newData)) {
-      console.error('Invalid status change');
-      return;
-    }
-    this.pageContentData = newData;
-  },
-
-  /**
-   * If the new mode corresponds to no eligible host or unset potential hosts
-   * (i.e. NO_ELIGIBLE_HOSTS or NO_HOST_SET), then newHostDeviceName should be
-   * falsy. Otherwise it should be truthy.
-   * @param {!MultiDevicePageContentData} newData
-   * @private
-   */
-  isStatusChangeValid_: function(newData) {
-    const noHostModes = [
-      settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS,
-      settings.MultiDeviceSettingsMode.NO_HOST_SET,
-    ];
-    return !newData.hostDeviceName === noHostModes.includes(newData.mode);
-  },
-
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeDoesChromebookSupportMultiDeviceFeatures: function() {
-    return !!this.pageContentData &&
-        this.isFeatureSupported(
-            settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE);
-  },
-});
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html
index 383a1b23..5b45963 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html
@@ -115,7 +115,7 @@
     <cr-dialog id="forgetDeviceDialog">
       <div slot="title">$i18n{multideviceForgetDevice}</div>
       <div slot="body">
-        <div class="settings-box first">
+        <div class="first">
           $i18n{multideviceForgetDeviceDialogMessage}
         </div>
       </div>
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 98b783f..3b2676828 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1357,12 +1357,6 @@
         <structure name="IDR_SETTINGS_MULTIDEVICE_PAGE_JS"
                    file="multidevice_page/multidevice_page.js"
                    type="chrome_html" />
-        <structure name="IDR_SETTINGS_MULTIDEVICE_PAGE_CONTAINER_HTML"
-                   file="multidevice_page/multidevice_page_container.html"
-                   type="chrome_html" />
-        <structure name="IDR_SETTINGS_MULTIDEVICE_PAGE_CONTAINER_JS"
-                   file="multidevice_page/multidevice_page_container.js"
-                   type="chrome_html" />
         <structure name="IDR_SETTINGS_MULTIDEVICE_SUBPAGE_HTML"
                    file="multidevice_page/multidevice_subpage.html"
                    type="chrome_html" />
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
index 189078697..25d80fd 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
@@ -8,94 +8,11 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://welcome/email/nux_email_proxy.html">
+<link rel="import" href="chrome://welcome/shared/chooser_shared_css.html">
 
 <dom-module id="email-chooser">
   <template>
-    <style include="paper-button-style">
-      :host {
-        display: block;
-        white-space: nowrap;
-      }
-
-      .option {
-        align-items: center;
-        background: white;
-        border-radius: 8px;
-        border: 1px solid transparent;
-        box-sizing: border-box;
-        box-shadow: 0 1px 2px 0 rgba(0,36,100,0.30),
-            0 2px 6px 2px rgba(0,36,100,0.15);
-        display: inline-flex;
-        flex-direction: column;
-        height: 96px;
-        justify-content: center;
-        position: relative;
-        vertical-align: bottom;
-        -webkit-appearance: none;
-        width: 120px;
-        outline: 0;
-      }
-
-      .option:not(:first-of-type) {
-        margin-inline-start: 24px;
-      }
-
-      .option.keyboard-focused:focus {
-        outline: -webkit-focus-ring-color auto 5px;
-      }
-
-      .option .email-name {
-        flex-grow: 0;
-        font-size: 0.875rem;
-        line-height: 1.25rem;
-        text-align: center;
-        white-space: normal;
-      }
-
-      .option .email-icon {
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: contain;
-        height: 40px;
-        margin: 0;
-        margin-bottom: 4px;
-        width: 40px;
-      }
-
-      .option iron-icon {
-        --iron-icon-fill-color: white;
-        background: lightgrey;
-        border-radius: 50%;
-        display: none;
-        height: 12px;
-        margin: 0;
-        position: absolute;
-        right: 10px;
-        top: 10px;
-        width: 12px;
-      }
-
-      .option.keyboard-focused:focus iron-icon[icon="cr:check"],
-      .option:hover iron-icon[icon="cr:check"],
-      .option[active] iron-icon[icon="cr:check"] {
-        display: block;
-      }
-
-      .option[active] {
-        border: 1px solid var(--google-blue-600);
-        color: var(--google-blue-600);
-      }
-
-      .option[active] iron-icon[icon="cr:check"] {
-        background: var(--google-blue-600);
-      }
-
-      .button-bar {
-        display: flex;
-        margin-top: 64px;
-        justify-content: space-between;
-      }
-
+    <style include="chooser-shared-css paper-button-style">
       .gmail {
         background-image: -webkit-image-set(
             url(chrome://welcome/email/gmail_1x.png) 1x,
@@ -131,8 +48,8 @@
       <button active$="[[getSelected_(item, selectedEmailProvider_)]]"
           on-click="onEmailClick_" on-pointerdown="onEmailPointerDown_"
           on-keyup="onEmailKeyUp_" class="option">
-        <div class$="[[item.icon]] email-icon"></div>
-        <div class="email-name">[[item.name]]</div>
+        <div class$="[[item.icon]] option-icon"></div>
+        <div class="option-name">[[item.name]]</div>
         <iron-icon icon="cr:check"></iron-icon>
       </button>
     </template>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
index b5a96d44..f92ff424 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
@@ -4,33 +4,11 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://welcome/shared/chooser_shared_css.html">
 
 <dom-module id="apps-chooser">
   <template>
-    <style>
-      :host {
-        display: flex;
-        flex-direction: column;
-      }
-
-      .app-icon {
-        margin-inline-end: 16px;
-        margin-inline-start: 4px;
-      }
-
-      iron-icon {
-        margin-inline-end: 48px;
-      }
-
-      [active] iron-icon[icon="cr:check"] {
-        --iron-icon-fill-color: var(--google-blue-600);
-        opacity: unset;
-      }
-
-      iron-icon[icon="cr:check"] {
-        opacity: 0;
-      }
-
+    <style include="chooser-shared-css">
       .gmail {
         content: -webkit-image-set(
             url(chrome://welcome/apps/gmail_1x.png) 1x,
@@ -61,43 +39,22 @@
             url(chrome://welcome/apps/news_2x.png) 2x);
       }
 
-      .chrome_store {
+      .chrome-store {
         content: -webkit-image-set(
             url(chrome://welcome/apps/chrome_store_1x.png) 1x,
             url(chrome://welcome/apps/chrome_store_2x.png) 2x);
       }
-
-      paper-button:first-of-type {
-        border-top: unset;
-      }
-
-      paper-button {
-        border-radius: 0;
-        border-top: var(--cr-section_-_border-top);
-        display: flex;
-        margin: 0;
-        padding: 12px 0;
-        text-transform: unset;
-      }
-
-      paper-button:not([raised]).keyboard-focus {
-        outline-width: unset;
-        font-weight: unset;
-      }
-
-      .app-name {
-        flex: 1;
-      }
     </style>
 
     <template is="dom-repeat" items="[[appList]]">
-      <paper-button toggles noink
-          active$="[[item.selected]]" on-click="onAppClick_">
-        <div class$="[[item.icon]] app-icon"></div>
-        <div class="app-name">[[item.name]]</div>
-        <iron-icon icon="cr:check"></iron-icon>
-      </paper-button>
+      <button active$="[[item.selected]]"
+          on-click="onAppClick_" on-pointerdown="onAppPointerDown_"
+          on-keyup="onAppKeyUp_" class="option">
+        <div class$="[[item.icon]] option-icon"></div>
+          <div class="option-name">[[item.name]]</div>
+          <iron-icon icon="cr:check"></iron-icon>
+        </button>
+      </template>
     </template>
-  </template>
   <script src="apps_chooser.js"></script>
-</dom-module>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
index 5cec1de..4cdeb18 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
@@ -53,8 +53,8 @@
             selected: true,
           },
           {
-            name: 'Chrome Web Store',
-            icon: 'chrome_store',
+            name: 'Web Store',
+            icon: 'chrome-store',
             selected: true,
           },
         ];
@@ -87,6 +87,22 @@
   },
 
   /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppPointerDown_: function(e) {
+    e.currentTarget.classList.remove('keyboard-focused');
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppKeyUp_: function(e) {
+    e.currentTarget.classList.add('keyboard-focused');
+  },
+
+  /**
    * @return {boolean}
    * @private
    */
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index 56f427a..41b5c58 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -6,6 +6,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://welcome/apps/apps_chooser.html">
 <link rel="import" href="chrome://welcome/apps/nux_google_apps_proxy.html">
@@ -21,33 +22,33 @@
         margin-left: auto;
         margin-right: auto;
         margin-top: 116px;
-        max-width: 568px;
+        width: fit-content;
       }
 
       .chrome-logo {
         content: -webkit-image-set(
-            url('chrome://welcome/logo.png') 1x,
-            url('chrome://welcome/logo2x.png') 2x);
+            url(chrome://welcome/logo.png) 1x,
+            url(chrome://welcome/logo2x.png) 2x);
         height: 40px;
         margin-bottom: 36px;
         width: 40px;
       }
 
       h1 {
-        color: #202124;
+        color: var(--cr-primary-text-color);
         font-size: 28px;
-        opacity: .8;
         margin-bottom: 16px;
+        opacity: .8;
       }
 
       .description {
-        color: #5f6368;
+        color: var(--cr-secondary-text-color);
         font-size: 16px;
         margin-bottom: 24px;
       }
 
       apps-chooser {
-        color: #202124;
+        color: var(--cr-primary-text-color);
         font-size: 14px;
         margin-bottom: 48px;
       }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
new file mode 100644
index 0000000..6c64c2a8
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
@@ -0,0 +1,93 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+
+<dom-module id="chooser-shared-css">
+  <template>
+    <style>
+      :host {
+        display: block;
+        white-space: nowrap;
+      }
+
+      .option {
+        -webkit-appearance: none;
+        align-items: center;
+        background: white;
+        border: 1px solid transparent;
+        border-radius: 8px;
+        box-shadow: 0 1px 2px 0 rgba(0,36,100,0.30),
+                    0 2px 6px 2px rgba(0,36,100,0.15);
+        box-sizing: border-box;
+        display: inline-flex;
+        flex-direction: column;
+        height: 96px;
+        justify-content: center;
+        outline: 0;
+        position: relative;
+        vertical-align: bottom;
+        width: 120px;
+      }
+
+      .option:not(:first-of-type) {
+        margin-inline-start: 24px;
+      }
+
+      .option.keyboard-focused:focus {
+        outline: -webkit-focus-ring-color auto 5px;
+      }
+
+      .option .option-name {
+        flex-grow: 0;
+        font-size: 0.875rem;
+        line-height: 1.25rem;
+        text-align: center;
+        white-space: normal;
+      }
+
+      .option .option-icon {
+        background-position: center;
+        background-repeat: no-repeat;
+        background-size: contain;
+        height: 40px;
+        margin: 0;
+        margin-bottom: 4px;
+        width: 40px;
+      }
+
+      .option iron-icon {
+        --iron-icon-fill-color: white;
+        background: lightgrey;
+        border-radius: 50%;
+        display: none;
+        height: 12px;
+        margin: 0;
+        position: absolute;
+        right: 10px;
+        top: 10px;
+        width: 12px;
+      }
+
+      .option.keyboard-focused:focus iron-icon[icon='cr:check'],
+      .option:hover iron-icon[icon='cr:check'],
+      .option[active] iron-icon[icon='cr:check'] {
+        display: block;
+      }
+
+      .option[active] {
+        border: 1px solid var(--google-blue-600);
+        color: var(--google-blue-600);
+      }
+
+      .option[active] iron-icon[icon='cr:check'] {
+        background: var(--google-blue-600);
+      }
+
+      .button-bar {
+        display: flex;
+        justify-content: space-between;
+        margin-top: 64px;
+      }
+    </style>
+  </template>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 0cdcf11..84bdcf8 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -124,8 +124,9 @@
   return false;
 }
 
-bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open) {
-  return RestoreIfNecessary(urls_to_open, NULL);
+bool SessionService::RestoreIfNecessary(const base::CommandLine& command_line,
+                                        const std::vector<GURL>& urls_to_open) {
+  return RestoreIfNecessary(command_line, urls_to_open, NULL);
 }
 
 void SessionService::ResetFromCurrentBrowsers() {
@@ -230,7 +231,8 @@
   if (!ShouldTrackBrowser(browser))
     return;
 
-  RestoreIfNecessary(std::vector<GURL>(), browser);
+  RestoreIfNecessary(*base::CommandLine::ForCurrentProcess(),
+                     std::vector<GURL>(), browser);
   SetWindowType(browser->session_id(),
                 browser->type(),
                 browser->is_app() ? TYPE_APP : TYPE_NORMAL);
@@ -566,7 +568,8 @@
   }
 }
 
-bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
+bool SessionService::RestoreIfNecessary(const base::CommandLine& command_line,
+                                        const std::vector<GURL>& urls_to_open,
                                         Browser* browser) {
   if (ShouldNewWindowStartSession()) {
     // We're going from no tabbed browsers to a tabbed browser (and not in
@@ -576,8 +579,8 @@
       MoveCurrentSessionToLastSession();
       move_on_new_browser_ = false;
     }
-    SessionStartupPref pref = StartupBrowserCreator::GetSessionStartupPref(
-        *base::CommandLine::ForCurrentProcess(), profile());
+    SessionStartupPref pref =
+        StartupBrowserCreator::GetSessionStartupPref(command_line, profile());
     sessions::TabRestoreService* tab_restore_service =
         TabRestoreServiceFactory::GetForProfileIfExisting(profile());
     if (pref.type == SessionStartupPref::LAST &&
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index 3c42ba4e..02a8676 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -28,6 +28,10 @@
 
 class Profile;
 
+namespace base {
+class CommandLine;
+}  // namespace base
+
 namespace content {
 class WebContents;
 }  // namespace content
@@ -89,8 +93,11 @@
   // during startup and window creation this is invoked to see if a session
   // needs to be restored. If a session needs to be restored it is done so
   // asynchronously and true is returned. If false is returned the session was
-  // not restored and the caller needs to create a new window.
-  bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open);
+  // not restored and the caller needs to create a new window. The command_line
+  // should be the one of the original process that lead to opening the window
+  // and deciding whether the session needs to be restored.
+  bool RestoreIfNecessary(const base::CommandLine& command_line,
+                          const std::vector<GURL>& urls_to_open);
 
   // Resets the contents of the file from the current state of all open
   // browsers whose profile matches our profile.
@@ -247,7 +254,8 @@
 
   // Implementation of RestoreIfNecessary. If |browser| is non-null and we need
   // to restore, the tabs are added to it, otherwise a new browser is created.
-  bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
+  bool RestoreIfNecessary(const base::CommandLine& command_line,
+                          const std::vector<GURL>& urls_to_open,
                           Browser* browser);
 
   // BrowserListObserver
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
index d5f4bb0..5d25f8e0 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
@@ -88,10 +88,8 @@
   items_.push_back(Item(NULL, Item::TYPE_SEPARATOR));
   items_.push_back(Item(NULL, Item::TYPE_CHOOSE_ANOTHER_FOLDER));
 
-  std::vector<Item>::iterator it = std::find(items_.begin(),
-                                             items_.end(),
-                                             Item(node->parent(),
-                                                  Item::TYPE_NODE));
+  auto it = std::find(items_.begin(), items_.end(),
+                      Item(node->parent(), Item::TYPE_NODE));
   node_parent_index_ = static_cast<int>(it - items_.begin());
 }
 
@@ -166,8 +164,7 @@
   // Changing is rare enough that we don't attempt to readjust the contents.
   // Update |items_| so we aren't left pointing to a deleted node.
   bool changed = false;
-  for (std::vector<Item>::iterator i = items_.begin();
-       i != items_.end();) {
+  for (auto i = items_.begin(); i != items_.end();) {
     if (i->type == Item::TYPE_NODE && i->node->HasAncestor(node)) {
       i = items_.erase(i);
       changed = true;
@@ -209,8 +206,7 @@
   // Changing is rare enough that we don't attempt to readjust the contents.
   // Update |items_| so we aren't left pointing to a deleted node.
   bool changed = false;
-  for (std::vector<Item>::iterator i = items_.begin();
-       i != items_.end();) {
+  for (auto i = items_.begin(); i != items_.end();) {
     if (i->type == Item::TYPE_NODE &&
         !bookmark_model_->is_permanent_node(i->node)) {
       i = items_.erase(i);
@@ -245,9 +241,8 @@
 }
 
 void RecentlyUsedFoldersComboModel::RemoveNode(const BookmarkNode* node) {
-  std::vector<Item>::iterator it = std::find(items_.begin(),
-                                             items_.end(),
-                                             Item(node, Item::TYPE_NODE));
+  auto it =
+      std::find(items_.begin(), items_.end(), Item(node, Item::TYPE_NODE));
   if (it != items_.end())
     items_.erase(it);
 }
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 15a93429..0879266 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -2392,7 +2392,7 @@
   if (!contents)
     return;
 
-  UpdateMap::iterator i = scheduled_updates_.find(contents);
+  auto i = scheduled_updates_.find(contents);
   if (i != scheduled_updates_.end())
     scheduled_updates_.erase(i);
 }
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 1b80948..2b03134 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -403,7 +403,8 @@
         SessionServiceFactory::GetForProfileForSessionRestore(
             profile->GetOriginalProfile());
     if (!session_service ||
-        !session_service->RestoreIfNecessary(std::vector<GURL>())) {
+        !session_service->RestoreIfNecessary(
+            *base::CommandLine::ForCurrentProcess(), std::vector<GURL>())) {
       OpenEmptyWindow(profile->GetOriginalProfile());
     }
   }
diff --git a/chrome/browser/ui/browser_finder.cc b/chrome/browser/ui/browser_finder.cc
index 5853b30..d11d741 100644
--- a/chrome/browser/ui/browser_finder.cc
+++ b/chrome/browser/ui/browser_finder.cc
@@ -152,8 +152,8 @@
   BrowserList* browser_list_impl = BrowserList::GetInstance();
   size_t count = 0;
   if (browser_list_impl) {
-    for (BrowserList::const_iterator i = browser_list_impl->begin();
-         i != browser_list_impl->end(); ++i) {
+    for (auto i = browser_list_impl->begin(); i != browser_list_impl->end();
+         ++i) {
       if (BrowserMatches(*i, profile, Browser::FEATURE_NONE, match_types,
                          display_id))
         count++;
diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc
index 244c5c21..e00c780 100644
--- a/chrome/browser/ui/browser_list.cc
+++ b/chrome/browser/ui/browser_list.cc
@@ -180,8 +180,8 @@
                                         const CloseCallback& on_close_aborted,
                                         const base::FilePath& profile_path,
                                         const bool skip_beforeunload) {
-  for (BrowserVector::const_iterator it = browsers_to_close.begin();
-       it != browsers_to_close.end(); ++it) {
+  for (auto it = browsers_to_close.begin(); it != browsers_to_close.end();
+       ++it) {
     if ((*it)->TryToCloseWindow(
             skip_beforeunload,
             base::Bind(&BrowserList::PostTryToCloseBrowserWindow,
@@ -220,8 +220,8 @@
                           profile_path, skip_beforeunload);
   } else if (!resetting_handlers) {
     base::AutoReset<bool> resetting_handlers_scoper(&resetting_handlers, true);
-    for (BrowserVector::const_iterator it = browsers_to_close.begin();
-         it != browsers_to_close.end(); ++it) {
+    for (auto it = browsers_to_close.begin(); it != browsers_to_close.end();
+         ++it) {
       (*it)->ResetTryToCloseWindow();
     }
     if (on_close_aborted)
@@ -333,7 +333,7 @@
 // static
 void BrowserList::RemoveBrowserFrom(Browser* browser,
                                     BrowserVector* browser_list) {
-  BrowserVector::iterator remove_browser =
+  auto remove_browser =
       std::find(browser_list->begin(), browser_list->end(), browser);
   if (remove_browser != browser_list->end())
     browser_list->erase(remove_browser);
diff --git a/chrome/browser/ui/fast_unload_controller.cc b/chrome/browser/ui/fast_unload_controller.cc
index 7fa178a..7c259975 100644
--- a/chrome/browser/ui/fast_unload_controller.cc
+++ b/chrome/browser/ui/fast_unload_controller.cc
@@ -243,8 +243,8 @@
   DCHECK(is_attempting_to_close_browser_);
   tabs_needing_before_unload_.clear();
   CancelTabNeedingBeforeUnloadAck();
-  for (WebContentsSet::iterator it = tabs_needing_unload_.begin();
-       it != tabs_needing_unload_.end(); it++) {
+  for (auto it = tabs_needing_unload_.begin(); it != tabs_needing_unload_.end();
+       it++) {
     content::WebContents* contents = *it;
 
     CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
@@ -403,7 +403,7 @@
                                   tabs_needing_before_unload_.end());
       tabs_needing_before_unload_.clear();
     } else {
-      WebContentsSet::iterator it = tabs_needing_before_unload_.begin();
+      auto it = tabs_needing_before_unload_.begin();
       content::WebContents* contents = *it;
       tabs_needing_before_unload_.erase(it);
       // Null check render_view_host here as this gets called on a PostTask and
@@ -445,9 +445,9 @@
     browser_->OnWindowClosing();
 
     // Run unload handlers detached since no more interaction is possible.
-    WebContentsSet::iterator it = tabs_needing_unload_.begin();
+    auto it = tabs_needing_unload_.begin();
     while (it != tabs_needing_unload_.end()) {
-      WebContentsSet::iterator current = it++;
+      auto current = it++;
       content::WebContents* contents = *current;
       tabs_needing_unload_.erase(current);
       // Null check render_view_host here as this gets called on a PostTask
diff --git a/chrome/browser/ui/hung_plugin_tab_helper.cc b/chrome/browser/ui/hung_plugin_tab_helper.cc
index 47c5827..cb4ec669 100644
--- a/chrome/browser/ui/hung_plugin_tab_helper.cc
+++ b/chrome/browser/ui/hung_plugin_tab_helper.cc
@@ -122,8 +122,7 @@
 
   // For now, just do a brute-force search to see if we have this plugin. Since
   // we'll normally have 0 or 1, this is fast.
-  for (PluginStateMap::iterator i = hung_plugins_.begin();
-       i != hung_plugins_.end(); ++i) {
+  for (auto i = hung_plugins_.begin(); i != hung_plugins_.end(); ++i) {
     if (i->second->path == plugin_path) {
       if (i->second->infobar)
         infobar_service->RemoveInfoBar(i->second->infobar);
@@ -144,7 +143,7 @@
   if (!infobar_observer_.IsObserving(infobar_service))
     infobar_observer_.Add(infobar_service);
 
-  PluginStateMap::iterator found = hung_plugins_.find(plugin_child_id);
+  auto found = hung_plugins_.find(plugin_child_id);
   if (found != hung_plugins_.end()) {
     if (!is_hung) {
       // Hung plugin became un-hung, close the infobar and delete our info.
@@ -191,7 +190,7 @@
 }
 
 void HungPluginTabHelper::KillPlugin(int child_id) {
-  PluginStateMap::iterator found = hung_plugins_.find(child_id);
+  auto found = hung_plugins_.find(child_id);
   DCHECK(found != hung_plugins_.end());
 
   base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
@@ -202,7 +201,7 @@
 void HungPluginTabHelper::OnReshowTimer(int child_id) {
   // The timer should have been cancelled if the record isn't in our map
   // anymore.
-  PluginStateMap::iterator found = hung_plugins_.find(child_id);
+  auto found = hung_plugins_.find(child_id);
   DCHECK(found != hung_plugins_.end());
   DCHECK(!found->second->infobar);
   ShowBar(child_id, found->second.get());
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.cc b/chrome/browser/ui/input_method/input_method_engine_base.cc
index 91de5a4..1265386 100644
--- a/chrome/browser/ui/input_method/input_method_engine_base.cc
+++ b/chrome/browser/ui/input_method/input_method_engine_base.cc
@@ -204,8 +204,7 @@
   composition_text_->selection.set_end(selection_end);
 
   // TODO: Add support for displaying selected text in the composition string.
-  for (std::vector<SegmentInfo>::const_iterator segment = segments.begin();
-       segment != segments.end(); ++segment) {
+  for (auto segment = segments.begin(); segment != segments.end(); ++segment) {
     ui::ImeTextSpan ime_text_span;
 
     ime_text_span.underline_color = SK_ColorTRANSPARENT;
@@ -410,7 +409,7 @@
     composition_changed_ = false;
   }
 
-  RequestMap::iterator request = request_map_.find(request_id);
+  auto request = request_map_.find(request_id);
   if (request == request_map_.end()) {
     LOG(ERROR) << "Request ID not found: " << request_id;
     return;
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index e1dffee8..1fa492ec 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -476,7 +476,7 @@
        {colors_, pref_service->GetBoolean(prefs::kUseCustomChromeFrame)
                      ? custom_frame_colors_
                      : native_frame_colors_}) {
-    ColorMap::const_iterator it = color_map.find(id);
+    auto it = color_map.find(id);
     if (it != color_map.end()) {
       *color = it->second;
       return true;
diff --git a/chrome/browser/ui/libgtkui/menu_util.cc b/chrome/browser/ui/libgtkui/menu_util.cc
index f61b49ce..1c11cb6 100644
--- a/chrome/browser/ui/libgtkui/menu_util.cc
+++ b/chrome/browser/ui/libgtkui/menu_util.cc
@@ -125,8 +125,7 @@
         break;
 
       case ui::MenuModel::TYPE_RADIO: {
-        std::map<int, GtkWidget*>::iterator iter =
-            radio_groups.find(model->GetGroupIdAt(i));
+        auto iter = radio_groups.find(model->GetGroupIdAt(i));
 
         if (iter == radio_groups.end()) {
           menu_item =
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc
index db69dfd..51be03f 100644
--- a/chrome/browser/ui/login/login_handler_browsertest.cc
+++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -97,7 +97,7 @@
   const net::AuthChallengeInfo* challenge = handler->auth_info();
 
   ASSERT_TRUE(challenge);
-  AuthMap::iterator i = auth_map_.find(challenge->realm);
+  auto i = auth_map_.find(challenge->realm);
   EXPECT_TRUE(auth_map_.end() != i);
   if (i != auth_map_.end()) {
     const AuthInfo& info = i->second;
diff --git a/chrome/browser/ui/login/login_handler_test_utils.cc b/chrome/browser/ui/login/login_handler_test_utils.cc
index 0191348..b7b6bd01 100644
--- a/chrome/browser/ui/login/login_handler_test_utils.cc
+++ b/chrome/browser/ui/login/login_handler_test_utils.cc
@@ -38,8 +38,7 @@
 }
 
 void LoginPromptBrowserTestObserver::RemoveHandler(LoginHandler* handler) {
-  std::list<LoginHandler*>::iterator i =
-      std::find(handlers_.begin(), handlers_.end(), handler);
+  auto i = std::find(handlers_.begin(), handlers_.end(), handler);
   // Cannot use ASSERT_NE, because gTest on Android confuses iterators with
   // containers.
   ASSERT_TRUE(i != handlers_.end());
diff --git a/chrome/browser/ui/passwords/password_manager_presenter.cc b/chrome/browser/ui/passwords/password_manager_presenter.cc
index cbd01dda..5b65052 100644
--- a/chrome/browser/ui/passwords/password_manager_presenter.cc
+++ b/chrome/browser/ui/passwords/password_manager_presenter.cc
@@ -55,8 +55,7 @@
   std::pair<password_manager::DuplicatesMap::iterator,
             password_manager::DuplicatesMap::iterator>
       dups = duplicates->equal_range(key);
-  for (password_manager::DuplicatesMap::iterator it = dups.first;
-       it != dups.second; ++it)
+  for (auto it = dups.first; it != dups.second; ++it)
     store->RemoveLogin(*it->second);
   duplicates->erase(key);
 }
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 7ace08e..fd89147b9 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -176,7 +176,7 @@
     // Check that browsers have been opened for all the launched profiles.
     // Note that browsers opened for profiles that were not added as launched
     // profiles are simply ignored.
-    std::set<const Profile*>::const_iterator i = launched_profiles_.begin();
+    auto i = launched_profiles_.begin();
     for (; i != launched_profiles_.end(); ++i) {
       if (opened_profiles_.find(*i) == opened_profiles_.end())
         return;
@@ -807,14 +807,14 @@
   size_t DEBUG_num_profiles_at_loop_start = std::numeric_limits<size_t>::max();
   base::debug::Alias(&DEBUG_num_profiles_at_loop_start);
 
-  Profiles::const_iterator DEBUG_it_begin = last_opened_profiles.begin();
+  auto DEBUG_it_begin = last_opened_profiles.begin();
   base::debug::Alias(&DEBUG_it_begin);
-  Profiles::const_iterator DEBUG_it_end = last_opened_profiles.end();
+  auto DEBUG_it_end = last_opened_profiles.end();
   base::debug::Alias(&DEBUG_it_end);
 
   // Launch the profiles in the order they became active.
-  for (Profiles::const_iterator it = last_opened_profiles.begin();
-       it != last_opened_profiles.end(); ++it, ++DEBUG_loop_counter) {
+  for (auto it = last_opened_profiles.begin(); it != last_opened_profiles.end();
+       ++it, ++DEBUG_loop_counter) {
     DEBUG_num_profiles_at_loop_start = last_opened_profiles.size();
     DCHECK(!(*it)->IsGuestSession());
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 6a3cf24..1f39df30 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1361,6 +1361,45 @@
             tab_strip->GetWebContentsAt(0)->GetURL().ExtractFileName());
 }
 
+class StartupIncognitoBrowserCreatorTest : public InProcessBrowserTest {
+ protected:
+  StartupIncognitoBrowserCreatorTest() = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kIncognito);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StartupIncognitoBrowserCreatorTest);
+};
+
+IN_PROC_BROWSER_TEST_F(StartupIncognitoBrowserCreatorTest,
+                       IncognitoStartupThenNormalStartup) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath dest_path = profile_manager->user_data_dir();
+  Profile* profile = nullptr;
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    profile = Profile::CreateProfile(
+        dest_path.Append(FILE_PATH_LITERAL("New Profile 1")), nullptr,
+        Profile::CreateMode::CREATE_MODE_SYNCHRONOUS);
+  }
+  ASSERT_TRUE(profile);
+  profile_manager->RegisterTestingProfile(profile, true, false);
+  SessionStartupPref::SetStartupPref(
+      profile, SessionStartupPref(SessionStartupPref::LAST));
+
+  EXPECT_FALSE(profile->restored_last_session());
+
+  base::CommandLine dummy(base::CommandLine::NO_PROGRAM);
+  StartupBrowserCreatorImpl creator(base::FilePath(), dummy,
+                                    chrome::startup::IS_NOT_FIRST_RUN);
+
+  EXPECT_TRUE(creator.Launch(profile, {}, false));
+
+  EXPECT_TRUE(profile->restored_last_session());
+}
+
 #endif  // !defined(OS_CHROMEOS)
 
 class StartupBrowserCreatorWelcomeBackTest : public InProcessBrowserTest {
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index 5da96407..3202d12 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -719,7 +719,8 @@
   SessionService* service =
       SessionServiceFactory::GetForProfileForSessionRestore(profile_);
 
-  return service && service->RestoreIfNecessary(TabsToUrls(tabs));
+  return service &&
+         service->RestoreIfNecessary(command_line_, TabsToUrls(tabs));
 }
 
 Browser* StartupBrowserCreatorImpl::RestoreOrCreateBrowser(
diff --git a/chrome/browser/ui/tabs/pinned_tab_codec.cc b/chrome/browser/ui/tabs/pinned_tab_codec.cc
index d801d94..a012aedd 100644
--- a/chrome/browser/ui/tabs/pinned_tab_codec.cc
+++ b/chrome/browser/ui/tabs/pinned_tab_codec.cc
@@ -111,7 +111,7 @@
   ListPrefUpdate update(prefs, prefs::kPinnedTabs);
   base::ListValue* values = update.Get();
   values->Clear();
-  for (StartupTabs::const_iterator i = tabs.begin(); i != tabs.end(); ++i)
+  for (auto i = tabs.begin(); i != tabs.end(); ++i)
     EncodeTab(*i, values);
 }
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 4f73de0..bd6d83a6 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -1106,8 +1106,7 @@
         base::RecordAction(UserMetricsAction("TabContextMenu_MuteTabs"));
       else
         base::RecordAction(UserMetricsAction("TabContextMenu_UnmuteTabs"));
-      for (std::vector<int>::const_iterator i = indices.begin();
-           i != indices.end(); ++i) {
+      for (auto i = indices.begin(); i != indices.end(); ++i) {
         chrome::SetTabAudioMuted(GetWebContentsAt(*i), mute,
                                  TabMutedReason::CONTEXT_MENU, std::string());
       }
diff --git a/chrome/browser/ui/tabs/tab_utils.cc b/chrome/browser/ui/tabs/tab_utils.cc
index 1111dc6..fa5e2ba 100644
--- a/chrome/browser/ui/tabs/tab_utils.cc
+++ b/chrome/browser/ui/tabs/tab_utils.cc
@@ -135,8 +135,7 @@
 
 bool AreAllTabsMuted(const TabStripModel& tab_strip,
                      const std::vector<int>& indices) {
-  for (std::vector<int>::const_iterator i = indices.begin(); i != indices.end();
-       ++i) {
+  for (auto i = indices.begin(); i != indices.end(); ++i) {
     if (!tab_strip.GetWebContentsAt(*i)->IsAudioMuted())
       return false;
   }
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 5c2342b5..5db023f3 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -877,8 +877,7 @@
   const GlobalErrorService::GlobalErrorList& errors =
       GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors();
   bool menu_items_added = false;
-  for (GlobalErrorService::GlobalErrorList::const_iterator it = errors.begin();
-       it != errors.end(); ++it) {
+  for (auto it = errors.begin(); it != errors.end(); ++it) {
     GlobalError* error = *it;
     DCHECK(error);
     if (error->HasMenuItem()) {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index d65c2ba..23ab254 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -636,7 +636,7 @@
 }
 
 void ToolbarActionsBar::OnToolbarActionRemoved(const std::string& action_id) {
-  ToolbarActions::iterator iter = toolbar_actions_.begin();
+  auto iter = toolbar_actions_.begin();
   while (iter != toolbar_actions_.end() && (*iter)->GetId() != action_id)
     ++iter;
 
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model.cc b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
index 11f6d77..92f29161 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
@@ -92,7 +92,7 @@
 }
 
 void ToolbarActionsModel::MoveActionIcon(const std::string& id, size_t index) {
-  std::vector<ToolbarItem>::iterator pos = toolbar_items_.begin();
+  auto pos = toolbar_items_.begin();
   while (pos != toolbar_items_.end() && (*pos).id != id)
     ++pos;
   if (pos == toolbar_items_.end()) {
@@ -103,7 +103,7 @@
   ToolbarItem action = *pos;
   toolbar_items_.erase(pos);
 
-  std::vector<std::string>::iterator pos_id =
+  auto pos_id =
       std::find(last_known_positions_.begin(), last_known_positions_.end(), id);
   if (pos_id != last_known_positions_.end())
     last_known_positions_.erase(pos_id);
@@ -112,7 +112,7 @@
     // If the index is not at the end, find the item currently at |index|, and
     // insert |action| before it in |toolbar_items_| and |action|'s id in
     // |last_known_positions_|.
-    std::vector<ToolbarItem>::iterator iter = toolbar_items_.begin() + index;
+    auto iter = toolbar_items_.begin() + index;
     last_known_positions_.insert(
         std::find(last_known_positions_.begin(), last_known_positions_.end(),
                   iter->id),
@@ -245,8 +245,8 @@
 }
 
 void ToolbarActionsModel::RemovePref(const ToolbarItem& item) {
-  std::vector<std::string>::iterator pos = std::find(
-      last_known_positions_.begin(), last_known_positions_.end(), item.id);
+  auto pos = std::find(last_known_positions_.begin(),
+                       last_known_positions_.end(), item.id);
 
   if (pos != last_known_positions_.end()) {
     last_known_positions_.erase(pos);
@@ -387,8 +387,7 @@
 }
 
 void ToolbarActionsModel::RemoveItem(const ToolbarItem& item) {
-  std::vector<ToolbarItem>::iterator pos =
-      std::find(toolbar_items_.begin(), toolbar_items_.end(), item);
+  auto pos = std::find(toolbar_items_.begin(), toolbar_items_.end(), item);
 
   if (pos == toolbar_items_.end())
     return;
@@ -654,8 +653,7 @@
 
   std::set<std::string> component_ids =
       component_actions_factory_->GetInitialComponentIds();
-  for (std::vector<ToolbarItem>::const_iterator iter =
-           original_model->toolbar_items_.begin();
+  for (auto iter = original_model->toolbar_items_.begin();
        iter != original_model->toolbar_items_.end(); ++iter) {
     // The extension might not be shown in incognito mode.
     // We may also disable certain component actions in incognito mode.
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
index a086bce..8060033 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
@@ -348,8 +348,7 @@
 
 testing::AssertionResult ToolbarActionsModelUnitTest::AddAndVerifyExtensions(
     const extensions::ExtensionList& extensions) {
-  for (extensions::ExtensionList::const_iterator iter = extensions.begin();
-       iter != extensions.end(); ++iter) {
+  for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) {
     if (!AddExtension(*iter)) {
       return testing::AssertionFailure() << "Failed to install extension: "
                                          << (*iter)->name();
diff --git a/chrome/browser/ui/unload_controller.cc b/chrome/browser/ui/unload_controller.cc
index c963f06a..07fab9b 100644
--- a/chrome/browser/ui/unload_controller.cc
+++ b/chrome/browser/ui/unload_controller.cc
@@ -208,8 +208,8 @@
   // Closing of window can be canceled from a beforeunload handler.
   DCHECK(is_attempting_to_close_browser_);
   tabs_needing_before_unload_fired_.clear();
-  for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin();
-      it != tabs_needing_unload_fired_.end(); ++it) {
+  for (auto it = tabs_needing_unload_fired_.begin();
+       it != tabs_needing_unload_fired_.end(); ++it) {
     DevToolsWindow::OnPageCloseCanceled(*it);
   }
   tabs_needing_unload_fired_.clear();
@@ -372,8 +372,7 @@
                                      content::WebContents* web_contents) {
   DCHECK(is_attempting_to_close_browser_);
 
-  UnloadListenerSet::iterator iter =
-      std::find(set->begin(), set->end(), web_contents);
+  auto iter = std::find(set->begin(), set->end(), web_contents);
   if (iter != set->end()) {
     set->erase(iter);
     return true;
diff --git a/chrome/browser/ui/views/accelerator_table_unittest.cc b/chrome/browser/ui/views/accelerator_table_unittest.cc
index 167d72a6d..7862e6d 100644
--- a/chrome/browser/ui/views/accelerator_table_unittest.cc
+++ b/chrome/browser/ui/views/accelerator_table_unittest.cc
@@ -34,8 +34,7 @@
 TEST(AcceleratorTableTest, CheckDuplicatedAccelerators) {
   std::set<AcceleratorMapping, Cmp> accelerators;
   const std::vector<AcceleratorMapping> accelerator_list(GetAcceleratorList());
-  for (std::vector<AcceleratorMapping>::const_iterator it =
-           accelerator_list.begin(); it != accelerator_list.end(); ++it) {
+  for (auto it = accelerator_list.begin(); it != accelerator_list.end(); ++it) {
     const AcceleratorMapping& entry = *it;
     EXPECT_TRUE(accelerators.insert(entry).second)
         << "Duplicated accelerator: " << entry.keycode << ", "
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index aeb2507..2872ac7 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -172,9 +172,8 @@
   CHECK(!is_kiosk_app_mode ||
         zoom::ZoomController::FromWebContents(web_view()->GetWebContents()));
 
-  for (std::map<ui::Accelerator, int>::const_iterator iter =
-           accelerator_table.begin();
-       iter != accelerator_table.end(); ++iter) {
+  for (auto iter = accelerator_table.begin(); iter != accelerator_table.end();
+       ++iter) {
     if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second))
       continue;
 
@@ -277,8 +276,7 @@
     const ui::Accelerator& accelerator) {
   const std::map<ui::Accelerator, int>& accelerator_table =
       GetAcceleratorTable();
-  std::map<ui::Accelerator, int>::const_iterator iter =
-      accelerator_table.find(accelerator);
+  auto iter = accelerator_table.find(accelerator);
   DCHECK(iter != accelerator_table.end());
   int command_id = iter->second;
   switch (command_id) {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index ddaf3da..972e9edb 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -475,8 +475,7 @@
 void BookmarkEditorView::ExpandAndSelect() {
   BookmarkExpandedStateTracker::Nodes expanded_nodes =
       bb_model_->expanded_state_tracker()->GetExpandedNodes();
-  for (BookmarkExpandedStateTracker::Nodes::const_iterator i(
-       expanded_nodes.begin()); i != expanded_nodes.end(); ++i) {
+  for (auto i(expanded_nodes.begin()); i != expanded_nodes.end(); ++i) {
     EditorNode* editor_node =
         FindNodeWithID(tree_model_->GetRoot(), (*i)->id());
     if (editor_node)
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
index 558107a6..865946e8 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
@@ -145,7 +145,7 @@
 base::string16 BookmarkMenuDelegate::GetTooltipText(
     int id,
     const gfx::Point& screen_loc) const {
-  MenuIDToNodeMap::const_iterator i = menu_id_to_node_map_.find(id);
+  auto i = menu_id_to_node_map_.find(id);
   // When removing bookmarks it may be possible to end up here without a node.
   if (i == menu_id_to_node_map_.end()) {
     DCHECK(is_mutating_model_);
@@ -372,7 +372,7 @@
 void BookmarkMenuDelegate::BookmarkNodeFaviconChanged(
     BookmarkModel* model,
     const BookmarkNode* node) {
-  NodeToMenuMap::iterator menu_pair = node_to_menu_map_.find(node);
+  auto menu_pair = node_to_menu_map_.find(node);
   if (menu_pair == node_to_menu_map_.end())
     return;  // We're not showing a menu item for the node.
 
@@ -396,9 +396,8 @@
 
   // Remove the menu items.
   std::set<MenuItemView*> changed_parent_menus;
-  for (std::vector<const BookmarkNode*>::const_iterator i(bookmarks.begin());
-       i != bookmarks.end(); ++i) {
-    NodeToMenuMap::iterator node_to_menu = node_to_menu_map_.find(*i);
+  for (auto i(bookmarks.begin()); i != bookmarks.end(); ++i) {
+    auto node_to_menu = node_to_menu_map_.find(*i);
     if (node_to_menu != node_to_menu_map_.end()) {
       MenuItemView* menu = node_to_menu->second;
       MenuItemView* parent = menu->GetParentMenuItem();
@@ -420,11 +419,9 @@
   DCHECK_LE(changed_parent_menus.size(), 1U);
 
   // Remove any descendants of the removed nodes in |node_to_menu_map_|.
-  for (NodeToMenuMap::iterator i(node_to_menu_map_.begin());
-       i != node_to_menu_map_.end(); ) {
+  for (auto i(node_to_menu_map_.begin()); i != node_to_menu_map_.end();) {
     bool ancestor_removed = false;
-    for (std::vector<const BookmarkNode*>::const_iterator j(bookmarks.begin());
-         j != bookmarks.end(); ++j) {
+    for (auto j(bookmarks.begin()); j != bookmarks.end(); ++j) {
       if (i->first->HasAncestor(*j)) {
         ancestor_removed = true;
         break;
@@ -438,8 +435,8 @@
     }
   }
 
-  for (std::set<MenuItemView*>::const_iterator i(changed_parent_menus.begin());
-       i != changed_parent_menus.end(); ++i)
+  for (auto i(changed_parent_menus.begin()); i != changed_parent_menus.end();
+       ++i)
     (*i)->ChildrenChanged();
 }
 
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 25e4fc4..064bb43 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -466,8 +466,7 @@
   const ContentSettingBubbleModel::RadioGroup& radio_group =
       bubble_content.radio_group;
   if (!radio_group.radio_items.empty()) {
-    for (ContentSettingBubbleModel::RadioItems::const_iterator i(
-         radio_group.radio_items.begin());
+    for (auto i(radio_group.radio_items.begin());
          i != radio_group.radio_items.end(); ++i) {
       auto radio = std::make_unique<views::RadioButton>(*i, 0);
       radio->SetEnabled(bubble_content.radio_group_enabled);
@@ -488,8 +487,7 @@
          LayoutRowType::INDENTED});
   }
 
-  for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i(
-           bubble_content.domain_lists.begin());
+  for (auto i(bubble_content.domain_lists.begin());
        i != bubble_content.domain_lists.end(); ++i) {
     auto list_view =
         std::make_unique<ContentSettingDomainListView>(i->title, i->hosts);
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_source_view.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_source_view.cc
index 705715f3..7af8468c 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_source_view.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_source_view.cc
@@ -83,7 +83,7 @@
     // Unselect all other sources.
     Views neighbours;
     parent()->GetViewsInGroup(GetGroup(), &neighbours);
-    for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
+    for (auto i(neighbours.begin()); i != neighbours.end(); ++i) {
       if (*i != this) {
         DCHECK_EQ((*i)->GetClassName(),
                   DesktopMediaSourceView::kDesktopMediaSourceViewClassName);
@@ -134,7 +134,7 @@
   if (neighbours.empty())
     return nullptr;
 
-  for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
+  for (auto i(neighbours.begin()); i != neighbours.end(); ++i) {
     DCHECK_EQ((*i)->GetClassName(),
               DesktopMediaSourceView::kDesktopMediaSourceViewClassName);
     DesktopMediaSourceView* source_view =
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
index fad9f3d..251aec72 100644
--- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
+++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -189,7 +189,7 @@
     const MediaGalleriesDialogController::Entry& gallery,
     views::View* container,
     int trailing_vertical_space) {
-  CheckboxMap::iterator iter = checkbox_map_.find(gallery.pref_info.pref_id);
+  auto iter = checkbox_map_.find(gallery.pref_info.pref_id);
   if (iter != checkbox_map_.end()) {
     views::Checkbox* checkbox = iter->second->checkbox();
     checkbox->SetChecked(gallery.selected);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index fa5eb90..8f629a37 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -621,8 +621,8 @@
     return true;
 #endif
   // Else, we retrieve the accelerator information from the accelerator table.
-  for (std::map<ui::Accelerator, int>::const_iterator it =
-           accelerator_table_.begin(); it != accelerator_table_.end(); ++it) {
+  for (auto it = accelerator_table_.begin(); it != accelerator_table_.end();
+       ++it) {
     if (it->second == cmd_id) {
       *accelerator = it->first;
       return true;
@@ -2984,8 +2984,7 @@
 bool BrowserView::FindCommandIdForAccelerator(
     const ui::Accelerator& accelerator,
     int* command_id) const {
-  std::map<ui::Accelerator, int>::const_iterator iter =
-      accelerator_table_.find(accelerator);
+  auto iter = accelerator_table_.find(accelerator);
   if (iter == accelerator_table_.end())
     return false;
 
diff --git a/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc
index b112b0f..f6254b5 100644
--- a/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc
+++ b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc
@@ -135,8 +135,7 @@
                                                    GParamSpec* /* ignored */) {
   // If the name owner changed, we need to reregister all the live xids with
   // the system.
-  for (std::set<unsigned long>::const_iterator it = live_xids_.begin();
-       it != live_xids_.end(); ++it) {
+  for (auto it = live_xids_.begin(); it != live_xids_.end(); ++it) {
     RegisterXID(*it);
   }
 }
diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
index 0cddbcb..0f3e3885 100644
--- a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
+++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
@@ -597,8 +597,7 @@
 }
 
 void GlobalMenuBarX11::OnBookmarkBarVisibilityChanged() {
-  CommandIDMenuItemMap::iterator it =
-      id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR);
+  auto it = id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR);
   if (it != id_to_menu_item_.end()) {
     PrefService* prefs = browser_->profile()->GetPrefs();
     // Note: Unlike the GTK version, we don't appear to need to do tricks where
@@ -723,7 +722,7 @@
 }
 
 void GlobalMenuBarX11::EnabledStateChangedForCommand(int id, bool enabled) {
-  CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id);
+  auto it = id_to_menu_item_.find(id);
   if (it != id_to_menu_item_.end())
     menuitem_property_set_bool(it->second, kPropertyEnabled, enabled);
 }
@@ -748,8 +747,7 @@
                                         TAG_RECENTLY_CLOSED_HEADER) + 1;
 
   unsigned int added_count = 0;
-  for (sessions::TabRestoreService::Entries::const_iterator it =
-           entries.begin();
+  for (auto it = entries.begin();
        it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
     sessions::TabRestoreService::Entry* entry = it->get();
 
diff --git a/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc b/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
index 347ef22..883a89c3 100644
--- a/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/alternate_nav_infobar_view.cc
@@ -57,7 +57,7 @@
   views::Label* last_label = labels->back();
   labels->pop_back();
   int used_width = 0;
-  for (Labels::iterator i(labels->begin()); i != labels->end(); ++i)
+  for (auto i(labels->begin()); i != labels->end(); ++i)
     used_width += (*i)->GetPreferredSize().width();
   int last_label_width = std::min(last_label->GetPreferredSize().width(),
                                   available_width - used_width);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 2610875..c083a87a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1917,7 +1917,7 @@
 TabStrip::FindClosingTabResult TabStrip::FindClosingTab(const Tab* tab) {
   DCHECK(tab->closing());
   for (auto i = tabs_closing_map_.begin(); i != tabs_closing_map_.end(); ++i) {
-    Tabs::iterator j = std::find(i->second.begin(), i->second.end(), tab);
+    auto j = std::find(i->second.begin(), i->second.end(), tab);
     if (j != i->second.end())
       return FindClosingTabResult(i, j);
   }
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index 3ede94a..ef4d8ea 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -762,8 +762,7 @@
 
       // Remove all elements in |AppMenu::command_id_to_entry_| that map to
       // |model_|.
-      AppMenu::CommandIDToEntry::iterator iter =
-          app_menu_->command_id_to_entry_.begin();
+      auto iter = app_menu_->command_id_to_entry_.begin();
       while (iter != app_menu_->command_id_to_entry_.end()) {
         if (iter->second.first == model_)
           app_menu_->command_id_to_entry_.erase(iter++);
@@ -1014,7 +1013,7 @@
     return false;
   }
 
-  CommandIDToEntry::const_iterator ix = command_id_to_entry_.find(command_id);
+  auto ix = command_id_to_entry_.find(command_id);
   const Entry& entry = ix->second;
   ui::Accelerator menu_accelerator;
   if (!entry.first->GetAcceleratorAt(entry.second, &menu_accelerator))
@@ -1254,7 +1253,7 @@
 }
 
 int AppMenu::ModelIndexFromCommandId(int command_id) const {
-  CommandIDToEntry::const_iterator ix = command_id_to_entry_.find(command_id);
+  auto ix = command_id_to_entry_.find(command_id);
   DCHECK(ix != command_id_to_entry_.end());
   return ix->second.second;
 }
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index b1f6bbf..404023b 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -167,7 +167,7 @@
 void BrowserActionsContainer::RemoveViewForAction(
     ToolbarActionViewController* action) {
   std::unique_ptr<ToolbarActionView> view;
-  for (ToolbarActionViews::iterator iter = toolbar_action_views_.begin();
+  for (auto iter = toolbar_action_views_.begin();
        iter != toolbar_action_views_.end(); ++iter) {
     if ((*iter)->view_controller() == action) {
       std::swap(view, *iter);
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 2c7a134..53ae802 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -144,6 +144,11 @@
   }
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+  // TODO(hcarmona): Move this behind nux::kNuxOnboardingFeature when email and
+  // apps experiments end.
+  html_source->AddResourcePath("shared/chooser_shared_css.html",
+                               IDR_NUX_CHOOSER_SHARED_CSS);
+
   if (base::FeatureList::IsEnabled(nux::kNuxOnboardingFeature)) {
     web_ui->AddMessageHandler(std::make_unique<nux::SetAsDefaultHandler>());
     nux::SetAsDefaultHandler::AddSources(html_source);
diff --git a/chrome/browser/ui/window_sizer/window_sizer.cc b/chrome/browser/ui/window_sizer/window_sizer.cc
index 66cebbb..22c00880 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer.cc
@@ -98,8 +98,7 @@
       window = browser_->window();
     } else {
       const BrowserList* browser_list = BrowserList::GetInstance();
-      for (BrowserList::const_reverse_iterator it =
-               browser_list->begin_last_active();
+      for (auto it = browser_list->begin_last_active();
            it != browser_list->end_last_active(); ++it) {
         Browser* last_active = *it;
         if (last_active && last_active->is_type_tabbed()) {
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index 887e137..c54f40f 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -98,7 +98,7 @@
       {"extension-10", ExtensionIDKey::Tag::kArray},
   };
 
-  std::set<std::string>::const_iterator it = extensions.begin();
+  auto it = extensions.begin();
   for (size_t i = 0; i < arraysize(extension_ids); ++i) {
     if (it == extensions.end()) {
       extension_ids[i].Clear();
diff --git a/chrome/common/extensions/api/commands/commands_manifest_unittest.cc b/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
index c5f156d..12fde8b 100644
--- a/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
+++ b/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
@@ -41,7 +41,7 @@
   const CommandMap* commands = CommandsInfo::GetNamedCommands(extension.get());
   ASSERT_TRUE(commands);
   ASSERT_EQ(1u, commands->size());
-  CommandMap::const_iterator iter = commands->begin();
+  auto iter = commands->begin();
   ASSERT_TRUE(commands->end() != iter);
   const Command* named_command = &(*iter).second;
   ASSERT_STREQ("feature1", named_command->command_name().c_str());
diff --git a/chrome/common/extensions/api/common_extension_api_unittest.cc b/chrome/common/extensions/api/common_extension_api_unittest.cc
index a37895e..4f5cce0 100644
--- a/chrome/common/extensions/api/common_extension_api_unittest.cc
+++ b/chrome/common/extensions/api/common_extension_api_unittest.cc
@@ -429,8 +429,7 @@
   manifest.SetInteger("manifest_version", 2);
   {
     std::unique_ptr<base::ListValue> permissions_list(new base::ListValue());
-    for (std::set<std::string>::const_iterator i = permissions.begin();
-        i != permissions.end(); ++i) {
+    for (auto i = permissions.begin(); i != permissions.end(); ++i) {
       permissions_list->AppendString(*i);
     }
     manifest.Set("permissions", std::move(permissions_list));
@@ -542,8 +541,7 @@
   values.Set(manifest_keys::kApp, std::move(app));
   {
     auto permissions_list = std::make_unique<base::ListValue>();
-    for (std::set<std::string>::const_iterator i = permissions.begin();
-        i != permissions.end(); ++i) {
+    for (auto i = permissions.begin(); i != permissions.end(); ++i) {
       permissions_list->AppendString(*i);
     }
     values.Set("permissions", std::move(permissions_list));
diff --git a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
index d28fc9c9..701ec1b8 100644
--- a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
+++ b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
@@ -128,8 +128,7 @@
     return false;
   }
 
-  for (base::ListValue::const_iterator it = manif_patterns->begin();
-       it != manif_patterns->end(); ++it) {
+  for (auto it = manif_patterns->begin(); it != manif_patterns->end(); ++it) {
     std::string str_pattern;
     it->GetAsString(&str_pattern);
     // TODO(sergeygs): Limit this to non-top-level domains.
diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc
index c38ad97..299f60c 100644
--- a/chrome/common/extensions/chrome_extensions_client.cc
+++ b/chrome/common/extensions/chrome_extensions_client.cc
@@ -129,7 +129,7 @@
     PermissionIDSet* permissions) const {
   // When editing this function, be sure to add the same functionality to
   // FilterHostPermissions() above.
-  for (URLPatternSet::const_iterator i = hosts.begin(); i != hosts.end(); ++i) {
+  for (auto i = hosts.begin(); i != hosts.end(); ++i) {
     // Filters out every URL pattern that matches chrome:// scheme.
     if (i->scheme() == content::kChromeUIScheme) {
       // chrome://favicon is the only URL for chrome:// scheme that we
diff --git a/chrome/common/extensions/command.cc b/chrome/common/extensions/command.cc
index 1bc2d4a..a22cf55 100644
--- a/chrome/common/extensions/command.cc
+++ b/chrome/common/extensions/command.cc
@@ -495,8 +495,7 @@
   command->GetBoolean(keys::kGlobal, &global);
 
   // Normalize the suggestions.
-  for (SuggestionMap::iterator iter = suggestions.begin();
-       iter != suggestions.end(); ++iter) {
+  for (auto iter = suggestions.begin(); iter != suggestions.end(); ++iter) {
     // Before we normalize Ctrl to Command we must detect when the developer
     // specified Command in the Default section, which will work on Mac after
     // normalization but only fail on other platforms when they try it out on
diff --git a/chrome/common/pepper_permission_util.cc b/chrome/common/pepper_permission_util.cc
index 1d632ed..35bf998 100644
--- a/chrome/common/pepper_permission_util.cc
+++ b/chrome/common/pepper_permission_util.cc
@@ -53,9 +53,7 @@
 
   typedef std::vector<SharedModuleInfo::ImportInfo> ImportInfoVector;
   const ImportInfoVector& imports = SharedModuleInfo::GetImports(extension);
-  for (ImportInfoVector::const_iterator it = imports.begin();
-       it != imports.end();
-       ++it) {
+  for (auto it = imports.begin(); it != imports.end(); ++it) {
     const Extension* imported_extension =
         extension_set->GetByID(it->extension_id);
     if (imported_extension &&
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc
index b1c6a400..6c45e54 100644
--- a/chrome/renderer/chrome_render_frame_observer.cc
+++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -296,9 +296,7 @@
   // any icon with a data URL to have originated from a favicon.  We don't want
   // to decode arbitrary data URLs in the browser process.  See
   // http://b/issue?id=1162972
-  for (std::vector<WebApplicationInfo::IconInfo>::iterator it =
-           web_app_info.icons.begin();
-       it != web_app_info.icons.end();) {
+  for (auto it = web_app_info.icons.begin(); it != web_app_info.icons.end();) {
     if (it->url.SchemeIs(url::kDataScheme))
       it = web_app_info.icons.erase(it);
     else
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
index eb57fda..07f10e12 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
@@ -741,8 +741,7 @@
   v8::HandleScope handle_scope(isolate);
   v8::Context::Scope context_scope(context()->v8_context());
 
-  RtpStreamCallbackMap::iterator it =
-      get_raw_events_callbacks_.find(transport_id);
+  auto it = get_raw_events_callbacks_.find(transport_id);
   if (it == get_raw_events_callbacks_.end())
     return;
   v8::Local<v8::Value> callback_args[] = {V8ValueConverter::Create()->ToV8Value(
@@ -759,7 +758,7 @@
   v8::HandleScope handle_scope(isolate);
   v8::Context::Scope context_scope(context()->v8_context());
 
-  RtpStreamCallbackMap::iterator it = get_stats_callbacks_.find(transport_id);
+  auto it = get_stats_callbacks_.find(transport_id);
   if (it == get_stats_callbacks_.end())
     return;
 
@@ -772,8 +771,7 @@
 
 CastRtpStream* CastStreamingNativeHandler::GetRtpStreamOrThrow(
     int transport_id) const {
-  RtpStreamMap::const_iterator iter = rtp_stream_map_.find(
-      transport_id);
+  auto iter = rtp_stream_map_.find(transport_id);
   if (iter != rtp_stream_map_.end())
     return iter->second.get();
   v8::Isolate* isolate = context()->v8_context()->GetIsolate();
@@ -786,8 +784,7 @@
 
 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
     int transport_id) const {
-  UdpTransportMap::const_iterator iter = udp_transport_map_.find(
-      transport_id);
+  auto iter = udp_transport_map_.find(transport_id);
   if (iter != udp_transport_map_.end())
     return iter->second.get();
   v8::Isolate* isolate = context()->v8_context()->GetIsolate();
diff --git a/chrome/renderer/media/cast_transport_ipc.cc b/chrome/renderer/media/cast_transport_ipc.cc
index 0a5cd51..7d9d41d 100644
--- a/chrome/renderer/media/cast_transport_ipc.cc
+++ b/chrome/renderer/media/cast_transport_ipc.cc
@@ -137,7 +137,7 @@
 }
 
 void CastTransportIPC::OnRtt(uint32_t rtp_sender_ssrc, base::TimeDelta rtt) {
-  ClientMap::iterator it = clients_.find(rtp_sender_ssrc);
+  auto it = clients_.find(rtp_sender_ssrc);
   if (it == clients_.end()) {
     LOG(ERROR) << "Received RTT report for unknown SSRC: " << rtp_sender_ssrc;
     return;
@@ -148,7 +148,7 @@
 void CastTransportIPC::OnRtcpCastMessage(
     uint32_t rtp_sender_ssrc,
     const media::cast::RtcpCastMessage& cast_message) {
-  ClientMap::iterator it = clients_.find(rtp_sender_ssrc);
+  auto it = clients_.find(rtp_sender_ssrc);
   if (it == clients_.end()) {
     LOG(ERROR) << "Received cast message for unknown SSRC: " << rtp_sender_ssrc;
     return;
@@ -157,7 +157,7 @@
 }
 
 void CastTransportIPC::OnReceivedPli(uint32_t rtp_sender_ssrc) {
-  ClientMap::iterator it = clients_.find(rtp_sender_ssrc);
+  auto it = clients_.find(rtp_sender_ssrc);
   if (it == clients_.end()) {
     LOG(ERROR) << "Received picture loss indicator for unknown SSRC: "
                << rtp_sender_ssrc;
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index 13c4ad8..8bcc00b 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -55,6 +55,7 @@
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_document_loader.h"
 #include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/webui/jstemplate_builder.h"
@@ -388,8 +389,12 @@
 
 void NetErrorHelper::LoadErrorPage(const std::string& html,
                                    const GURL& failed_url) {
-  render_frame()->GetWebFrame()->LoadHTMLString(
-      html, GURL(kUnreachableWebDataURL), failed_url, true);
+  render_frame()->GetWebFrame()->CommitDataNavigation(
+      blink::WebURLRequest(GURL(kUnreachableWebDataURL)), blink::WebData(html),
+      blink::WebString::FromUTF8("text/html"),
+      blink::WebString::FromUTF8("UTF-8"), failed_url,
+      blink::WebFrameLoadType::kReplaceCurrentItem, blink::WebHistoryItem(),
+      false /* is_client_redirect */, nullptr, nullptr);
 }
 
 void NetErrorHelper::EnablePageHelperFunctions(net::Error net_error) {
diff --git a/chrome/renderer/prerender/prerender_dispatcher.cc b/chrome/renderer/prerender/prerender_dispatcher.cc
index cad6cd7..b7c3a3a4 100644
--- a/chrome/renderer/prerender/prerender_dispatcher.cc
+++ b/chrome/renderer/prerender/prerender_dispatcher.cc
@@ -54,7 +54,7 @@
 }
 
 void PrerenderDispatcher::PrerenderStart(int prerender_id) {
-  std::map<int, WebPrerender>::iterator it = prerenders_.find(prerender_id);
+  auto it = prerenders_.find(prerender_id);
   if (it == prerenders_.end())
     return;
 
@@ -68,7 +68,7 @@
 }
 
 void PrerenderDispatcher::PrerenderStopLoading(int prerender_id) {
-  std::map<int, WebPrerender>::iterator it = prerenders_.find(prerender_id);
+  auto it = prerenders_.find(prerender_id);
   if (it == prerenders_.end())
     return;
 
@@ -81,7 +81,7 @@
 }
 
 void PrerenderDispatcher::PrerenderDomContentLoaded(int prerender_id) {
-  std::map<int, WebPrerender>::iterator it = prerenders_.find(prerender_id);
+  auto it = prerenders_.find(prerender_id);
   if (it == prerenders_.end())
     return;
 
@@ -101,7 +101,7 @@
 void PrerenderDispatcher::PrerenderRemoveAliases(
     const std::vector<GURL>& aliases) {
   for (size_t i = 0; i < aliases.size(); ++i) {
-    std::multiset<GURL>::iterator it = running_prerender_urls_.find(aliases[i]);
+    auto it = running_prerender_urls_.find(aliases[i]);
     if (it != running_prerender_urls_.end()) {
       running_prerender_urls_.erase(it);
     }
@@ -109,7 +109,7 @@
 }
 
 void PrerenderDispatcher::PrerenderStop(int prerender_id) {
-  std::map<int, WebPrerender>::iterator it = prerenders_.find(prerender_id);
+  auto it = prerenders_.find(prerender_id);
   if (it == prerenders_.end())
     return;
   WebPrerender& prerender = it->second;
diff --git a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc
index a55b877..c570c33c 100644
--- a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc
+++ b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc
@@ -207,7 +207,7 @@
   // Check if the size of shingle hashes is over the limit.
   if (shingle_hashes_->size() > max_shingles_per_page_) {
     // Pop the largest one.
-    std::set<uint32_t>::iterator it = shingle_hashes_->end();
+    auto it = shingle_hashes_->end();
     shingle_hashes_->erase(--it);
   }
 
@@ -234,16 +234,14 @@
   //
   state_->previous_words.append(word_lower);
   std::string current_term = state_->previous_words;
-  for (std::list<size_t>::iterator it = state_->previous_word_sizes.begin();
+  for (auto it = state_->previous_word_sizes.begin();
        it != state_->previous_word_sizes.end(); ++it) {
     hashes_to_check[crypto::SHA256HashString(current_term)] = current_term;
     current_term.erase(0, *it);
   }
 
   // Add features for any hashes that match page_term_hashes_.
-  for (std::map<std::string, std::string>::iterator it =
-           hashes_to_check.begin();
-       it != hashes_to_check.end(); ++it) {
+  for (auto it = hashes_to_check.begin(); it != hashes_to_check.end(); ++it) {
     if (page_term_hashes_->find(it->first) != page_term_hashes_->end()) {
       features_->AddBooleanFeature(features::kPageTerm + it->second);
     }
diff --git a/chrome/renderer/safe_browsing/phishing_term_feature_extractor_unittest.cc b/chrome/renderer/safe_browsing/phishing_term_feature_extractor_unittest.cc
index 4089c02d..9ced887 100644
--- a/chrome/renderer/safe_browsing/phishing_term_feature_extractor_unittest.cc
+++ b/chrome/renderer/safe_browsing/phishing_term_feature_extractor_unittest.cc
@@ -54,8 +54,7 @@
     // Chinese (translation of "goodbye")
     terms.insert("\xe5\x86\x8d\xe8\xa7\x81");
 
-    for (base::hash_set<std::string>::iterator it = terms.begin();
-         it != terms.end(); ++it) {
+    for (auto it = terms.begin(); it != terms.end(); ++it) {
       term_hashes_.insert(crypto::SHA256HashString(*it));
     }
 
@@ -72,8 +71,7 @@
     words.insert("\xe4\xbd\xa0\xe5\xa5\xbd");
     words.insert("\xe5\x86\x8d\xe8\xa7\x81");
 
-    for (base::hash_set<std::string>::iterator it = words.begin();
-         it != words.end(); ++it) {
+    for (auto it = words.begin(); it != words.end(); ++it) {
       word_hashes_.insert(MurmurHash3String(*it, kMurmurHash3Seed));
     }
 
@@ -247,7 +245,7 @@
                                                    kMurmurHash3Seed));
   expected_shingle_hashes.insert(MurmurHash3String("way too many words ",
                                                    kMurmurHash3Seed));
-  std::set<uint32_t>::iterator it = expected_shingle_hashes.end();
+  auto it = expected_shingle_hashes.end();
   expected_shingle_hashes.erase(--it);
 
   features.Clear();
diff --git a/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
index 1b416f2..931c520 100644
--- a/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
+++ b/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
@@ -69,8 +69,7 @@
     host_tokens.pop_back();
 
     // Now we're just left with the "other" host tokens.
-    for (std::vector<std::string>::iterator it = host_tokens.begin();
-         it != host_tokens.end(); ++it) {
+    for (auto it = host_tokens.begin(); it != host_tokens.end(); ++it) {
       if (!features->AddBooleanFeature(features::kUrlOtherHostToken + *it))
         return false;
     }
diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc
index daccf7fb..2e071b2 100644
--- a/chrome/service/cloud_print/cloud_print_connector.cc
+++ b/chrome/service/cloud_print/cloud_print_connector.cc
@@ -137,7 +137,7 @@
     return;
   }
 
-  JobHandlerMap::iterator printer_it = job_handler_map_.find(printer_id);
+  auto printer_it = job_handler_map_.find(printer_id);
   if (printer_it == job_handler_map_.end()) {
     std::string status_message =
         l10n_util::GetStringUTF8(IDS_CLOUD_PRINT_ZOMBIE_PRINTER);
@@ -381,8 +381,7 @@
 bool CloudPrintConnector::RemovePrinterFromList(
     const std::string& printer_name,
     printing::PrinterList* printer_list) {
-  for (printing::PrinterList::iterator it = printer_list->begin();
-       it != printer_list->end(); ++it) {
+  for (auto it = printer_list->begin(); it != printer_list->end(); ++it) {
     if (IsSamePrinter(it->printer_name, printer_name)) {
       printer_list->erase(it);
       return true;
@@ -566,7 +565,7 @@
 
 void CloudPrintConnector::OnPrinterDelete(const std::string& printer_id) {
   // Remove corresponding printer job handler.
-  JobHandlerMap::iterator it = job_handler_map_.find(printer_id);
+  auto it = job_handler_map_.find(printer_id);
   if (it != job_handler_map_.end()) {
     it->second->Shutdown();
     job_handler_map_.erase(it);
diff --git a/chrome/service/cloud_print/connector_settings.cc b/chrome/service/cloud_print/connector_settings.cc
index 83a4617..f80ecc7 100644
--- a/chrome/service/cloud_print/connector_settings.cc
+++ b/chrome/service/cloud_print/connector_settings.cc
@@ -94,7 +94,7 @@
 }
 
 bool ConnectorSettings::ShouldConnect(const std::string& printer_name) const {
-  Printers::const_iterator printer = printers_.find(printer_name);
+  auto printer = printers_.find(printer_name);
   if (printer != printers_.end())
     return !connect_new_printers_;
   return connect_new_printers_;
diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc
index d526e93..e672f06 100644
--- a/chrome/service/cloud_print/printer_job_handler.cc
+++ b/chrome/service/cloud_print/printer_job_handler.cc
@@ -217,7 +217,7 @@
   base::subtle::NoBarrier_AtomicIncrement(&g_total_jobs_done, 1);
   job_queue_handler_.JobDone(job_details_.job_id_);
 
-  for (JobStatusUpdaterList::iterator it = job_status_updater_list_.begin();
+  for (auto it = job_status_updater_list_.begin();
        it != job_status_updater_list_.end(); ++it) {
     if (it->get() == updater) {
       job_status_updater_list_.erase(it);
diff --git a/chrome/services/media_gallery_util/media_metadata_parser.cc b/chrome/services/media_gallery_util/media_metadata_parser.cc
index ae99a20..9c9de4b 100644
--- a/chrome/services/media_gallery_util/media_metadata_parser.cc
+++ b/chrome/services/media_gallery_util/media_metadata_parser.cc
@@ -57,15 +57,12 @@
   metadata->title = extractor.title();
   metadata->track = extractor.track();
 
-  for (media::AudioVideoMetadataExtractor::StreamInfoVector::const_iterator it =
-           extractor.stream_infos().begin();
+  for (auto it = extractor.stream_infos().begin();
        it != extractor.stream_infos().end(); ++it) {
     chrome::mojom::MediaStreamInfoPtr stream_info =
         chrome::mojom::MediaStreamInfo::New(
             it->type, base::Value(base::Value::Type::DICTIONARY));
-    for (std::map<std::string, std::string>::const_iterator tag_it =
-             it->tags.begin();
-         tag_it != it->tags.end(); ++tag_it) {
+    for (auto tag_it = it->tags.begin(); tag_it != it->tags.end(); ++tag_it) {
       stream_info->additional_properties.SetKey(tag_it->first,
                                                 base::Value(tag_it->second));
     }
@@ -73,8 +70,7 @@
   }
 
   if (get_attached_images) {
-    for (std::vector<std::string>::const_iterator it =
-             extractor.attached_images_bytes().begin();
+    for (auto it = extractor.attached_images_bytes().begin();
          it != extractor.attached_images_bytes().end(); ++it) {
       attached_images->push_back(metadata::AttachedImage());
       attached_images->back().data = *it;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 27048fd..8bb7bcc 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2611,6 +2611,7 @@
     "../browser/prerender/prerender_util_unittest.cc",
     "../browser/previews/previews_infobar_delegate_unittest.cc",
     "../browser/previews/previews_lite_page_decider_unittest.cc",
+    "../browser/previews/previews_lite_page_infobar_delegate_unittest.cc",
     "../browser/previews/previews_lite_page_navigation_throttle_unittest.cc",
     "../browser/previews/previews_service_unittest.cc",
     "../browser/previews/previews_ui_tab_helper_unittest.cc",
diff --git a/chrome/test/base/test_chrome_web_ui_controller_factory.cc b/chrome/test/base/test_chrome_web_ui_controller_factory.cc
index f599baea..09cdf54b 100644
--- a/chrome/test/base/test_chrome_web_ui_controller_factory.cc
+++ b/chrome/test/base/test_chrome_web_ui_controller_factory.cc
@@ -55,7 +55,6 @@
 TestChromeWebUIControllerFactory::WebUIProvider*
     TestChromeWebUIControllerFactory::GetWebUIProvider(
         Profile* profile, const GURL& url) const {
-  FactoryOverridesMap::const_iterator found =
-      factory_overrides_.find(url.host());
+  auto found = factory_overrides_.find(url.host());
   return found != factory_overrides_.end() ? found->second : nullptr;
 }
diff --git a/chrome/test/base/test_launcher_utils.cc b/chrome/test/base/test_launcher_utils.cc
index 177ea0cc..22a199b 100644
--- a/chrome/test/base/test_launcher_utils.cc
+++ b/chrome/test/base/test_launcher_utils.cc
@@ -73,8 +73,7 @@
                              base::CommandLine* out_command_line) {
   const base::CommandLine::SwitchMap& switch_map =
       in_command_line.GetSwitches();
-  for (base::CommandLine::SwitchMap::const_iterator i = switch_map.begin();
-       i != switch_map.end(); ++i) {
+  for (auto i = switch_map.begin(); i != switch_map.end(); ++i) {
     const std::string& switch_name = i->first;
     if (switch_name == switch_to_remove)
       continue;
diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc
index c122355..43f0f0ff 100644
--- a/chrome/test/base/testing_profile_manager.cc
+++ b/chrome/test/base/testing_profile_manager.cc
@@ -175,7 +175,7 @@
 void TestingProfileManager::DeleteTestingProfile(const std::string& name) {
   DCHECK(called_set_up_);
 
-  TestingProfilesMap::iterator it = testing_profiles_.find(name);
+  auto it = testing_profiles_.find(name);
   DCHECK(it != testing_profiles_.end());
 
   TestingProfile* profile = it->second;
@@ -191,8 +191,8 @@
 void TestingProfileManager::DeleteAllTestingProfiles() {
   ProfileAttributesStorage& storage =
       profile_manager_->GetProfileAttributesStorage();
-  for (TestingProfilesMap::iterator it = testing_profiles_.begin();
-       it != testing_profiles_.end(); ++it) {
+  for (auto it = testing_profiles_.begin(); it != testing_profiles_.end();
+       ++it) {
     TestingProfile* profile = it->second;
     storage.RemoveProfile(profile->GetPath());
   }
@@ -203,7 +203,7 @@
 void TestingProfileManager::DeleteGuestProfile() {
   DCHECK(called_set_up_);
 
-  TestingProfilesMap::iterator it = testing_profiles_.find(kGuestProfileName);
+  auto it = testing_profiles_.find(kGuestProfileName);
   DCHECK(it != testing_profiles_.end());
 
   profile_manager_->profiles_info_.erase(ProfileManager::GetGuestProfilePath());
@@ -212,7 +212,7 @@
 void TestingProfileManager::DeleteSystemProfile() {
   DCHECK(called_set_up_);
 
-  TestingProfilesMap::iterator it = testing_profiles_.find(kSystemProfileName);
+  auto it = testing_profiles_.find(kSystemProfileName);
   DCHECK(it != testing_profiles_.end());
 
   profile_manager_->profiles_info_.erase(
diff --git a/chrome/test/base/v8_unit_test.cc b/chrome/test/base/v8_unit_test.cc
index 7f8ed5b..0243c81 100644
--- a/chrome/test/base/v8_unit_test.cc
+++ b/chrome/test/base/v8_unit_test.cc
@@ -59,8 +59,7 @@
 
 bool V8UnitTest::ExecuteJavascriptLibraries() {
   std::string utf8_content;
-  for (std::vector<base::FilePath>::iterator user_libraries_iterator =
-           user_libraries_.begin();
+  for (auto user_libraries_iterator = user_libraries_.begin();
        user_libraries_iterator != user_libraries_.end();
        ++user_libraries_iterator) {
     std::string library_content;
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index bb93d11f..450c5560 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -610,9 +610,8 @@
 }
 
 void Switches::SetFromSwitches(const Switches& switches) {
-  for (SwitchMap::const_iterator iter = switches.switch_map_.begin();
-       iter != switches.switch_map_.end();
-       ++iter) {
+  for (auto iter = switches.switch_map_.begin();
+       iter != switches.switch_map_.end(); ++iter) {
     switch_map_[iter->first] = iter->second;
   }
 }
@@ -651,7 +650,7 @@
 
 Switches::NativeString Switches::GetSwitchValueNative(
     const std::string& name) const {
-  SwitchMap::const_iterator iter = switch_map_.find(name);
+  auto iter = switch_map_.find(name);
   if (iter == switch_map_.end())
     return NativeString();
   return iter->second;
@@ -662,16 +661,14 @@
 }
 
 void Switches::AppendToCommandLine(base::CommandLine* command) const {
-  for (SwitchMap::const_iterator iter = switch_map_.begin();
-       iter != switch_map_.end();
-       ++iter) {
+  for (auto iter = switch_map_.begin(); iter != switch_map_.end(); ++iter) {
     command->AppendSwitchNative(iter->first, iter->second);
   }
 }
 
 std::string Switches::ToString() const {
   std::string str;
-  SwitchMap::const_iterator iter = switch_map_.begin();
+  auto iter = switch_map_.begin();
   while (iter != switch_map_.end()) {
     str += "--" + iter->first;
     std::string value = GetSwitchValue(iter->first);
@@ -756,8 +753,7 @@
         base::BindRepeating(&ParseBoolean, &network_emulation_enabled);
   }
 
-  for (std::map<std::string, Parser>::iterator it = parser_map.begin();
-       it != parser_map.end(); ++it) {
+  for (auto it = parser_map.begin(); it != parser_map.end(); ++it) {
     const base::Value* capability = NULL;
     if (desired_caps.Get(it->first, &capability)) {
       Status status = it->second.Run(*capability, this);
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index 52cd0af..a2f76dd 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -27,8 +27,7 @@
 }
 
 bool ChromeImpl::HasCrashedWebView() {
-  for (WebViewList::iterator it = web_views_.begin();
-       it != web_views_.end(); ++it) {
+  for (auto it = web_views_.begin(); it != web_views_.end(); ++it) {
     if ((*it)->WasCrashed())
       return true;
   }
@@ -72,7 +71,7 @@
                                 bool w3c_compliant) {
   // Check if some web views are closed (or in the case of background pages,
   // become inactive).
-  WebViewList::iterator it = web_views_.begin();
+  auto it = web_views_.begin();
   while (it != web_views_.end()) {
     const WebViewInfo* view = views_info.GetForId((*it)->GetId());
     if (!view || view->IsInactiveBackgroundPage()) {
@@ -112,8 +111,7 @@
 }
 
 Status ChromeImpl::GetWebViewById(const std::string& id, WebView** web_view) {
-  for (WebViewList::iterator it = web_views_.begin();
-       it != web_views_.end(); ++it) {
+  for (auto it = web_views_.begin(); it != web_views_.end(); ++it) {
     if ((*it)->GetId() == id) {
       *web_view = (*it).get();
       return Status(kOk);
@@ -199,8 +197,7 @@
   Status status = devtools_http_client_->CloseWebView(id);
   if (status.IsError())
     return status;
-  for (WebViewList::iterator iter = web_views_.begin();
-       iter != web_views_.end(); ++iter) {
+  for (auto iter = web_views_.begin(); iter != web_views_.end(); ++iter) {
     if ((*iter)->GetId() == id) {
       web_views_.erase(iter);
       break;
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index 8f333eb3..d651608 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -385,7 +385,7 @@
   // have been deleted from |response_info_map_|) or blocked while notifying
   // listeners.
   if (expected_id != -1) {
-    ResponseInfoMap::iterator iter = response_info_map_.find(expected_id);
+    auto iter = response_info_map_.find(expected_id);
     if (iter == response_info_map_.end() || iter->second->state != kWaiting)
       return Status(kOk);
   }
@@ -508,7 +508,7 @@
 
 Status DevToolsClientImpl::ProcessCommandResponse(
     const internal::InspectorCommandResponse& response) {
-  ResponseInfoMap::iterator iter = response_info_map_.find(response.id);
+  auto iter = response_info_map_.find(response.id);
   if (IsVLogOn(1)) {
     std::string method, result;
     if (iter != response_info_map_.end())
diff --git a/chrome/test/chromedriver/chrome/network_conditions.cc b/chrome/test/chromedriver/chrome/network_conditions.cc
index bc28c9e..62270bf3 100644
--- a/chrome/test/chromedriver/chrome/network_conditions.cc
+++ b/chrome/test/chromedriver/chrome/network_conditions.cc
@@ -35,9 +35,7 @@
   if (!networks_value->GetAsList(&networks))
     return Status(kUnknownError, "malformed networks list");
 
-  for (base::ListValue::iterator it = networks->begin();
-       it != networks->end();
-       ++it) {
+  for (auto it = networks->begin(); it != networks->end(); ++it) {
     base::DictionaryValue* network = NULL;
     if (!it->GetAsDictionary(&network)) {
       return Status(kUnknownError,
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 1cee354..5ea1c85 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -412,8 +412,7 @@
                    "new Promise(x => setTimeout(() => setTimeout(x, 20), 20)");
   params.SetBoolean("awaitPromise", true);
   client_->SendCommand("Runtime.evaluate", params);
-  for (std::list<MouseEvent>::const_iterator it = events.begin();
-       it != events.end(); ++it) {
+  for (auto it = events.begin(); it != events.end(); ++it) {
     base::DictionaryValue params;
 
     switch (it->type) {
@@ -453,8 +452,7 @@
     return DispatchTouchEventsForMouseEvents(events, frame);
 
   double page_scale_factor = 1.0;
-  for (std::list<MouseEvent>::const_iterator it = events.begin();
-       it != events.end(); ++it) {
+  for (auto it = events.begin(); it != events.end(); ++it) {
     base::DictionaryValue params;
     params.SetString("type", GetAsString(it->type));
     params.SetInteger("x", it->x * page_scale_factor);
@@ -479,8 +477,7 @@
 }
 
 Status WebViewImpl::DispatchTouchEvents(const std::list<TouchEvent>& events) {
-  for (std::list<TouchEvent>::const_iterator it = events.begin();
-       it != events.end(); ++it) {
+  for (auto it = events.begin(); it != events.end(); ++it) {
     Status status = DispatchTouchEvent(*it);
     if (status.IsError())
       return status;
@@ -489,8 +486,7 @@
 }
 
 Status WebViewImpl::DispatchKeyEvents(const std::list<KeyEvent>& events) {
-  for (std::list<KeyEvent>::const_iterator it = events.begin();
-       it != events.end(); ++it) {
+  for (auto it = events.begin(); it != events.end(); ++it) {
     base::DictionaryValue params;
     params.SetString("type", GetAsString(it->type));
     if (it->modifiers & kNumLockKeyModifierMask) {
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index 9a26a2f..8a0371e 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -316,7 +316,7 @@
     const base::DictionaryValue& params,
     const std::string& session_id,
     const CommandCallback& callback) {
-  SessionThreadMap::iterator iter = session_thread_map->find(session_id);
+  auto iter = session_thread_map->find(session_id);
   if (iter == session_thread_map->end()) {
     Status status(return_ok_without_session ? kOk : kInvalidSessionId);
     callback.Run(status, std::unique_ptr<base::Value>(), session_id,
diff --git a/chrome/test/chromedriver/devtools_events_logger.cc b/chrome/test/chromedriver/devtools_events_logger.cc
index 9796887..94d60d0 100644
--- a/chrome/test/chromedriver/devtools_events_logger.cc
+++ b/chrome/test/chromedriver/devtools_events_logger.cc
@@ -17,9 +17,7 @@
 inline DevToolsEventsLogger::~DevToolsEventsLogger() {}
 
 Status DevToolsEventsLogger::OnConnected(DevToolsClient* client) {
-  for (base::ListValue::const_iterator it = prefs_->begin();
-       it != prefs_->end();
-       ++it) {
+  for (auto it = prefs_->begin(); it != prefs_->end(); ++it) {
     std::string event;
     it->GetAsString(&event);
     events_.insert(event);
@@ -30,7 +28,7 @@
 Status DevToolsEventsLogger::OnEvent(DevToolsClient* client,
                                      const std::string& method,
                                      const base::DictionaryValue& params) {
-  std::unordered_set<std::string>::iterator it = events_.find(method);
+  auto it = events_.find(method);
   if (it != events_.end()) {
     base::DictionaryValue log_message_dict;
     log_message_dict.SetString("method", method);
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 472dad3..a5e0fe5d 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -669,8 +669,8 @@
       "  return document.evaluate(xpath, document, null,"
       "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
       "}";
-  for (std::list<FrameInfo>::reverse_iterator rit = session->frames.rbegin();
-       rit != session->frames.rend(); ++rit) {
+  for (auto rit = session->frames.rbegin(); rit != session->frames.rend();
+       ++rit) {
     base::ListValue args;
     args.AppendString(
         base::StringPrintf("//*[@cd_frame_id_ = '%s']",
diff --git a/chrome/test/chromedriver/key_converter_unittest.cc b/chrome/test/chromedriver/key_converter_unittest.cc
index 2b9da34..b88a7af 100644
--- a/chrome/test/chromedriver/key_converter_unittest.cc
+++ b/chrome/test/chromedriver/key_converter_unittest.cc
@@ -28,7 +28,7 @@
   EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, release_modifiers,
                                         &modifiers, &events).code());
   EXPECT_EQ(expected_events.size(), events.size());
-  std::list<KeyEvent>::const_iterator expected = expected_events.begin();
+  auto expected = expected_events.begin();
   std::list<KeyEvent>::const_iterator actual = events.begin();
   while (expected != expected_events.end() && actual != events.end()) {
     EXPECT_EQ(expected->type, actual->type);
diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc
index 8e3beaf..e3712cd 100644
--- a/chrome/test/chromedriver/logging.cc
+++ b/chrome/test/chromedriver/logging.cc
@@ -169,9 +169,7 @@
 
 bool GetFirstErrorMessageFromList(const base::ListValue* list,
                                   std::string* message) {
-  for (base::ListValue::const_iterator it = list->begin();
-       it != list->end();
-       ++it) {
+  for (auto it = list->begin(); it != list->end(); ++it) {
     const base::DictionaryValue* log_entry = NULL;
     it->GetAsDictionary(&log_entry);
     if (log_entry != NULL) {
@@ -314,9 +312,7 @@
   Log::Level browser_log_level = Log::kWarning;
   const LoggingPrefs& prefs = capabilities.logging_prefs;
 
-  for (LoggingPrefs::const_iterator iter = prefs.begin();
-       iter != prefs.end();
-       ++iter) {
+  for (auto iter = prefs.begin(); iter != prefs.end(); ++iter) {
     std::string type = iter->first;
     Log::Level level = iter->second;
     if (type == WebDriverLog::kPerformanceType) {
diff --git a/chrome/test/data/nacl/exit_status/pm_exit_status_test.cc b/chrome/test/data/nacl/exit_status/pm_exit_status_test.cc
index 0254f382..ecb09c3 100644
--- a/chrome/test/data/nacl/exit_status/pm_exit_status_test.cc
+++ b/chrome/test/data/nacl/exit_status/pm_exit_status_test.cc
@@ -18,6 +18,7 @@
 #include <sys/fcntl.h>
 #include <unistd.h>
 
+#include "base/format_macros.h"
 #include "ppapi/cpp/instance.h"
 #include "ppapi/cpp/module.h"
 #include "ppapi/cpp/var.h"
@@ -93,14 +94,14 @@
 
     for (size_t ix = 0; kMsgHandlers[ix].request != NULL; ++ix) {
       if (op_name == kMsgHandlers[ix].request) {
-        fprintf(stderr, "found at index %u\n", ix);
+        fprintf(stderr, "found at index %" PRIuS "\n", ix);
         kMsgHandlers[ix].handler(message_data, &sb);
         break;
       }
     }
 
     len = strlen(sb.c_str());
-    fprintf(stderr, "posting reply len %d\n", len);
+    fprintf(stderr, "posting reply len %" PRIuS "\n", len);
     fprintf(stderr, "posting reply \"%s\".\n", sb.c_str());
     fflush(stderr);
 
diff --git a/chrome/test/data/nacl/sysconf_nprocessors_onln/sysconf_nprocessors_onln_test.cc b/chrome/test/data/nacl/sysconf_nprocessors_onln/sysconf_nprocessors_onln_test.cc
index 4c7355da..82e83c8 100644
--- a/chrome/test/data/nacl/sysconf_nprocessors_onln/sysconf_nprocessors_onln_test.cc
+++ b/chrome/test/data/nacl/sysconf_nprocessors_onln/sysconf_nprocessors_onln_test.cc
@@ -76,14 +76,14 @@
 
     for (size_t ix = 0; kMsgHandlers[ix].request != NULL; ++ix) {
       if (op_name == kMsgHandlers[ix].request) {
-        fprintf(stderr, "found at index %u\n", ix);
+        fprintf(stderr, "found at index %zu\n", ix);
         kMsgHandlers[ix].handler(message_data, &sb);
         break;
       }
     }
 
     len = strlen(sb.c_str());
-    fprintf(stderr, "posting reply len %d\n", len);
+    fprintf(stderr, "posting reply len %zu\n", len);
     fprintf(stderr, "posting reply \"%s\".\n", sb.c_str());
 
     PostMessage(pp::Var(sb));
diff --git a/chrome/test/data/perf/tough_compositor_cases/infinite_scroll_root_fixed_n_layers.html b/chrome/test/data/perf/tough_compositor_cases/infinite_scroll_root_fixed_n_layers.html
new file mode 100644
index 0000000..e5e82a7
--- /dev/null
+++ b/chrome/test/data/perf/tough_compositor_cases/infinite_scroll_root_fixed_n_layers.html
@@ -0,0 +1,252 @@
+<!doctype html>
+<!--
+Copyright (c) 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.
+-->
+<html>
+  <head>
+    <title>infinite-scroll</title>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+    <style>
+      html {
+        height: 100%;
+      }
+      body {
+        display: block;
+        height: 100%;
+        margin: 0;
+      }
+      #container {
+        overflow-y: scroll;
+        height: 100%;
+        margin: 0;
+        contain: layout;
+      }
+      #runway {
+        height: 100000px;
+        contain: layout;
+      }
+      .item {
+        -webkit-transform: translate3d(0,0,0);
+        box-sizing: border-box;
+        overflow: hidden;
+        contain: layout;
+      }
+
+      .conversation {
+        font-family: sans-serif;
+        height: 70px;
+        display: flex;
+        border-bottom: 1px solid lightgray;
+      }
+      .avatar {
+        display: flex;
+        flex-shrink: 0;
+        color: white;
+        align-items: center;
+        justify-content: center;
+        font-size: 40px;
+        width: 50px;
+        height: 50px;
+        margin: 10px;
+        background-color: lightblue;
+      }
+      .summary {
+        flex: 1;
+        flex-shrink: 0;
+        display: flex;
+        flex-direction: column;
+        padding: 10px 10px 10px 0px;
+      }
+      .topline {
+        display: flex;
+        flex-shrink: 0;
+      }
+      .participants {
+        flex: 1;
+        flex-shrink: 0;
+        font-weight: bold;
+      }
+      .time {
+        flex-shrink: 0;
+        font-size: 12px;
+        color: #444;
+      }
+      .bottomline {
+        flex-shrink: 0;
+        display: flex;
+      }
+      .preview {
+        flex: 1;
+        flex-shrink: 0;
+        font-size: 12px;
+        height: 2em;
+        text-overflow: ellipsis;
+      }
+      .subject {
+        font-weight: bold;
+      }
+      .snippet {
+        color: #444;
+      }
+      .trinkets {
+        flex-shrink: 0;
+        font-size: 20px;
+      }
+      .fixed {
+        position: fixed;
+        top: 0;
+        left: 50%;
+        transform: translate(-50%, 0);
+        border: 3px solid #73AD21;
+      }
+    </style>
+  </head>
+  <body >
+    <div id="runway">
+      <template>
+        <div class="item conversation">
+          <div class="avatar">A</div>
+          <div class="summary">
+            <div class="topline">
+              <div class="participants">{{ participants }}</div>
+              <div class="time">{{ time }}</div>
+            </div>
+            <div class="bottomline">
+              <div class="preview"><span class="subject">{{ subject }}</span> &mdash;
+                <span class="snippet">{{ snippet }}</span></div>
+              <div class="trinkets">&#x2606;</div>
+            </div>
+          </div>
+        </div>
+      </template>
+    </div>
+    <div class="fixed">This div element has position: fixed</div>
+    <script>
+      var qs = (function(a) {
+        if (a == "") return {};
+        var b = {};
+        for (var i = 0; i < a.length; ++i)
+        {
+          var p=a[i].split('=', 2);
+          if (p.length == 1)
+            b[p[0]] = "";
+          else
+            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
+        }
+        return b;
+      })(window.location.search.substr(1).split('&'));
+
+      function getRandomItem(array) {
+        return array[Math.floor(Math.random() * array.length)];
+      }
+      function generateFakeData() {
+        var kNumberOfItems = 500;
+        var possibleParticipants = [
+          'Adam', 'Ojan', 'Elliot', 'Chris',
+        ];
+        var possibleTimes = [
+          'Now', 'Yesterday', 'Last week',
+        ];
+        var possibleSubjects = [
+          'Do you even bench?',
+          'I like to scroll forever',
+          'Lunch',
+          'What if my subject is really long? Longer than that. Like really, really, long?',
+        ];
+        var possibleSnippets = [
+          'Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.',
+          'When, in disgrace with fortune and men\'s eyes, I all alone beweep my outcast state,',
+          'We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility',
+        ];
+        var data = new Array(kNumberOfItems);
+        for (var i = 0; i < kNumberOfItems; ++i) {
+          data[i] = {
+            participants: getRandomItem(possibleParticipants),
+            time: getRandomItem(possibleTimes),
+            subject: getRandomItem(possibleSubjects),
+            snippet: getRandomItem(possibleSnippets),
+          }
+          console.log(data[i]);
+        }
+        return data;
+      }
+
+      var data = generateFakeData();
+
+      "use strict";
+      (function(exports) {
+        var kHeight = 70;
+        var kPhysicalCount = qs['layer_count'];
+
+        var container = window;
+        var runway = document.getElementById('runway');
+
+
+        var template = runway.querySelector('template');
+        var items = new Array(kPhysicalCount);
+        for (var i = 0; i < kPhysicalCount; ++i) {
+          var fragment = template.content.cloneNode(true);
+          runway.appendChild(fragment);
+          var item = runway.lastElementChild;
+          items[i] = item;
+          item.transformValue_ = 0;
+          item.participants_ = item.querySelector('.participants').firstChild;
+          item.time_ = item.querySelector('.time').firstChild;
+          item.subject_ = item.querySelector('.subject').firstChild;
+          item.snippet_ = item.querySelector('.snippet').firstChild;
+
+          updateText(item, i);
+        }
+
+        var physicalHeight = kHeight * kPhysicalCount;
+
+        function updateText(item, index) {
+          var datum = data[index % data.length];
+          item.participants_.nodeValue = datum.participants;
+          item.time_.nodeValue = datum.time;
+          item.subject_.nodeValue = datum.subject;
+          item.snippet_.nodeValue = datum.snippet;
+        }
+
+        container.addEventListener('scroll', function(e) {
+          var scrollTop = container.scrollY;
+
+          var firstVirtualIndex = Math.floor(scrollTop / kHeight);
+          var firstPhysicalIndex = firstVirtualIndex % kPhysicalCount;
+
+          var baseVirtualIndex = firstVirtualIndex - firstPhysicalIndex;
+
+          var baseTransformValue = kHeight * baseVirtualIndex;
+          var nextTransformValue = baseTransformValue + physicalHeight;
+
+          var baseTransformString = 'translate3d(0,' + baseTransformValue + 'px,0)';
+          var nextTransformString = 'translate3d(0,' + nextTransformValue + 'px,0)';
+
+          window.requestAnimationFrame(function() {
+            for (var i = 0; i < firstPhysicalIndex; ++i) {
+              var item = items[i];
+              if (item.transformValue_ != nextTransformValue) {
+                // NOTE(vmiura): Removed updateText to remove layout changes from the benchmark.
+                //updateText(item, baseVirtualIndex + kPhysicalCount + i);
+                item.style.WebkitTransform = nextTransformString;
+                item.transformValue_ = nextTransformValue;
+              }
+
+            }
+            for (var i = firstPhysicalIndex; i < kPhysicalCount; ++i) {
+              var item = items[i];
+              if (item.transformValue_ != baseTransformValue) {
+                // NOTE(vmiura): Removed updateText to remove layout changes from the benchmark.
+                //updateText(item, baseVirtualIndex + i);
+                item.style.WebkitTransform = baseTransformString;
+                item.transformValue_ = baseTransformValue;
+              }
+            }
+          });
+        });
+      })(window);
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index 29f7ed15..2435e011 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -8,6 +8,8 @@
     ValidPageRanges: 'valid page ranges',
     InvalidPageRanges: 'invalid page ranges',
     NupChangesPages: 'nup changes pages',
+    ClearInput: 'clear input',
+    TabOrder: 'tab order',
   };
 
   const suiteName = 'PagesSettingsTest';
@@ -18,6 +20,12 @@
     /** @type {?print_preview.DocumentInfo} */
     let documentInfo = null;
 
+    /** @type {!Array<number>} */
+    const oneToHundred = Array.from({length: 100}, (x, i) => i + 1);
+
+    /** @type {string} */
+    const limitError = 'Out of bounds page reference, limit is ';
+
     /** @override */
     setup(function() {
       documentInfo = new print_preview.DocumentInfo();
@@ -89,99 +97,97 @@
           });
     }
 
-    /** @param {!Array<number>} expectedPages The expected pages value. */
-    function validateState(expectedPages) {
+    /**
+     * @param {!Array<number>} expectedPages The expected pages value.
+     * @param {string} expectedError The expected error message.
+     * @param {boolean} invalid Whether the pages setting should be invalid.
+     */
+    function validateState(expectedPages, expectedError, invalid) {
       const pagesValue = pagesSection.getSettingValue('pages');
       assertEquals(expectedPages.length, pagesValue.length);
       expectedPages.forEach((page, index) => {
         assertEquals(page, pagesValue[index]);
       });
-      assertTrue(pagesSection.$$('cr-input').errorMessage.length === 0);
+      assertEquals(!invalid, pagesSection.getSetting('pages').valid);
+      assertEquals(expectedError !== '', pagesSection.$$('cr-input').invalid);
+      assertEquals(expectedError, pagesSection.$$('cr-input').errorMessage);
     }
 
     // Tests that the page ranges set are valid for different user inputs.
     test(assert(TestNames.ValidPageRanges), function() {
-      const oneToHundred = Array.from({length: 100}, (x, i) => i + 1);
       const tenToHundred = Array.from({length: 91}, (x, i) => i + 10);
 
       return setupInput('1, 2, 3, 1, 56', 100)
           .then(function() {
-            validateState([1, 2, 3, 56]);
+            validateState([1, 2, 3, 56], '', false);
             return setupInput('1-3, 6-9, 6-10', 100);
           })
           .then(function() {
-            validateState([1, 2, 3, 6, 7, 8, 9, 10]);
+            validateState([1, 2, 3, 6, 7, 8, 9, 10], '', false);
             return setupInput('10-', 100);
           })
           .then(function() {
-            validateState(tenToHundred);
+            validateState(tenToHundred, '', false);
             return setupInput('10-100', 100);
           })
           .then(function() {
-            validateState(tenToHundred);
+            validateState(tenToHundred, '', false);
             return setupInput('-', 100);
           })
           .then(function() {
-            validateState(oneToHundred);
+            validateState(oneToHundred, '', false);
             // https://crbug.com/806165
             return setupInput('1\u30012\u30013\u30011\u300156', 100);
           })
           .then(function() {
-            validateState([1, 2, 3, 56]);
+            validateState([1, 2, 3, 56], '', false);
             return setupInput('1,2,3\u30011\u300156', 100);
           })
           .then(function() {
-            validateState([1, 2, 3, 56]);
+            validateState([1, 2, 3, 56], '', false);
           });
     });
 
     // Tests that the correct error messages are shown for different user
     // inputs.
     test(assert(TestNames.InvalidPageRanges), function() {
-      const limitError = 'Out of bounds page reference, limit is ';
       const syntaxError = 'Invalid page range, use e.g. 1-5, 8, 11-13';
 
-      /** @param {string} expectedMessage The expected error message. */
-      const validateErrorState = function(expectedMessage) {
-        assertFalse(pagesSection.$$('cr-input').errorMessage.length === 0);
-        assertEquals(expectedMessage, pagesSection.$$('cr-input').errorMessage);
-      };
-
       return setupInput('10-100000', 100)
           .then(function() {
-            validateErrorState(limitError + '100');
+            validateState(oneToHundred, limitError + '100', true);
             return setupInput('1, 100000', 100);
           })
           .then(function() {
-            validateErrorState(limitError + '100');
+            validateState(oneToHundred, limitError + '100', true);
             return setupInput('1, 2, 0, 56', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
             return setupInput('-1, 1, 2,, 56', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
             return setupInput('1,2,56-40', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
             return setupInput('101-110', 100);
           })
           .then(function() {
-            validateErrorState(limitError + '100');
+            validateState(oneToHundred, limitError + '100', true);
             return setupInput('1\u30012\u30010\u300156', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
             return setupInput('-1,1,2\u3001\u300156', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
             return setupInput('--', 100);
           })
           .then(function() {
-            validateErrorState(syntaxError);
+            validateState(oneToHundred, syntaxError, true);
           });
     });
 
@@ -202,35 +208,179 @@
           .then(function() {
             const rangesValue =
                 JSON.stringify(pagesSection.getSettingValue('ranges'));
-            validateState([1, 2, 3, 56]);
+            validateState([1, 2, 3, 56], '', false);
             pagesSection.setSetting('pagesPerSheet', 2);
             validateRanges(rangesValue);
-            validateState([1, 2]);
+            validateState([1, 2], '', false);
             pagesSection.setSetting('pagesPerSheet', 4);
             validateRanges(rangesValue);
-            validateState([1]);
+            validateState([1], '', false);
             pagesSection.setSetting('pagesPerSheet', 1);
             return setupInput('1-3, 6-9, 6-10', 100);
           })
           .then(function() {
             const rangesValue =
                 JSON.stringify(pagesSection.getSettingValue('ranges'));
-            validateState([1, 2, 3, 6, 7, 8, 9, 10]);
+            validateState([1, 2, 3, 6, 7, 8, 9, 10], '', false);
             pagesSection.setSetting('pagesPerSheet', 2);
             validateRanges(rangesValue);
-            validateState([1, 2, 3, 4]);
+            validateState([1, 2, 3, 4], '', false);
             pagesSection.setSetting('pagesPerSheet', 3);
             validateRanges(rangesValue);
-            validateState([1, 2, 3]);
+            validateState([1, 2, 3], '', false);
             return setupInput('1-3', 100);
           })
           .then(function() {
             const rangesValue =
                 JSON.stringify(pagesSection.getSettingValue('ranges'));
-            validateState([1]);
+            validateState([1], '', false);
             pagesSection.setSetting('pagesPerSheet', 1);
             validateRanges(rangesValue);
-            validateState([1, 2, 3]);
+            validateState([1, 2, 3], '', false);
+          });
+    });
+
+    // Tests that the clearing a valid input has no effect, clearing an invalid
+    // input does not show an error message but does not reset the preview, and
+    // changing focus from an empty input in either case automatically reselects
+    // the "all" radio button.
+    test(assert(TestNames.ClearInput), function() {
+      const input = pagesSection.$.pageSettingsCustomInput.inputElement;
+      const radioGroup = pagesSection.$$('paper-radio-group');
+      assertEquals(pagesSection.pagesValueEnum_.ALL, radioGroup.selected);
+      return setupInput('1-2', 3)
+          .then(function() {
+            assertEquals(
+                pagesSection.pagesValueEnum_.CUSTOM, radioGroup.selected);
+            validateState([1, 2], '', false);
+            return setupInput('', 3);
+          })
+          .then(function() {
+            assertEquals(
+                pagesSection.pagesValueEnum_.CUSTOM, radioGroup.selected);
+            validateState([1, 2], '', false);
+            const whenBlurred = test_util.eventToPromise('blur', input);
+            input.blur();
+            return whenBlurred;
+          })
+          .then(function() {
+            assertEquals(pagesSection.pagesValueEnum_.ALL, radioGroup.selected);
+            validateState([1, 2, 3], '', false);
+            return setupInput('5', 3);
+          })
+          .then(function() {
+            assertEquals(
+                pagesSection.pagesValueEnum_.CUSTOM, radioGroup.selected);
+            validateState([1, 2, 3], limitError + '3', true);
+            return setupInput('', 3);
+          })
+          .then(function() {
+            assertEquals(
+                pagesSection.pagesValueEnum_.CUSTOM, radioGroup.selected);
+            validateState([1, 2, 3], '', true);
+            const whenBlurred = test_util.eventToPromise('blur', input);
+            input.blur();
+            return whenBlurred;
+          })
+          .then(function() {
+            assertEquals(pagesSection.pagesValueEnum_.ALL, radioGroup.selected);
+            validateState([1, 2, 3], '', false);
+          });
+    });
+
+    // Tests that the radio buttons and custom input are appropriately
+    // inside/outside the tab order.
+    test(assert(TestNames.TabOrder), function() {
+      documentInfo.updatePageCount(3);
+
+      const radioGroup = pagesSection.$$('paper-radio-group');
+      const customRadio = pagesSection.$.customRadioButton;
+      const allRadio = pagesSection.$.allRadioButton;
+      const input = pagesSection.$.pageSettingsCustomInput;
+
+      /** @param {boolean} allSelected Whether all radio button is selected. */
+      const validateTabOrder = function(allSelected) {
+        const expectedSelection = allSelected ?
+            pagesSection.pagesValueEnum_.ALL :
+            pagesSection.pagesValueEnum_.CUSTOM;
+        assertEquals(expectedSelection, radioGroup.selected);
+        assertEquals(allSelected, customRadio.tabIndex === -1);
+        assertEquals(allSelected, input.tabIndex === -1);
+        assertEquals(!allSelected, allRadio.tabIndex === -1);
+      };
+
+      let whenFocused = test_util.eventToPromise('focus', radioGroup);
+      // Focus the radio group.
+      radioGroup.focus();
+      return whenFocused
+          .then(() => {
+            // Start out with all selected.
+            validateTabOrder(true);
+
+            // Down arrow, to switch to custom.
+            whenFocused = test_util.eventToPromise('focus', input.inputElement);
+            MockInteractions.keyEventOn(
+                allRadio, 'keydown', 40, [], 'ArrowDown');
+            return whenFocused;
+          })
+          .then(() => {
+            // Custom selected
+            validateTabOrder(false);
+
+            // Set a custom page range.
+            input.inputElement.value = '1, 2';
+            input.inputElement.dispatchEvent(
+                new CustomEvent('input', {composed: true, bubbles: true}));
+            return test_util.eventToPromise('input-change', pagesSection);
+          })
+          .then(() => {
+            validateTabOrder(false);
+
+            // Shift + tab should focus the custom radio button, not skip it.
+            const whenCustomRadioFocused =
+                test_util.eventToPromise('focus', customRadio);
+            MockInteractions.keyEventOn(
+                input.inputElement, 'keydown', 9, ['shift'], 'Tab');
+            return whenCustomRadioFocused;
+          })
+          .then(function() {
+            validateTabOrder(false);
+
+            // Clear the input.
+            input.inputElement.value = '';
+            input.inputElement.dispatchEvent(
+                new CustomEvent('input', {composed: true, bubbles: true}));
+            return test_util.eventToPromise('input-change', pagesSection);
+          })
+          .then(function() {
+            validateTabOrder(false);
+
+            // Focusing custom radio button does not reselect all.
+            const whenCustomRadioFocused =
+                test_util.eventToPromise('focus', customRadio);
+            MockInteractions.keyEventOn(
+                input.inputElement, 'keydown', 9, ['shift'], 'Tab');
+            return whenCustomRadioFocused;
+          })
+          .then(function() {
+            validateTabOrder(false);
+
+            // Blurring the radio button reselects all
+            const whenCustomRadioBlurred =
+                test_util.eventToPromise('blur', customRadio);
+            customRadio.blur();
+            Polymer.dom.flush();
+            return whenCustomRadioBlurred;
+          })
+          .then(function() {
+            // Send focus back to the radio group. It should now send focus to
+            // all, since it is selected.
+            const whenAllFocused = test_util.eventToPromise('focus', allRadio);
+            radioGroup.focus();
+            return whenAllFocused;
+          })
+          .then(function() {
+            validateTabOrder(true);
           });
     });
   });
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
index e82fee6..7136481 100644
--- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -132,3 +132,11 @@
 TEST_F('PrintPreviewPagesSettingsTest', 'NupChangesPages', function() {
   this.runMochaTest(pages_settings_test.TestNames.NupChangesPages);
 });
+
+TEST_F('PrintPreviewPagesSettingsTest', 'ClearInput', function() {
+  this.runMochaTest(pages_settings_test.TestNames.ClearInput);
+});
+
+TEST_F('PrintPreviewPagesSettingsTest', 'TabOrder', function() {
+  this.runMochaTest(pages_settings_test.TestNames.TabOrder);
+});
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index e2d6ec1..8d252bd 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1936,31 +1936,6 @@
 });
 
 /**
- * Test fixture for the multidevice settings page container.
- * @constructor
- * @extends {CrSettingsBrowserTest}
- */
-function CrSettingsMultidevicePageContainerTest() {}
-
-CrSettingsMultidevicePageContainerTest.prototype = {
-  __proto__: CrSettingsBrowserTest.prototype,
-
-  /** @override */
-  browsePreload:
-      'chrome://settings/multidevice_page/multidevice_page_container.html',
-
-  /** @override */
-  extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    '../test_browser_proxy.js',
-    'multidevice_page_container_tests.js',
-  ]),
-};
-
-TEST_F('CrSettingsMultidevicePageContainerTest', 'All', function() {
-  mocha.run();
-});
-
-/**
  * Test fixture for the multidevice settings page.
  * @constructor
  * @extends {CrSettingsBrowserTest}
diff --git a/chrome/test/data/webui/settings/multidevice_page_container_tests.js b/chrome/test/data/webui/settings/multidevice_page_container_tests.js
deleted file mode 100644
index 705885f..0000000
--- a/chrome/test/data/webui/settings/multidevice_page_container_tests.js
+++ /dev/null
@@ -1,125 +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.
-
-/**
- * @implements {settings.MultideviceBrowserProxy}
- * Note: showMultiDeviceSetupDialog is not used by the
- * multidevice-page-container element.
- */
-class TestMultideviceBrowserProxy extends TestBrowserProxy {
-  constructor(initialPageContentData) {
-    super([
-      'showMultiDeviceSetupDialog',
-      'getPageContentData',
-    ]);
-    this.data = initialPageContentData;
-  }
-
-  /** @override */
-  getPageContentData() {
-    this.methodCalled('getPageContentData');
-    return Promise.resolve(this.data);
-  }
-}
-
-suite('Multidevice', function() {
-  let multidevicePageContainer = null;
-  let browserProxy = null;
-  let ALL_MODES;
-
-  /**
-   * @param {!settings.MultiDeviceSettingsMode} mode
-   * @param {boolean} isSuiteSupported
-   * @return {!MultiDevicePageContentData}
-   */
-  function getFakePageContentData(mode, isSuiteSupported) {
-    return {
-      mode: mode,
-      hostDeviceName: [
-        settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER,
-        settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION,
-        settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED,
-      ].includes(mode) ?
-          'Pixel XL' :
-          undefined,
-      betterTogetherState: isSuiteSupported ?
-          settings.MultiDeviceFeatureState.ENABLED_BY_USER :
-          settings.MultiDeviceFeatureState.NOT_SUPPORTED_BY_CHROMEBOOK,
-    };
-  }
-
-  /**
-   * @param {!settings.MultiDeviceSettingsMode} newMode
-   * @param {boolean} isSuiteSupported
-   */
-  function changePageContent(newMode, isSuiteSupported) {
-    cr.webUIListenerCallback(
-        'settings.updateMultidevicePageContentData',
-        getFakePageContentData(newMode, isSuiteSupported));
-    Polymer.dom.flush();
-  }
-
-  suiteSetup(function() {
-    ALL_MODES = Object.values(settings.MultiDeviceSettingsMode);
-  });
-
-  /** @return {?HTMLElement} */
-  const getMultidevicePage = () =>
-      multidevicePageContainer.$$('settings-multidevice-page');
-
-  // Initializes page container with provided mode and sets the Better Together
-  // suite's feature state based on provided boolean. It returns a promise that
-  // only resolves when the multidevice-page has changed its pageContentData and
-  // the ensuing callbacks back been flushed.
-  /**
-   * @param {!settings.MultiDeviceSettingsMode} mode
-   * @param {boolean} isSuiteSupported
-   * @return {!Promise}
-   */
-  function setInitialPageContent(mode, isSuiteSupported) {
-    if (multidevicePageContainer)
-      multidevicePageContainer.remove();
-    browserProxy = new TestMultideviceBrowserProxy(
-        getFakePageContentData(mode, isSuiteSupported));
-    settings.MultiDeviceBrowserProxyImpl.instance_ = browserProxy;
-
-    PolymerTest.clearBody();
-    multidevicePageContainer =
-        document.createElement('settings-multidevice-page-container');
-    document.body.appendChild(multidevicePageContainer);
-
-    return browserProxy.whenCalled('getPageContentData')
-        .then(Polymer.dom.flush());
-  }
-
-  test(
-      'WebUIListener toggles multidevice page based suite support in all modes',
-      function() {
-        return setInitialPageContent(
-                   settings.MultiDeviceSettingsMode.NO_HOST_SET,
-                   /* isSuiteSupported */ true)
-            .then(() => {
-              for (const mode of ALL_MODES) {
-                // Check that the settings-page is visible iff the suite is
-                // supported.
-                changePageContent(mode, /* isSuiteSupported */ true);
-                assertTrue(!!getMultidevicePage());
-
-                changePageContent(mode, /* isSuiteSupported */ false);
-                assertFalse(!!getMultidevicePage());
-
-                changePageContent(mode, /* isSuiteSupported */ true);
-                assertTrue(!!getMultidevicePage());
-              }
-            });
-      });
-
-  test(
-      'multidevice-page is not attached if suite is not supported', function() {
-        return setInitialPageContent(
-                   settings.MultiDeviceSettingsMode.NO_HOST_SET,
-                   /* isSuiteSupported */ false)
-            .then(() => assertFalse(!!getMultidevicePage()));
-      });
-});
diff --git a/chrome/test/data/webui/settings/multidevice_page_tests.js b/chrome/test/data/webui/settings/multidevice_page_tests.js
index db762afb..1e03e972 100644
--- a/chrome/test/data/webui/settings/multidevice_page_tests.js
+++ b/chrome/test/data/webui/settings/multidevice_page_tests.js
@@ -2,69 +2,98 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @implements {settings.MultideviceBrowserProxy}
- * Note: Only showMultiDeviceSetupDialog is used by the multidevice-page
- * element.
- */
-class TestMultideviceBrowserProxy extends TestBrowserProxy {
-  constructor() {
-    super([
-      'showMultiDeviceSetupDialog',
-      'getPageContentData',
-      'setFeatureEnabledState',
-    ]);
-  }
-
-  /** @override */
-  showMultiDeviceSetupDialog() {
-    this.methodCalled('showMultiDeviceSetupDialog');
-  }
-
-  /** @override */
-  setFeatureEnabledState(feature, enabled, opt_authToken) {
-    this.methodCalled(
-        'setFeatureEnabledState', [feature, enabled, opt_authToken]);
-  }
-}
-
 suite('Multidevice', function() {
+  /**
+   * Builds fake pageContentData for the specified mode. If it is a mode
+   * corresponding to a set host, it will set the hostDeviceName to the provided
+   * name or else default to HOST_DEVICE.
+   * @param {settings.MultiDeviceSettingsMode} mode
+   * @param {string=} opt_hostDeviceName Overrides default if |mode| corresponds
+   *     to a set host.
+   * @return {!MultiDevicePageContentData}
+   */
+  function createFakePageContentData(mode, opt_hostDeviceName) {
+    let pageContentData = {mode: mode};
+    if ([
+          settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER,
+          settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION,
+          settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED,
+        ].includes(mode)) {
+      pageContentData.hostDeviceName = opt_hostDeviceName || HOST_DEVICE;
+    }
+    return pageContentData;
+  }
+
+  /**
+   * @implements {settings.MultideviceBrowserProxy}
+   * Note: Only showMultiDeviceSetupDialog is used by the multidevice-page
+   * element.
+   */
+  class TestMultideviceBrowserProxy extends TestBrowserProxy {
+    constructor() {
+      super([
+        'showMultiDeviceSetupDialog',
+        'getPageContentData',
+        'setFeatureEnabledState',
+      ]);
+      this.data = createFakePageContentData(
+          settings.MultiDeviceSettingsMode.NO_HOST_SET);
+    }
+
+    /** @override */
+    getPageContentData() {
+      this.methodCalled('getPageContentData');
+      return Promise.resolve(this.data);
+    }
+
+    /** @override */
+    showMultiDeviceSetupDialog() {
+      this.methodCalled('showMultiDeviceSetupDialog');
+    }
+
+    /** @override */
+    setFeatureEnabledState(feature, enabled, opt_authToken) {
+      this.methodCalled(
+          'setFeatureEnabledState', [feature, enabled, opt_authToken]);
+    }
+  }
+
   let multidevicePage = null;
   let browserProxy = null;
   let ALL_MODES;
   const HOST_DEVICE = 'Pixel XL';
 
   /**
+   * Sets pageContentData via WebUI Listener and flushes.
+   * @param {!MultiDevicePageContentData}
+   */
+  function setPageContentData(newPageContentData) {
+    cr.webUIListenerCallback(
+        'settings.updateMultidevicePageContentData', newPageContentData);
+    Polymer.dom.flush();
+  }
+
+  /**
    * Sets pageContentData to the specified mode. If it is a mode corresponding
    * to a set host, it will set the hostDeviceName to the provided name or else
    * default to HOST_DEVICE.
    * @param {settings.MultiDeviceSettingsMode} newMode
-   * @param {string|undefined} newHostDeviceName Overrides default if there
-   *     newMode corresponds to a set host.
+   * @param {string=} opt_newHostDeviceName Overrides default if |newMode|
+   *     corresponds to a set host.
    */
-  function setPageContentData(newMode, newHostDeviceName) {
-    let newPageContentData = {mode: newMode};
-    if ([
-          settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER,
-          settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION,
-          settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED,
-        ].includes(newMode)) {
-      newPageContentData.hostDeviceName = newHostDeviceName || HOST_DEVICE;
-    }
-    multidevicePage.pageContentData = newPageContentData;
-    Polymer.dom.flush();
+  function setHostData(newMode, opt_newHostDeviceName) {
+    setPageContentData(
+        createFakePageContentData(newMode, opt_newHostDeviceName));
   }
 
   function setSuiteState(newState) {
-    multidevicePage.pageContentData = Object.assign(
-        {}, multidevicePage.pageContentData, {betterTogetherState: newState});
-    Polymer.dom.flush();
+    setPageContentData(Object.assign(
+        {}, multidevicePage.pageContentData, {betterTogetherState: newState}));
   }
 
   function setSmartLockState(newState) {
-    multidevicePage.pageContentData = Object.assign(
-        {}, multidevicePage.pageContentData, {smartLockState: newState});
-    Polymer.dom.flush();
+    setPageContentData(Object.assign(
+        {}, multidevicePage.pageContentData, {smartLockState: newState}));
   }
 
   /**
@@ -129,7 +158,7 @@
   const getSubpage = () => multidevicePage.$$('settings-multidevice-subpage');
 
   test('clicking setup shows multidevice setup dialog', function() {
-    setPageContentData(settings.MultiDeviceSettingsMode.NO_HOST_SET);
+    setHostData(settings.MultiDeviceSettingsMode.NO_HOST_SET);
     const button = multidevicePage.$$('paper-button');
     assertTrue(!!button);
     button.click();
@@ -138,23 +167,23 @@
 
   test('headings render based on mode and host', function() {
     for (const mode of ALL_MODES) {
-      setPageContentData(mode);
+      setHostData(mode);
       assertEquals(multidevicePage.isHostSet(), getLabel() === HOST_DEVICE);
     }
   });
 
   test('changing host device changes header', function() {
-    setPageContentData(settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED);
+    setHostData(settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED);
     assertEquals(getLabel(), HOST_DEVICE);
     const anotherHost = 'Super Duper ' + HOST_DEVICE;
-    setPageContentData(
+    setHostData(
         settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, anotherHost);
     assertEquals(getLabel(), anotherHost);
   });
 
   test('item is actionable if and only if a host is set', function() {
     for (const mode of ALL_MODES) {
-      setPageContentData(mode);
+      setHostData(mode);
       assertEquals(
           multidevicePage.isHostSet(),
           !!multidevicePage.$$('#multidevice-item').hasAttribute('actionable'));
@@ -164,7 +193,7 @@
   test(
       'clicking item with verified host opens subpage with features',
       function() {
-        setPageContentData(settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED);
+        setHostData(settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED);
         assertFalse(!!getSubpage());
         multidevicePage.$$('#multidevice-item').click();
         assertTrue(!!getSubpage());
@@ -174,7 +203,7 @@
   test(
       'clicking item with unverified set host opens subpage without features',
       function() {
-        setPageContentData(
+        setHostData(
             settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION,
             HOST_DEVICE);
         assertFalse(!!getSubpage());
@@ -184,7 +213,7 @@
       });
 
   test('policy prohibited suite shows policy indicator', function() {
-    setPageContentData(settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS);
+    setHostData(settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS);
     assertFalse(!!multidevicePage.$$('cr-policy-indicator'));
     // Prohibit suite by policy.
     setSuiteState(settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY);
diff --git a/chrome/tools/convert_dict/aff_reader.cc b/chrome/tools/convert_dict/aff_reader.cc
index 4185a6a..51a13d0 100644
--- a/chrome/tools/convert_dict/aff_reader.cc
+++ b/chrome/tools/convert_dict/aff_reader.cc
@@ -155,7 +155,7 @@
 }
 
 int AffReader::GetAFIndexForAFString(const std::string& af_string) {
-  std::map<std::string, int>::iterator found = affix_groups_.find(af_string);
+  auto found = affix_groups_.find(af_string);
   if (found != affix_groups_.end())
     return found->second;
   std::string my_string(af_string);
@@ -166,8 +166,7 @@
 // line with "AF" for the parser to read later.
 std::vector<std::string> AffReader::GetAffixGroups() const {
   int max_id = 0;
-  for (std::map<std::string, int>::const_iterator i = affix_groups_.begin();
-       i != affix_groups_.end(); ++i) {
+  for (auto i = affix_groups_.begin(); i != affix_groups_.end(); ++i) {
     if (i->second > max_id)
       max_id = i->second;
   }
@@ -175,8 +174,7 @@
   std::vector<std::string> ret;
 
   ret.resize(max_id);
-  for (std::map<std::string, int>::const_iterator i = affix_groups_.begin();
-       i != affix_groups_.end(); ++i) {
+  for (auto i = affix_groups_.begin(); i != affix_groups_.end(); ++i) {
     // Convert the indices into 1-based.
     ret[i->second - 1] = std::string("AF ") + i->first;
   }
diff --git a/chrome/tools/convert_dict/convert_dict_unittest.cc b/chrome/tools/convert_dict/convert_dict_unittest.cc
index 338b048..a12752c 100644
--- a/chrome/tools/convert_dict/convert_dict_unittest.cc
+++ b/chrome/tools/convert_dict/convert_dict_unittest.cc
@@ -70,8 +70,7 @@
   std::string aff_data(base::StringPrintf("SET %s\n", codepage));
 
   std::string dic_data(base::StringPrintf("%" PRIuS "\n", word_list.size()));
-  for (std::map<base::string16, bool>::const_iterator it = word_list.begin();
-       it != word_list.end(); ++it) {
+  for (auto it = word_list.begin(); it != word_list.end(); ++it) {
     std::string encoded_word;
     EXPECT_TRUE(UTF16ToCodepage(it->first,
                                 codepage,
diff --git a/chrome/tools/convert_dict/dic_reader.cc b/chrome/tools/convert_dict/dic_reader.cc
index 07382ac..3eb8ca10 100644
--- a/chrome/tools/convert_dict/dic_reader.cc
+++ b/chrome/tools/convert_dict/dic_reader.cc
@@ -115,7 +115,7 @@
     if (word_tab_offset != std::string::npos)
       utf8word = utf8word.substr(0, word_tab_offset);
 
-    WordSet::iterator found = word_set->find(utf8word);
+    auto found = word_set->find(utf8word);
     std::set<int> affix_vector;
     affix_vector.insert(affix_index);
 
@@ -170,11 +170,9 @@
                     "UTF-8", false);
   }
   // Make sure the words are sorted, they may be unsorted in the input.
-  for (WordSet::iterator word = word_set.begin(); word != word_set.end();
-       ++word) {
+  for (auto word = word_set.begin(); word != word_set.end(); ++word) {
     std::vector<int> affixes;
-    for (std::set<int>::iterator aff = word->second.begin();
-         aff != word->second.end(); ++aff)
+    for (auto aff = word->second.begin(); aff != word->second.end(); ++aff)
       affixes.push_back(*aff);
 
     // Double check that the affixes are sorted. This isn't strictly necessary
diff --git a/chrome/utility/importer/external_process_importer_bridge.cc b/chrome/utility/importer/external_process_importer_bridge.cc
index 2119346..9c9c671 100644
--- a/chrome/utility/importer/external_process_importer_bridge.cc
+++ b/chrome/utility/importer/external_process_importer_bridge.cc
@@ -45,11 +45,9 @@
   // Debug bounds-check which prevents pushing an iterator beyond its end()
   // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|).
   int bookmarks_left = bookmarks.end() - bookmarks.begin();
-  for (std::vector<ImportedBookmarkEntry>::const_iterator it =
-           bookmarks.begin(); it < bookmarks.end();) {
+  for (auto it = bookmarks.begin(); it < bookmarks.end();) {
     std::vector<ImportedBookmarkEntry> bookmark_group;
-    std::vector<ImportedBookmarkEntry>::const_iterator end_group =
-        it + std::min(bookmarks_left, kNumBookmarksToSend);
+    auto end_group = it + std::min(bookmarks_left, kNumBookmarksToSend);
     bookmark_group.assign(it, end_group);
 
     (*observer_)->OnBookmarksImportGroup(bookmark_group);
@@ -71,11 +69,9 @@
   // Debug bounds-check which prevents pushing an iterator beyond its end()
   // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|).
   int favicons_left = favicons.end() - favicons.begin();
-  for (favicon_base::FaviconUsageDataList::const_iterator it = favicons.begin();
-       it < favicons.end();) {
+  for (auto it = favicons.begin(); it < favicons.end();) {
     favicon_base::FaviconUsageDataList favicons_group;
-    favicon_base::FaviconUsageDataList::const_iterator end_group =
-        it + std::min(favicons_left, kNumFaviconsToSend);
+    auto end_group = it + std::min(favicons_left, kNumFaviconsToSend);
     favicons_group.assign(it, end_group);
 
     (*observer_)->OnFaviconsImportGroup(favicons_group);
@@ -94,11 +90,9 @@
   // Debug bounds-check which prevents pushing an iterator beyond its end()
   // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|).
   int rows_left = rows.end() - rows.begin();
-  for (std::vector<ImporterURLRow>::const_iterator it = rows.begin();
-       it < rows.end();) {
+  for (auto it = rows.begin(); it < rows.end();) {
     std::vector<ImporterURLRow> row_group;
-    std::vector<ImporterURLRow>::const_iterator end_group =
-        it + std::min(rows_left, kNumHistoryRowsToSend);
+    auto end_group = it + std::min(rows_left, kNumHistoryRowsToSend);
     row_group.assign(it, end_group);
 
     (*observer_)->OnHistoryImportGroup(row_group, visit_source);
@@ -133,13 +127,10 @@
   // its end() (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 ==
   // s.end()|).
   int autofill_form_data_entries_left = entries.end() - entries.begin();
-  for (std::vector<ImporterAutofillFormDataEntry>::const_iterator it =
-           entries.begin();
-       it < entries.end();) {
+  for (auto it = entries.begin(); it < entries.end();) {
     std::vector<ImporterAutofillFormDataEntry> autofill_form_data_entry_group;
-    std::vector<ImporterAutofillFormDataEntry>::const_iterator end_group =
-        it +
-        std::min(autofill_form_data_entries_left, kNumAutofillFormDataToSend);
+    auto end_group = it + std::min(autofill_form_data_entries_left,
+                                   kNumAutofillFormDataToSend);
     autofill_form_data_entry_group.assign(it, end_group);
 
     (*observer_)->OnAutofillFormDataImportGroup(autofill_form_data_entry_group);
diff --git a/chrome/utility/importer/firefox_importer.cc b/chrome/utility/importer/firefox_importer.cc
index da84b878..f7ed312 100644
--- a/chrome/utility/importer/firefox_importer.cc
+++ b/chrome/utility/importer/firefox_importer.cc
@@ -796,8 +796,7 @@
   if (!s.is_valid())
     return;
 
-  for (FaviconMap::const_iterator i = favicon_map.begin();
-       i != favicon_map.end(); ++i) {
+  for (auto i = favicon_map.begin(); i != favicon_map.end(); ++i) {
     s.BindInt64(0, i->first);
     if (s.Step()) {
       std::vector<unsigned char> data;
diff --git a/chromecast/base/device_capabilities.h b/chromecast/base/device_capabilities.h
index 8ee06e16..2a09a9fe 100644
--- a/chromecast/base/device_capabilities.h
+++ b/chromecast/base/device_capabilities.h
@@ -120,7 +120,7 @@
     }
 
     // Accessor for complete capabilities string in JSON format.
-    const std::string& json_string() const { return *json_string_.get(); }
+    const std::string& json_string() const { return json_string_; }
 
    private:
     friend class base::RefCountedThreadSafe<Data>;
@@ -135,7 +135,7 @@
     ~Data();
 
     const std::unique_ptr<const base::DictionaryValue> dictionary_;
-    const std::unique_ptr<const std::string> json_string_;
+    const std::string json_string_;
 
     DISALLOW_COPY_AND_ASSIGN(Data);
   };
diff --git a/chromecast/base/device_capabilities_impl.cc b/chromecast/base/device_capabilities_impl.cc
index 398d221..936ca2e 100644
--- a/chromecast/base/device_capabilities_impl.cc
+++ b/chromecast/base/device_capabilities_impl.cc
@@ -103,16 +103,13 @@
 
 DeviceCapabilities::Data::Data()
     : dictionary_(new base::DictionaryValue),
-      json_string_(SerializeToJson(*dictionary_)) {
-  DCHECK(json_string_.get());
-}
+      json_string_(*SerializeToJson(*dictionary_)) {}
 
 DeviceCapabilities::Data::Data(
     std::unique_ptr<const base::DictionaryValue> dictionary)
     : dictionary_(std::move(dictionary)),
-      json_string_(SerializeToJson(*dictionary_)) {
+      json_string_(*SerializeToJson(*dictionary_)) {
   DCHECK(dictionary_.get());
-  DCHECK(json_string_.get());
 }
 
 DeviceCapabilitiesImpl::Data::~Data() {}
diff --git a/chromecast/base/device_capabilities_impl_unittest.cc b/chromecast/base/device_capabilities_impl_unittest.cc
index d819975..692f5ed 100644
--- a/chromecast/base/device_capabilities_impl_unittest.cc
+++ b/chromecast/base/device_capabilities_impl_unittest.cc
@@ -187,8 +187,8 @@
                       const base::Value& value) {
   base::DictionaryValue dict_value;
   dict_value.Set(key, value.CreateDeepCopy());
-  std::unique_ptr<const std::string> dict_json(SerializeToJson(dict_value));
-  return dict_json.get() && *dict_json == json;
+  base::Optional<std::string> dict_json = SerializeToJson(dict_value);
+  return dict_json && *dict_json == json;
 }
 
 // The function runs through the set of basic operations of DeviceCapabilities.
@@ -273,9 +273,8 @@
 
 // Tests that class is in correct state after Create().
 TEST_F(DeviceCapabilitiesImplTest, Create) {
-  std::unique_ptr<const std::string> empty_dict_string(
-      SerializeToJson(base::DictionaryValue()));
-  EXPECT_EQ(capabilities()->GetAllData()->json_string(), *empty_dict_string);
+  std::string empty_dict_string = *SerializeToJson(base::DictionaryValue());
+  EXPECT_EQ(capabilities()->GetAllData()->json_string(), empty_dict_string);
   EXPECT_TRUE(capabilities()->GetAllData()->dictionary().empty());
 }
 
@@ -290,9 +289,8 @@
                                       false);
 
   EXPECT_EQ(capabilities()->GetValidator(key), &manager);
-  std::unique_ptr<const std::string> empty_dict_string(
-      SerializeToJson(base::DictionaryValue()));
-  EXPECT_EQ(capabilities()->GetAllData()->json_string(), *empty_dict_string);
+  std::string empty_dict_string = *SerializeToJson(base::DictionaryValue());
+  EXPECT_EQ(capabilities()->GetAllData()->json_string(), empty_dict_string);
   EXPECT_FALSE(capabilities()->GetCapability(key));
 }
 
@@ -309,9 +307,8 @@
   delete manager;
 
   EXPECT_FALSE(capabilities()->GetValidator(key));
-  std::unique_ptr<const std::string> empty_dict_string(
-      SerializeToJson(base::DictionaryValue()));
-  EXPECT_EQ(capabilities()->GetAllData()->json_string(), *empty_dict_string);
+  std::string empty_dict_string = *SerializeToJson(base::DictionaryValue());
+  EXPECT_EQ(capabilities()->GetAllData()->json_string(), empty_dict_string);
   EXPECT_FALSE(capabilities()->GetCapability(key));
 }
 
diff --git a/chromecast/base/serializers.cc b/chromecast/base/serializers.cc
index 7041a961..1c38636 100644
--- a/chromecast/base/serializers.cc
+++ b/chromecast/base/serializers.cc
@@ -23,12 +23,12 @@
   return value;
 }
 
-std::unique_ptr<std::string> SerializeToJson(const base::Value& value) {
-  std::unique_ptr<std::string> json_str(new std::string());
-  JSONStringValueSerializer serializer(json_str.get());
-  if (!serializer.Serialize(value))
-    json_str.reset(nullptr);
-  return json_str;
+base::Optional<std::string> SerializeToJson(const base::Value& value) {
+  std::string json_str;
+  JSONStringValueSerializer serializer(&json_str);
+  if (serializer.Serialize(value))
+    return json_str;
+  return base::nullopt;
 }
 
 std::unique_ptr<base::Value> DeserializeJsonFromFile(
diff --git a/chromecast/base/serializers.h b/chromecast/base/serializers.h
index df648fee..110ccc92 100644
--- a/chromecast/base/serializers.h
+++ b/chromecast/base/serializers.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/optional.h"
 
 namespace base {
 class Value;
@@ -22,8 +23,10 @@
 std::unique_ptr<base::Value> DeserializeFromJson(const std::string& text);
 
 // Helper function which serializes |value| into a JSON string. If a
-// serialization error occurs,the return value will hold the NULL pointer.
-std::unique_ptr<std::string> SerializeToJson(const base::Value& value);
+// serialization error occurs,the return value will be base::nullopt.
+// Dereferencing the result is equivalent to DCHECK()-ing that serialization
+// succeeded and retrieving the serialized string.
+base::Optional<std::string> SerializeToJson(const base::Value& value);
 
 // Helper function which deserializes JSON file at |path| into a base::Value.
 // If file in |path| is empty, is not valid JSON, or if some other
diff --git a/chromecast/base/serializers_unittest.cc b/chromecast/base/serializers_unittest.cc
index 5e3d751..750e237 100644
--- a/chromecast/base/serializers_unittest.cc
+++ b/chromecast/base/serializers_unittest.cc
@@ -56,22 +56,22 @@
 
 TEST(SerializeToJson, BadValue) {
   base::Value value(std::vector<char>(12));
-  std::unique_ptr<std::string> str = SerializeToJson(value);
-  EXPECT_EQ(nullptr, str.get());
+  base::Optional<std::string> str = SerializeToJson(value);
+  EXPECT_EQ(base::nullopt, str);
 }
 
 TEST(SerializeToJson, EmptyValue) {
   base::DictionaryValue value;
-  std::unique_ptr<std::string> str = SerializeToJson(value);
-  ASSERT_NE(nullptr, str.get());
+  base::Optional<std::string> str = SerializeToJson(value);
+  ASSERT_NE(base::nullopt, str);
   EXPECT_EQ(kEmptyJsonString, *str);
 }
 
 TEST(SerializeToJson, PopulatedValue) {
   base::DictionaryValue orig_value;
   orig_value.SetString(kTestKey, kTestValue);
-  std::unique_ptr<std::string> str = SerializeToJson(orig_value);
-  ASSERT_NE(nullptr, str.get());
+  base::Optional<std::string> str = SerializeToJson(orig_value);
+  ASSERT_NE(nullptr, str);
 
   std::unique_ptr<base::Value> new_value = DeserializeFromJson(*str);
   ASSERT_NE(nullptr, new_value.get());
diff --git a/chromecast/crash/linux/crash_testing_utils.cc b/chromecast/crash/linux/crash_testing_utils.cc
index aa7cea8..f81e548 100644
--- a/chromecast/crash/linux/crash_testing_utils.cc
+++ b/chromecast/crash/linux/crash_testing_utils.cc
@@ -62,7 +62,7 @@
   std::string lockfile;
 
   for (const auto& elem : *contents) {
-    std::unique_ptr<std::string> dump_info = SerializeToJson(elem);
+    base::Optional<std::string> dump_info = SerializeToJson(elem);
     RCHECK(dump_info, -1, "Failed to serialize DumpInfo");
     lockfile += *dump_info;
     lockfile += "\n";  // Add line seperatators
diff --git a/chromecast/crash/linux/synchronized_minidump_manager.cc b/chromecast/crash/linux/synchronized_minidump_manager.cc
index 62149fb2..459bc3e 100644
--- a/chromecast/crash/linux/synchronized_minidump_manager.cc
+++ b/chromecast/crash/linux/synchronized_minidump_manager.cc
@@ -302,7 +302,7 @@
   std::string lockfile;
 
   for (const auto& elem : *dumps) {
-    std::unique_ptr<std::string> dump_info = SerializeToJson(elem);
+    base::Optional<std::string> dump_info = SerializeToJson(elem);
     RCHECK(dump_info, false);
     lockfile += *dump_info;
     lockfile += "\n";  // Add line seperatators
diff --git a/chromeos/components/drivefs/fake_drivefs.cc b/chromeos/components/drivefs/fake_drivefs.cc
index 9e73ccb8..df1ffe2 100644
--- a/chromeos/components/drivefs/fake_drivefs.cc
+++ b/chromeos/components/drivefs/fake_drivefs.cc
@@ -89,6 +89,7 @@
   bool hosted = false;
   bool shared = false;
   std::string original_name;
+  mojom::Capabilities capabilities;
 };
 
 class FakeDriveFs::SearchQuery : public mojom::SearchQuery {
@@ -235,11 +236,13 @@
                               const std::string& mime_type,
                               const std::string& original_name,
                               bool pinned,
-                              bool shared) {
+                              bool shared,
+                              const mojom::Capabilities& capabilities) {
   auto& stored_metadata = metadata_[path];
   stored_metadata.mime_type = mime_type;
   stored_metadata.original_name = original_name;
   stored_metadata.hosted = (original_name != path.BaseName().value());
+  stored_metadata.capabilities = capabilities;
   if (pinned) {
     stored_metadata.pinned = true;
   }
@@ -294,7 +297,7 @@
                            ? path.BaseName().value()
                            : stored_metadata.original_name;
   metadata->alternate_url = GURL(base::StrCat({prefix, suffix})).spec();
-  metadata->capabilities = mojom::Capabilities::New();
+  metadata->capabilities = stored_metadata.capabilities.Clone();
 
   std::move(callback).Run(drive::FILE_ERROR_OK, std::move(metadata));
 }
diff --git a/chromeos/components/drivefs/fake_drivefs.h b/chromeos/components/drivefs/fake_drivefs.h
index 853aca9d..403771c0 100644
--- a/chromeos/components/drivefs/fake_drivefs.h
+++ b/chromeos/components/drivefs/fake_drivefs.h
@@ -34,7 +34,8 @@
                    const std::string& mime_type,
                    const std::string& original_name,
                    bool pinned,
-                   bool shared);
+                   bool shared,
+                   const mojom::Capabilities& capabilities);
 
   const base::FilePath& mount_path() { return mount_path_; }
 
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
index 51e2b2f..18e88d3c 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
@@ -156,7 +156,8 @@
   CheckForNewUserPotentialHostExistsEvent(host_status_with_device);
   CheckForExistingUserHostSwitchedEvent(host_status_with_device,
                                         host_device_id_before_update);
-  CheckForExistingUserChromebookAddedEvent(host_device_id_before_update);
+  CheckForExistingUserChromebookAddedEvent(host_status_with_device,
+                                           host_device_id_before_update);
 }
 
 void AccountStatusChangeDelegateNotifierImpl::
@@ -207,6 +208,7 @@
 
 void AccountStatusChangeDelegateNotifierImpl::
     CheckForExistingUserChromebookAddedEvent(
+        const HostStatusProvider::HostStatusWithDevice& host_status_with_device,
         const base::Optional<std::string>& host_device_id_before_update) {
   // The Chromebook added event requires that a set host was found by the
   // update, i.e. there was no host before the host status update but afterward
@@ -214,7 +216,8 @@
   if (!host_device_id_from_most_recent_update_ || host_device_id_before_update)
     return;
 
-  delegate()->OnNewChromebookAddedForExistingUser();
+  delegate()->OnNewChromebookAddedForExistingUser(
+      host_status_with_device.host_device()->name());
   pref_service_->SetInt64(kExistingUserChromebookAddedPrefName,
                           clock_->Now().ToJavaTime());
 }
diff --git a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
index 150d2246..66950f8 100644
--- a/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
+++ b/chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
@@ -86,6 +86,7 @@
       const HostStatusProvider::HostStatusWithDevice& host_status_with_device,
       const base::Optional<std::string>& host_device_id_before_update);
   void CheckForExistingUserChromebookAddedEvent(
+      const HostStatusProvider::HostStatusWithDevice& host_status_with_device,
       const base::Optional<std::string>& host_device_id_before_update);
 
   // Loads data from previous session using PrefService.
diff --git a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
index cb340b5..d06af56 100644
--- a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
+++ b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.cc
@@ -28,7 +28,8 @@
   ++num_existing_user_host_switched_events_handled_;
 }
 
-void FakeAccountStatusChangeDelegate::OnNewChromebookAddedForExistingUser() {
+void FakeAccountStatusChangeDelegate::OnNewChromebookAddedForExistingUser(
+    const std::string& new_host_device_name) {
   ++num_existing_user_chromebook_added_events_handled_;
 }
 
diff --git a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
index 7c6b4a4..8f65e2a5 100644
--- a/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
+++ b/chromeos/services/multidevice_setup/fake_account_status_change_delegate.h
@@ -36,7 +36,8 @@
   void OnPotentialHostExistsForNewUser() override;
   void OnConnectedHostSwitchedForExistingUser(
       const std::string& new_host_device_name) override;
-  void OnNewChromebookAddedForExistingUser() override;
+  void OnNewChromebookAddedForExistingUser(
+      const std::string& new_host_device_name) override;
 
  private:
   size_t num_new_user_events_handled_ = 0u;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
index 5ae1536..6cea743 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -251,7 +251,8 @@
           kTestDeviceNameForDebugNotification);
       break;
     case mojom::EventTypeForDebugging::kExistingUserNewChromebookAdded:
-      delegate->OnNewChromebookAddedForExistingUser();
+      delegate->OnNewChromebookAddedForExistingUser(
+          kTestDeviceNameForDebugNotification);
       break;
     default:
       NOTREACHED();
diff --git a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
index a5f5c517..85209c3 100644
--- a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
+++ b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -94,14 +94,13 @@
   // Callback which indicates that the currently-connected MultiDevice host has
   // changed. This likely means that the user has changed MultiDevice settings
   // on another device. This function is only called if the current user has
-  // already set up MultiDevice features. Note: |new_host_device_name| is
-  // expected to be a UTF-8 string.
+  // already set up MultiDevice features.
   OnConnectedHostSwitchedForExistingUser(string new_host_device_name);
 
   // Callback which indicates that a new Chromebook was added to the account of
   // the current user. This function is only called if the current user has
   // already set up MultiDevice features.
-  OnNewChromebookAddedForExistingUser();
+  OnNewChromebookAddedForExistingUser(string new_host_device_name);
 };
 
 interface HostStatusObserver {
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index bf1c03ee..13b03d91 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -371,8 +371,7 @@
                       webState:(web::WebState*)webState
              completionHandler:(SuggestionsAvailableCompletion)completion {
   web::WebFrame* frame =
-      web::WebFramesManager::FromWebState(webState)->GetFrameWithId(
-          base::SysNSStringToUTF8(frameID));
+      GetWebFrameWithId(webState, base::SysNSStringToUTF8(frameID));
   autofill::AutofillManager* autofillManager =
       [self autofillManagerFromWebState:webState webFrame:frame];
   if (!autofillManager)
@@ -574,6 +573,7 @@
 - (void)processPage:(web::WebState*)webState {
   web::WebFramesManager* framesManager =
       web::WebFramesManager::FromWebState(webState);
+  DCHECK(framesManager);
   if (!autofill::switches::IsAutofillIFrameMessagingEnabled()) {
     [self processFrame:framesManager->GetMainWebFrame() inWebState:webState];
   } else {
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index c44b2ff..5184f25 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -77,8 +77,8 @@
     const auto& presentation = script_proto.presentation();
     script->handle.name = presentation.name();
     script->handle.autostart = presentation.autostart();
-    script->precondition =
-        ScriptPrecondition::FromProto(presentation.precondition());
+    script->precondition = ScriptPrecondition::FromProto(
+        script_proto.path(), presentation.precondition());
     script->priority = presentation.priority();
 
     if (script->handle.name.empty() || script->handle.path.empty() ||
diff --git a/components/autofill_assistant/browser/script_precondition.cc b/components/autofill_assistant/browser/script_precondition.cc
index b843b15e..c81793d51 100644
--- a/components/autofill_assistant/browser/script_precondition.cc
+++ b/components/autofill_assistant/browser/script_precondition.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/strcat.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "third_party/re2/src/re2/re2.h"
@@ -15,6 +16,7 @@
 namespace autofill_assistant {
 // Static
 std::unique_ptr<ScriptPrecondition> ScriptPrecondition::FromProto(
+    const std::string& script_path,
     const ScriptPreconditionProto& script_precondition_proto) {
   std::vector<std::vector<std::string>> elements_exist;
   for (const auto& element : script_precondition_proto.elements_exist()) {
@@ -22,6 +24,12 @@
     for (const auto& selector : element.selectors()) {
       selectors.emplace_back(selector);
     }
+    if (selectors.empty()) {
+      DLOG(WARNING)
+          << "Empty selectors in script precondition for script path: "
+          << script_path << ".";
+      continue;
+    }
     elements_exist.emplace_back(selectors);
   }
 
@@ -34,7 +42,8 @@
   for (const auto& pattern : script_precondition_proto.path_pattern()) {
     auto re = std::make_unique<re2::RE2>(pattern);
     if (re->error_code() != re2::RE2::NoError) {
-      LOG(ERROR) << "Invalid regexp in script precondition '" << pattern;
+      DLOG(ERROR) << "Invalid regexp in script precondition '" << pattern
+                  << "' for script path: " << script_path << ".";
       return nullptr;
     }
     path_pattern.emplace_back(std::move(re));
@@ -140,7 +149,10 @@
   if (domain_match_.empty())
     return true;
 
-  return domain_match_.find(url.host()) != domain_match_.end();
+  // We require the scheme and host parts to match.
+  // TODO(crbug.com/806868): Consider using Origin::IsSameOriginWith here.
+  std::string scheme_domain = base::StrCat({url.scheme(), "://", url.host()});
+  return domain_match_.find(scheme_domain) != domain_match_.end();
 }
 
 bool ScriptPrecondition::MatchPath(const GURL& url) const {
diff --git a/components/autofill_assistant/browser/script_precondition.h b/components/autofill_assistant/browser/script_precondition.h
index 5455c49..11a8247b 100644
--- a/components/autofill_assistant/browser/script_precondition.h
+++ b/components/autofill_assistant/browser/script_precondition.h
@@ -28,7 +28,11 @@
  public:
   // Builds a precondition from its proto representation. Returns nullptr if the
   // preconditions are invalid.
+  //
+  // Note: The |script_path| paramter is used to allow logging misconfigured
+  // scripts.
   static std::unique_ptr<ScriptPrecondition> FromProto(
+      const std::string& script_path,
       const ScriptPreconditionProto& script_precondition_proto);
 
   ScriptPrecondition(
diff --git a/components/autofill_assistant/browser/script_precondition_unittest.cc b/components/autofill_assistant/browser/script_precondition_unittest.cc
index 0d498dd5..44c37d6 100644
--- a/components/autofill_assistant/browser/script_precondition_unittest.cc
+++ b/components/autofill_assistant/browser/script_precondition_unittest.cc
@@ -77,7 +77,7 @@
 
   // Runs the preconditions and returns the result.
   bool Check(const ScriptPreconditionProto& proto) {
-    auto precondition = ScriptPrecondition::FromProto(proto);
+    auto precondition = ScriptPrecondition::FromProto("unused", proto);
     if (!precondition)
       return false;
 
@@ -99,17 +99,36 @@
 
 TEST_F(ScriptPreconditionTest, DomainMatch) {
   ScriptPreconditionProto proto;
-  proto.add_domain("match.example.com");
-  proto.add_domain("alsomatch.example.com");
+  proto.add_domain("http://match.example.com");
+  proto.add_domain("http://alsomatch.example.com");
 
   SetUrl("http://match.example.com/path");
   EXPECT_TRUE(Check(proto));
 
+  // Scheme must match.
+  SetUrl("https://match.example.com/path");
+  EXPECT_FALSE(Check(proto)) << "Scheme must match.";
+
+  // Port is ignored.
+  SetUrl("http://match.example.com:8080");
+  EXPECT_TRUE(Check(proto)) << "Port should be ignored";
+
   SetUrl("http://nomatch.example.com/path");
-  EXPECT_FALSE(Check(proto));
+  EXPECT_FALSE(Check(proto)) << "nomatch";
 
   SetUrl("http://alsomatch.example.com/path");
-  EXPECT_TRUE(Check(proto));
+  EXPECT_TRUE(Check(proto)) << "Path should be ignored";
+
+  SetUrl("http://alsomatch.example.com/path?a=b");
+  EXPECT_TRUE(Check(proto)) << "Query should be ignored.";
+}
+
+TEST_F(ScriptPreconditionTest, TrailingSlash) {
+  ScriptPreconditionProto proto;
+  proto.add_domain("http://example.com/");
+
+  SetUrl("http://example.com/path");
+  EXPECT_FALSE(Check(proto));
 }
 
 TEST_F(ScriptPreconditionTest, PathFullMatch) {
@@ -142,7 +161,18 @@
   ScriptPreconditionProto proto;
   proto.add_path_pattern("invalid[");
 
-  EXPECT_EQ(nullptr, ScriptPrecondition::FromProto(proto));
+  EXPECT_EQ(nullptr, ScriptPrecondition::FromProto("unused", proto));
+}
+
+TEST_F(ScriptPreconditionTest, IgnoreEmptyElementsExist) {
+  EXPECT_CALL(mock_web_controller_, OnElementExists(ElementsAre("exists"), _))
+      .WillOnce(RunOnceCallback<1>(true));
+
+  ScriptPreconditionProto proto;
+  proto.add_elements_exist()->add_selectors("exists");
+  proto.add_elements_exist();
+
+  EXPECT_TRUE(Check(proto));
 }
 
 TEST_F(ScriptPreconditionTest, WrongScriptStatusEqualComparator) {
@@ -237,20 +267,18 @@
 
 TEST_F(ScriptPreconditionTest, MultipleConditions) {
   ScriptPreconditionProto proto;
-  proto.add_domain("match.example.com");
+  proto.add_domain("http://match.example.com");
   proto.add_path_pattern("/path");
   proto.add_elements_exist()->add_selectors("exists");
 
   // Domain and path don't match.
   EXPECT_FALSE(Check(proto));
 
-  // Domain, path and selector match.
   SetUrl("http://match.example.com/path");
-  EXPECT_TRUE(Check(proto));
+  EXPECT_TRUE(Check(proto)) << "Domain, path and selector must match.";
 
-  // Selector doesn't match.
   proto.mutable_elements_exist(0)->set_selectors(0, "does_not_exist");
-  EXPECT_FALSE(Check(proto));
+  EXPECT_FALSE(Check(proto)) << "Element can not match.";
 }
 
 TEST_F(ScriptPreconditionTest, FormValueMatch) {
diff --git a/components/discardable_memory/service/discardable_shared_memory_manager.cc b/components/discardable_memory/service/discardable_shared_memory_manager.cc
index 748e0a0..99fcdfe 100644
--- a/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -14,7 +14,6 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/discardable_memory.h"
-#include "base/memory/memory_coordinator_client_registry.h"
 #include "base/memory/shared_memory_tracker.h"
 #include "base/numerics/safe_math.h"
 #include "base/process/memory.h"
@@ -235,11 +234,9 @@
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "DiscardableSharedMemoryManager",
       base::ThreadTaskRunnerHandle::Get());
-  base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
 }
 
 DiscardableSharedMemoryManager::~DiscardableSharedMemoryManager() {
-  base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this);
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
 
@@ -405,32 +402,6 @@
   return bytes_allocated_;
 }
 
-void DiscardableSharedMemoryManager::OnMemoryStateChange(
-    base::MemoryState state) {
-  // Don't use SetMemoryLimit() as it frees up existing allocations.
-  // OnPurgeMemory() is called to actually free up memory.
-  base::AutoLock lock(lock_);
-  switch (state) {
-    case base::MemoryState::NORMAL:
-      memory_limit_ = default_memory_limit_;
-      break;
-    case base::MemoryState::THROTTLED:
-      memory_limit_ = 0;
-      break;
-    case base::MemoryState::SUSPENDED:
-    // Note that SUSPENDED never occurs in the main browser process so far.
-    // Fall through.
-    case base::MemoryState::UNKNOWN:
-      NOTREACHED();
-      break;
-  }
-}
-
-void DiscardableSharedMemoryManager::OnPurgeMemory() {
-  base::AutoLock lock(lock_);
-  ReduceMemoryUsageUntilWithinLimit(0);
-}
-
 void DiscardableSharedMemoryManager::WillDestroyCurrentMessageLoop() {
   // The mojo thead is going to be destroyed. We should invalidate all related
   // weak ptrs and remove the destrunction observer.
diff --git a/components/discardable_memory/service/discardable_shared_memory_manager.h b/components/discardable_memory/service/discardable_shared_memory_manager.h
index 354cf393..6005fd1 100644
--- a/components/discardable_memory/service/discardable_shared_memory_manager.h
+++ b/components/discardable_memory/service/discardable_shared_memory_manager.h
@@ -17,7 +17,6 @@
 #include "base/macros.h"
 #include "base/memory/discardable_memory_allocator.h"
 #include "base/memory/discardable_shared_memory.h"
-#include "base/memory/memory_coordinator_client.h"
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/unsafe_shared_memory_region.h"
@@ -49,7 +48,6 @@
 class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
     : public base::DiscardableMemoryAllocator,
       public base::trace_event::MemoryDumpProvider,
-      public base::MemoryCoordinatorClient,
       public base::MessageLoopCurrent::DestructionObserver {
  public:
   DiscardableSharedMemoryManager();
@@ -117,10 +115,6 @@
     return a->memory()->last_known_usage() > b->memory()->last_known_usage();
   }
 
-  // base::MemoryCoordinatorClient implementation:
-  void OnMemoryStateChange(base::MemoryState state) override;
-  void OnPurgeMemory() override;
-
   // base::MessageLoopCurrent::DestructionObserver implementation:
   void WillDestroyCurrentMessageLoop() override;
 
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc
index 767ca15..ccf9748 100644
--- a/components/domain_reliability/quic_error_mapping.cc
+++ b/components/domain_reliability/quic_error_mapping.cc
@@ -291,6 +291,7 @@
     {quic::QUIC_INVALID_PATH_RESPONSE_DATA, "quic.invalid.path_response_data"},
     {quic::QUIC_INVALID_MESSAGE_DATA, "quic.invalid.message_data"},
     {quic::IETF_QUIC_PROTOCOL_VIOLATION, "quic.ietf.protocol_violation"},
+    {quic::QUIC_INVALID_NEW_TOKEN, "quic.invalid_new_token"},
 
     // No error. Used as bound while iterating.
     {quic::QUIC_LAST_ERROR, "quic.last_error"}};
diff --git a/components/feed/core/feed_journal_mutation_unittest.cc b/components/feed/core/feed_journal_mutation_unittest.cc
index f6c8346..b220a5d6 100644
--- a/components/feed/core/feed_journal_mutation_unittest.cc
+++ b/components/feed/core/feed_journal_mutation_unittest.cc
@@ -13,7 +13,11 @@
 
 const char kJournalName1[] = "Journal1";
 const char kJournalName2[] = "Journal2";
-const char kJournalValue[] = "value";
+const uint8_t kJournalValue[] = {
+    8,   1,   18,  40,  70,  69,  65,   84,  85,  82,  69, 58, 58,
+    115, 116, 111, 114, 105, 101, 115,  46,  102, 58,  58, 45, 56,
+    57,  48,  56,  51,  51,  54,  49,   56,  52,  53,  48, 48, 54,
+    55,  56,  48,  51,  57,  24,  -119, -10, -71, -35, 5};
 
 }  // namespace
 
@@ -30,13 +34,19 @@
   EXPECT_EQ(mutation.journal_name(), kJournalName1);
   EXPECT_TRUE(mutation.Empty());
 
-  mutation.AddAppendOperation(kJournalValue);
+  std::vector<uint8_t> bytes_vector(
+      kJournalValue,
+      kJournalValue + sizeof(kJournalValue) / sizeof(kJournalValue[0]));
+
+  mutation.AddAppendOperation(
+      std::string(bytes_vector.begin(), bytes_vector.end()));
   EXPECT_FALSE(mutation.Empty());
 
   JournalOperation operation = mutation.TakeFristOperation();
   EXPECT_TRUE(mutation.Empty());
   EXPECT_EQ(operation.type(), JournalOperation::JOURNAL_APPEND);
-  EXPECT_EQ(operation.value(), kJournalValue);
+  EXPECT_EQ(operation.value(),
+            std::string(bytes_vector.begin(), bytes_vector.end()));
 }
 
 TEST_F(FeedJournalMutationTest, AddCopyOperation) {
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 3100501..51f3fb6 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -155,6 +155,7 @@
     BLOATED_RENDERER_INFOBAR_DELEGATE = 84,
     SUPERVISED_USERS_DEPRECATED_INFOBAR_DELEGATE = 85,
     NEAR_OOM_REDUCTION_INFOBAR_ANDROID = 86,
+    LITE_PAGE_PREVIEWS_INFOBAR = 87,
   };
 
   // Describes navigation events, used to decide whether infobars should be
diff --git a/components/neterror/resources/neterror.html b/components/neterror/resources/neterror.html
index 96f398c..be2f4bcd 100644
--- a/components/neterror/resources/neterror.html
+++ b/components/neterror/resources/neterror.html
@@ -63,16 +63,14 @@
           </div>
         </div>
         <div id="offline-content-list" hidden>
-          <webview>
-            <div class="offline-content-list-title" jsselect="offlineContentList"
-                jscontent="title"></div>
-            <div id="offline-content-suggestions"></div>
-            <div class="offline-content-list-action">
-              <a class="link-button" onclick="launchDownloadsPage()" 
-                  jsselect="offlineContentList" jscontent="actionText">
-              </a>
-            </div>
-          </webview>
+          <div class="offline-content-list-title" jsselect="offlineContentList"
+              jscontent="title"></div>
+          <div id="offline-content-suggestions"></div>
+          <div class="offline-content-list-action">
+            <a class="link-button" onclick="launchDownloadsPage()"
+                jsselect="offlineContentList" jscontent="actionText">
+            </a>
+          </div>
         </div>
         <div id="offline-content-summary" onclick="launchDownloadsPage()" hidden>
           <div class="offline-content-summary-images">
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
index 7739592..c938519a 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -5,7 +5,7 @@
 #include "components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h"
 
 #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
-#include "ui/gl/gl_bindings.h"
+#include "third_party/khronos/GLES2/gl2.h"
 
 namespace viz {
 
diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd
index eacdf8f..8017be1 100644
--- a/content/app/strings/content_strings.grd
+++ b/content/app/strings/content_strings.grd
@@ -704,22 +704,6 @@
         video
       </message>
 
-      <message name="IDS_AX_MEDIA_MUTE_BUTTON_HELP" desc="Accessibility help description for mute button">
-        mute audio track
-      </message>
-
-      <message name="IDS_AX_MEDIA_UNMUTE_BUTTON_HELP" desc="Accessibility help description for turn mute off button">
-        unmute audio track
-      </message>
-
-      <message name="IDS_AX_MEDIA_PLAY_BUTTON_HELP" desc="Accessibility help description for play button">
-        begin playback
-      </message>
-
-      <message name="IDS_AX_MEDIA_PAUSE_BUTTON_HELP" desc="Accessibility help description for pause button">
-        pause playback
-      </message>
-
       <message name="IDS_AX_MEDIA_AUDIO_SLIDER_HELP" desc="Accessibility help description for audio timeline slider">
         audio time scrubber
       </message>
@@ -740,42 +724,6 @@
         number of seconds of movie remaining
       </message>
 
-      <message name="IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON_HELP" desc="Accessibility help description for enter fullscreen button">
-        play movie in full screen mode
-      </message>
-
-      <message name="IDS_AX_MEDIA_EXIT_FULL_SCREEN_BUTTON_HELP" desc="Accessibility help description for exit fullscreen button">
-        exit full screen
-      </message>
-
-      <message name="IDS_AX_MEDIA_DISPLAY_CUT_OUT_FULL_SCREEN_BUTTON_HELP" desc="Accessibility help description for display cutout fullscreen button">
-        toggle fullscreen into display cutout area
-      </message>
-
-      <message name="IDS_AX_MEDIA_ENTER_PICTURE_IN_PICTURE_BUTTON_HELP" desc="Accessibility help description for enter Picture-in-Picture button">
-        play video in picture-in-picture mode
-      </message>
-
-      <message name="IDS_AX_MEDIA_EXIT_PICTURE_IN_PICTURE_BUTTON_HELP" desc="Accessibility help description for exit Picture-in-Picture button">
-        exit picture-in-picture
-      </message>
-
-      <message name="IDS_AX_MEDIA_SHOW_CLOSED_CAPTIONS_BUTTON_HELP" desc="Accessibility help description for show closed captions button">
-        start displaying closed captions
-      </message>
-
-      <message name="IDS_AX_MEDIA_HIDE_CLOSED_CAPTIONS_BUTTON_HELP" desc="Accessibility help description for hide closed captions button">
-        stop displaying closed captions
-      </message>
-
-      <message name="IDS_AX_MEDIA_CAST_OFF_BUTTON_HELP" desc="Accessibility help description for remote playback button">
-        play on remote device
-      </message>
-
-      <message name="IDS_AX_MEDIA_CAST_ON_BUTTON_HELP" desc="Accessibility help description for remote playback control button">
-        control remote playback
-      </message>
-
       <message name="IDS_AX_MEDIA_OVERFLOW_BUTTON_HELP" desc="Accessibility help description for overflow button.">
         more options
       </message>
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index 459c79a..53eb77c 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -600,6 +600,8 @@
     return false;
   }
   DCHECK(was_called);
+  if (IsMainResourceType(resource_type_))
+    should_create_subresource_loader_ = true;
   return true;
 }
 
diff --git a/content/browser/devtools/devtools_video_consumer.cc b/content/browser/devtools/devtools_video_consumer.cc
index f06866c3..1dd814c 100644
--- a/content/browser/devtools/devtools_video_consumer.cc
+++ b/content/browser/devtools/devtools_video_consumer.cc
@@ -57,7 +57,7 @@
   skbitmap.allocN32Pixels(frame->visible_rect().width(),
                           frame->visible_rect().height());
   cc::SkiaPaintCanvas canvas(skbitmap);
-  renderer.Copy(frame, &canvas, media::Context3D());
+  renderer.Copy(frame, &canvas, media::Context3D(), nullptr);
   return skbitmap;
 }
 
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 004cbb10..2ed248d 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -556,16 +556,6 @@
 
   // Actually log the UMA.
   UMA_HISTOGRAM_ENUMERATION("Stability.ChildFrameCrash.Visibility", visibility);
-
-  if (visibility == CrashVisibility::kShownAfterCrashing) {
-    auto* rfh = frame_proxy_in_parent_renderer_->frame_tree_node()
-                    ->current_frame_host();
-    if (rfh->GetParent() && rfh->is_local_root()) {
-      UMA_HISTOGRAM_BOOLEAN(
-          "RenderFrameHostImpl.ReceivedPostMessageFromNonDescendant",
-          rfh->received_post_message_from_non_descendant());
-    }
-  }
 }
 
 bool CrossProcessFrameConnector::IsVisible() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 1dc020f..2af671e 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -766,14 +766,6 @@
   // for unload handler processing.
   void SetSubframeUnloadTimeoutForTesting(const base::TimeDelta& timeout);
 
-  bool received_post_message_from_non_descendant() const {
-    return received_post_message_from_non_descendant_;
-  }
-
-  void did_receive_post_message_from_non_descendant() {
-    received_post_message_from_non_descendant_ = true;
-  }
-
   // Returns the list of NavigationEntry ids corresponding to NavigationRequests
   // waiting to commit in this RenderFrameHost.
   std::set<int> GetNavigationEntryIdsPendingCommit();
@@ -1437,12 +1429,6 @@
   // relevant NavigationEntry.
   int nav_entry_id_;
 
-  // Tracks if a frame has been influenced by post message from
-  // non-descendant frames. Useful for determining if silently reloading a
-  // crashed frame is safe. Post messages from descendants to not matter for
-  // this decision since they will be reloaded as well.
-  bool received_post_message_from_non_descendant_ = false;
-
   // Used to swap out or shut down this RFH when the unload event is taking too
   // long to execute, depending on the number of active frames in the
   // SiteInstance.  May be null in tests.
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index bad4c9f..c357287 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -381,11 +381,6 @@
             ->SynchronizeVisualPropertiesIgnoringPendingAck();
       }
 
-      if (!source_rfh->frame_tree_node()->IsDescendantOf(
-              target_rfh->frame_tree_node())) {
-        target_rfh->did_receive_post_message_from_non_descendant();
-      }
-
       // Ensure that we have a swapped-out RVH and proxy for the source frame
       // in the target SiteInstance. If it doesn't exist, create it on demand
       // and also create its opener chain, since that will also be accessible
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 1df1149..3c2cf89 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -23,14 +23,15 @@
 #include "content/common/child_process_host_impl.h"
 #include "content/common/content_switches_internal.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/pepper_plugin_info.h"
 #include "content/public/common/process_type.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
 #include "content/public/common/service_names.mojom.h"
-#include "net/base/network_change_notifier.h"
 #include "ppapi/proxy/ppapi_messages.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
 #include "services/service_manager/sandbox/switches.h"
 #include "services/service_manager/zygote/common/zygote_buildflags.h"
@@ -129,25 +130,38 @@
 };
 
 class PpapiPluginProcessHost::PluginNetworkObserver
-    : public net::NetworkChangeNotifier::NetworkChangeObserver {
+    : public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   explicit PluginNetworkObserver(PpapiPluginProcessHost* process_host)
-      : process_host_(process_host) {
-    net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+      : process_host_(process_host),
+        network_connection_tracker_(nullptr),
+        weak_factory_(this) {
+    GetNetworkConnectionTrackerFromUIThread(
+        base::BindOnce(&PluginNetworkObserver::SetNetworkConnectionTracker,
+                       weak_factory_.GetWeakPtr()));
+  }
+
+  void SetNetworkConnectionTracker(
+      network::NetworkConnectionTracker* network_connection_tracker) {
+    DCHECK(network_connection_tracker);
+    network_connection_tracker_ = network_connection_tracker;
+    network_connection_tracker_->AddNetworkConnectionObserver(this);
   }
 
   ~PluginNetworkObserver() override {
-    net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+    if (network_connection_tracker_)
+      network_connection_tracker_->RemoveNetworkConnectionObserver(this);
   }
 
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override {
+  void OnConnectionChanged(network::mojom::ConnectionType type) override {
     process_host_->Send(new PpapiMsg_SetNetworkState(
-        type != net::NetworkChangeNotifier::CONNECTION_NONE));
+        type != network::mojom::ConnectionType::CONNECTION_NONE));
   }
 
  private:
   PpapiPluginProcessHost* const process_host_;
+  network::NetworkConnectionTracker* network_connection_tracker_;
+  base::WeakPtrFactory<PluginNetworkObserver> weak_factory_;
 };
 
 PpapiPluginProcessHost::~PpapiPluginProcessHost() {
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index d9faa8a4..c120832 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -80,9 +80,11 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_surface_tracker.h"
 #include "gpu/vulkan/buildflags.h"
+#if BUILDFLAG(ENABLE_VULKAN)
 #include "gpu/vulkan/init/vulkan_factory.h"
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "gpu/vulkan/vulkan_surface.h"
+#endif
 #include "services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 #include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index 815bdf0..c55163a 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -60,7 +60,6 @@
   }
   ~TracingUmaTracker() = default;
   TracingUmaTracker(TracingUmaTracker&& tracker) = default;
-  TracingUmaTracker& operator=(TracingUmaTracker&& tracker) = default;
 
   void Stop() {
     TRACE_EVENT_ASYNC_END0(
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 8e80b67a..7159d70 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -1218,11 +1218,10 @@
                      AsWeakPtr(), std::move(controller_request)));
 }
 
-void ServiceWorkerProviderHost::CloneForWorker(
+void ServiceWorkerProviderHost::CloneContainerHost(
     mojom::ServiceWorkerContainerHostRequest container_host_request) {
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
-  bindings_for_worker_threads_.AddBinding(this,
-                                          std::move(container_host_request));
+  additional_bindings_.AddBinding(this, std::move(container_host_request));
 }
 
 void ServiceWorkerProviderHost::Ping(PingCallback callback) {
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 9a950be..801a78dc 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -541,7 +541,7 @@
   void EnsureControllerServiceWorker(
       mojom::ControllerServiceWorkerRequest controller_request,
       mojom::ControllerServiceWorkerPurpose purpose) override;
-  void CloneForWorker(
+  void CloneContainerHost(
       mojom::ServiceWorkerContainerHostRequest container_host_request) override;
   void Ping(PingCallback callback) override;
   void HintToUpdateServiceWorker() override;
@@ -673,17 +673,10 @@
   // content::ServiceWorkerProviderHost will be destroyed.
   mojo::AssociatedBinding<mojom::ServiceWorkerContainerHost> binding_;
 
-  // Mojo bindings for provider host pointers which are used from (dedicated or
-  // shared) worker threads.
-  // When this is hosting a shared worker, |bindings_for_worker_threads_|
-  // contains exactly one element for the shared worker thread. This binding is
-  // needed because the host pointer which is bound to |binding_| can only be
-  // used from the main thread.
-  // When this is hosting a document, |bindings_for_worker_threads_| contains
-  // all dedicated workers associated with the document. This binding is needed
-  // for the host pointers which are used from the dedicated worker threads.
-  mojo::BindingSet<mojom::ServiceWorkerContainerHost>
-      bindings_for_worker_threads_;
+  // Container host bindings other than the original |binding_|. These include
+  // bindings for container host pointers used from (dedicated or shared) worker
+  // threads, or from ServiceWorkerSubresourceLoaderFactory.
+  mojo::BindingSet<mojom::ServiceWorkerContainerHost> additional_bindings_;
 
   // For service worker execution contexts.
   mojo::Binding<service_manager::mojom::InterfaceProvider>
diff --git a/content/browser/shared_worker/shared_worker_script_fetcher.cc b/content/browser/shared_worker/shared_worker_script_fetcher.cc
index b3d3180bf..6c50fba1 100644
--- a/content/browser/shared_worker/shared_worker_script_fetcher.cc
+++ b/content/browser/shared_worker/shared_worker_script_fetcher.cc
@@ -7,7 +7,6 @@
 #include "base/feature_list.h"
 #include "content/browser/shared_worker/shared_worker_script_loader.h"
 #include "content/browser/shared_worker/shared_worker_script_loader_factory.h"
-#include "content/common/navigation_subresource_loader_params.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/url_loader_throttle.h"
@@ -71,7 +70,8 @@
     CreateAndStartCallback callback)
     : script_loader_factory_(std::move(script_loader_factory)),
       resource_request_(std::move(resource_request)),
-      callback_(std::move(callback)) {
+      callback_(std::move(callback)),
+      response_url_loader_binding_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
@@ -104,14 +104,49 @@
     const network::ResourceResponseHead& head) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // TODO(nhiroki): Support AppCache's fallback case. See
-  // NavigationLoaderInterceptor::MaybeCreateLoaderForResponse() for
-  // reference (https://crbug.com/715632).
+  base::WeakPtr<SharedWorkerScriptLoader> script_loader =
+      script_loader_factory_->GetScriptLoader();
+  if (script_loader && script_loader->default_loader_used_) {
+    // If the default network loader was used to handle the URL load request we
+    // need to see if the request interceptors want to potentially create a new
+    // loader for the response, e.g. AppCache's fallback.
+    DCHECK(!response_url_loader_);
+    network::mojom::URLLoaderClientRequest response_client_request;
+    if (script_loader->MaybeCreateLoaderForResponse(head, &response_url_loader_,
+                                                    &response_client_request,
+                                                    url_loader_.get())) {
+      DCHECK(response_url_loader_);
+      response_url_loader_binding_.Bind(std::move(response_client_request));
+      subresource_loader_params_ = script_loader->TakeSubresourceLoaderParams();
+      url_loader_.reset();
+      // OnReceiveResponse() will be called again.
+      return;
+    }
+  }
 
   blink::mojom::SharedWorkerMainScriptLoadParamsPtr main_script_load_params =
       blink::mojom::SharedWorkerMainScriptLoadParams::New();
+
+  // Fill in params for loading shared worker's main script and subresources.
   main_script_load_params->response_head = head;
-  main_script_load_params->url_loader_client_endpoints = url_loader_->Unbind();
+  if (url_loader_) {
+    // The main script was served by a request interceptor or the default
+    // network loader.
+    DCHECK(!response_url_loader_);
+    main_script_load_params->url_loader_client_endpoints =
+        url_loader_->Unbind();
+    subresource_loader_params_ = script_loader->TakeSubresourceLoaderParams();
+  } else {
+    // The main script was served by the default network loader first, and then
+    // a request interceptor created another loader |response_url_loader_| for
+    // serving an alternative response.
+    DCHECK(response_url_loader_);
+    DCHECK(response_url_loader_binding_.is_bound());
+    main_script_load_params->url_loader_client_endpoints =
+        network::mojom::URLLoaderClientEndpoints::New(
+            response_url_loader_.PassInterface(),
+            response_url_loader_binding_.Unbind());
+  }
 
   for (size_t i = 0; i < redirect_infos_.size(); ++i) {
     main_script_load_params->redirect_infos.emplace_back(redirect_infos_[i]);
@@ -119,11 +154,8 @@
         redirect_response_heads_[i]);
   }
 
-  base::Optional<SubresourceLoaderParams> subresource_loader_params =
-      script_loader_factory_->TakeSubresourceLoaderParams();
-
   std::move(callback_).Run(std::move(main_script_load_params),
-                           std::move(subresource_loader_params),
+                           std::move(subresource_loader_params_),
                            true /* success */);
   delete this;
 }
diff --git a/content/browser/shared_worker/shared_worker_script_fetcher.h b/content/browser/shared_worker/shared_worker_script_fetcher.h
index af7e55a..4117d0d 100644
--- a/content/browser/shared_worker/shared_worker_script_fetcher.h
+++ b/content/browser/shared_worker/shared_worker_script_fetcher.h
@@ -7,6 +7,8 @@
 
 #include "base/callback.h"
 #include "base/optional.h"
+#include "content/common/navigation_subresource_loader_params.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
@@ -22,7 +24,6 @@
 class SharedWorkerScriptLoaderFactory;
 class ThrottlingURLLoader;
 class URLLoaderThrottle;
-struct SubresourceLoaderParams;
 
 // NetworkService (PlzWorker):
 // This is an implementation of the URLLoaderClient for shared worker's main
@@ -71,10 +72,22 @@
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
   std::unique_ptr<SharedWorkerScriptLoaderFactory> script_loader_factory_;
+
   std::unique_ptr<network::ResourceRequest> resource_request_;
   CreateAndStartCallback callback_;
+
+  // URLLoader instance backed by a request interceptor (e.g.,
+  // AppCacheRequestHandler) or the network service.
   std::unique_ptr<ThrottlingURLLoader> url_loader_;
 
+  // URLLoader instance for handling a response received from the default
+  // network loader. This can be provided by an interceptor. For example,
+  // AppCache's interceptor creates this for AppCache's fallback case.
+  network::mojom::URLLoaderPtr response_url_loader_;
+  mojo::Binding<network::mojom::URLLoaderClient> response_url_loader_binding_;
+
+  base::Optional<SubresourceLoaderParams> subresource_loader_params_;
+
   std::vector<net::RedirectInfo> redirect_infos_;
   std::vector<network::ResourceResponseHead> redirect_response_heads_;
 };
diff --git a/content/browser/shared_worker/shared_worker_script_loader.cc b/content/browser/shared_worker/shared_worker_script_loader.cc
index 4854a89..11c292b 100644
--- a/content/browser/shared_worker/shared_worker_script_loader.cc
+++ b/content/browser/shared_worker/shared_worker_script_loader.cc
@@ -117,6 +117,7 @@
 
 void SharedWorkerScriptLoader::LoadFromNetwork(
     bool reset_subresource_loader_params) {
+  default_loader_used_ = true;
   network::mojom::URLLoaderClientPtr client;
   if (url_loader_client_binding_)
     url_loader_client_binding_.Unbind();
@@ -245,4 +246,22 @@
   client_->OnComplete(status);
 }
 
+bool SharedWorkerScriptLoader::MaybeCreateLoaderForResponse(
+    const network::ResourceResponseHead& response,
+    network::mojom::URLLoaderPtr* response_url_loader,
+    network::mojom::URLLoaderClientRequest* response_client_request,
+    ThrottlingURLLoader* url_loader) {
+  DCHECK(default_loader_used_);
+  for (auto& interceptor : interceptors_) {
+    if (interceptor->MaybeCreateLoaderForResponse(response, response_url_loader,
+                                                  response_client_request,
+                                                  url_loader)) {
+      subresource_loader_params_ =
+          interceptor->MaybeCreateSubresourceLoaderParams();
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace content
diff --git a/content/browser/shared_worker/shared_worker_script_loader.h b/content/browser/shared_worker/shared_worker_script_loader.h
index 93b7140..017ca7f 100644
--- a/content/browser/shared_worker/shared_worker_script_loader.h
+++ b/content/browser/shared_worker/shared_worker_script_loader.h
@@ -20,6 +20,7 @@
 namespace content {
 
 class AppCacheHost;
+class ThrottlingURLLoader;
 class NavigationLoaderInterceptor;
 class ResourceContext;
 class ServiceWorkerProviderHost;
@@ -83,12 +84,25 @@
       mojo::ScopedDataPipeConsumerHandle body) override;
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
+  // Returns a URLLoader client endpoint if an interceptor wants to handle the
+  // response, i.e. return a different response. For e.g. AppCache may have
+  // fallback content.
+  bool MaybeCreateLoaderForResponse(
+      const network::ResourceResponseHead& response,
+      network::mojom::URLLoaderPtr* response_url_loader,
+      network::mojom::URLLoaderClientRequest* response_client_request,
+      ThrottlingURLLoader* url_loader);
+
   base::Optional<SubresourceLoaderParams> TakeSubresourceLoaderParams() {
     return std::move(subresource_loader_params_);
   }
 
   base::WeakPtr<SharedWorkerScriptLoader> GetWeakPtr();
 
+  // Set to true if the default URLLoader (network service) was used for the
+  // current request.
+  bool default_loader_used_ = false;
+
  private:
   void Start();
   void MaybeStartLoader(
diff --git a/content/browser/shared_worker/shared_worker_script_loader_factory.cc b/content/browser/shared_worker/shared_worker_script_loader_factory.cc
index eae7545..bdbec0c 100644
--- a/content/browser/shared_worker/shared_worker_script_loader_factory.cc
+++ b/content/browser/shared_worker/shared_worker_script_loader_factory.cc
@@ -89,10 +89,4 @@
   NOTREACHED();
 }
 
-base::Optional<SubresourceLoaderParams>
-SharedWorkerScriptLoaderFactory::TakeSubresourceLoaderParams() {
-  DCHECK(script_loader_);
-  return script_loader_->TakeSubresourceLoaderParams();
-}
-
 }  // namespace content
diff --git a/content/browser/shared_worker/shared_worker_script_loader_factory.h b/content/browser/shared_worker/shared_worker_script_loader_factory.h
index 4eedffb8..58072e3 100644
--- a/content/browser/shared_worker/shared_worker_script_loader_factory.h
+++ b/content/browser/shared_worker/shared_worker_script_loader_factory.h
@@ -59,7 +59,9 @@
                                 traffic_annotation) override;
   void Clone(network::mojom::URLLoaderFactoryRequest request) override;
 
-  base::Optional<SubresourceLoaderParams> TakeSubresourceLoaderParams();
+  base::WeakPtr<SharedWorkerScriptLoader> GetScriptLoader() {
+    return script_loader_;
+  }
 
  private:
   const int process_id_;
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index f927098..cca3568d 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -391,6 +391,7 @@
 
   const bool force_fetch = load_flags_ & net::LOAD_BYPASS_CACHE;
 
+  cert_fetch_start_time_ = base::TimeTicks::Now();
   cert_fetcher_ = std::move(cert_fetcher_factory_)
                       ->CreateFetcherAndStart(
                           cert_url, force_fetch, *version_,
@@ -423,8 +424,13 @@
     std::unique_ptr<SignedExchangeCertificateChain> cert_chain) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
                "SignedExchangeHandler::OnCertReceived");
+  base::TimeDelta cert_fetch_duration =
+      base::TimeTicks::Now() - cert_fetch_start_time_;
   DCHECK_EQ(state_, State::kFetchingCertificate);
   if (result != SignedExchangeLoadResult::kSuccess) {
+    UMA_HISTOGRAM_MEDIUM_TIMES("SignedExchange.Time.CertificateFetch.Failure",
+                               cert_fetch_duration);
+
     signed_exchange_utils::ReportErrorAndTraceEvent(
         devtools_proxy_.get(), "Failed to fetch the certificate.",
         std::make_pair(0 /* signature_index */,
@@ -433,6 +439,8 @@
     return;
   }
 
+  UMA_HISTOGRAM_MEDIUM_TIMES("SignedExchange.Time.CertificateFetch.Success",
+                             cert_fetch_duration);
   unverified_cert_chain_ = std::move(cert_chain);
 
   const SignedExchangeSignatureVerifier::Result verify_result =
diff --git a/content/browser/web_package/signed_exchange_handler.h b/content/browser/web_package/signed_exchange_handler.h
index 2e94169..0465a06 100644
--- a/content/browser/web_package/signed_exchange_handler.h
+++ b/content/browser/web_package/signed_exchange_handler.h
@@ -152,6 +152,8 @@
 
   base::RepeatingCallback<int(void)> frame_tree_node_id_getter_;
 
+  base::TimeTicks cert_fetch_start_time_;
+
   base::WeakPtrFactory<SignedExchangeHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeHandler);
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index 0da4cb7..3599afc 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -222,6 +222,8 @@
   EXPECT_EQ(original_fingerprint, fingerprint);
   histogram_tester_.ExpectUniqueSample("SignedExchange.LoadResult",
                                        SignedExchangeLoadResult::kSuccess, 1);
+  histogram_tester_.ExpectTotalCount(
+      "SignedExchange.Time.CertificateFetch.Success", 1);
 }
 
 IN_PROC_BROWSER_TEST_F(SignedExchangeRequestHandlerBrowserTest,
@@ -310,6 +312,8 @@
   histogram_tester_.ExpectUniqueSample(
       "SignedExchange.LoadResult", SignedExchangeLoadResult::kCertFetchError,
       1);
+  histogram_tester_.ExpectTotalCount(
+      "SignedExchange.Time.CertificateFetch.Failure", 1);
 }
 
 class SignedExchangeRequestHandlerRealCertVerifierBrowserTest
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 1e794d78..1f0bfd3c 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -126,14 +126,6 @@
       return IDS_AX_MEDIA_AUDIO_ELEMENT_HELP;
     case WebLocalizedString::kAXMediaVideoElementHelp:
       return IDS_AX_MEDIA_VIDEO_ELEMENT_HELP;
-    case WebLocalizedString::kAXMediaMuteButtonHelp:
-      return IDS_AX_MEDIA_MUTE_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaUnMuteButtonHelp:
-      return IDS_AX_MEDIA_UNMUTE_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaPlayButtonHelp:
-      return IDS_AX_MEDIA_PLAY_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaPauseButtonHelp:
-      return IDS_AX_MEDIA_PAUSE_BUTTON_HELP;
     case WebLocalizedString::kAXMediaAudioSliderHelp:
       return IDS_AX_MEDIA_AUDIO_SLIDER_HELP;
     case WebLocalizedString::kAXMediaVideoSliderHelp:
@@ -144,24 +136,6 @@
       return IDS_AX_MEDIA_CURRENT_TIME_DISPLAY_HELP;
     case WebLocalizedString::kAXMediaTimeRemainingDisplayHelp:
       return IDS_AX_MEDIA_TIME_REMAINING_DISPLAY_HELP;
-    case WebLocalizedString::kAXMediaEnterFullscreenButtonHelp:
-      return IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaExitFullscreenButtonHelp:
-      return IDS_AX_MEDIA_EXIT_FULL_SCREEN_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaDisplayCutoutFullscreenButtonHelp:
-      return IDS_AX_MEDIA_DISPLAY_CUT_OUT_FULL_SCREEN_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaEnterPictureInPictureButtonHelp:
-      return IDS_AX_MEDIA_ENTER_PICTURE_IN_PICTURE_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaExitPictureInPictureButtonHelp:
-      return IDS_AX_MEDIA_EXIT_PICTURE_IN_PICTURE_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaShowClosedCaptionsButtonHelp:
-      return IDS_AX_MEDIA_SHOW_CLOSED_CAPTIONS_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaHideClosedCaptionsButtonHelp:
-      return IDS_AX_MEDIA_HIDE_CLOSED_CAPTIONS_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaCastOffButtonHelp:
-      return IDS_AX_MEDIA_CAST_OFF_BUTTON_HELP;
-    case WebLocalizedString::kAXMediaCastOnButtonHelp:
-      return IDS_AX_MEDIA_CAST_ON_BUTTON_HELP;
     case WebLocalizedString::kAXMediaOverflowButtonHelp:
       return IDS_AX_MEDIA_OVERFLOW_BUTTON_HELP;
     case WebLocalizedString::kAXMillisecondFieldText:
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 72fe98a..4cb11d61 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -499,6 +499,9 @@
 
   WebRuntimeFeatures::EnableNoHoverAfterLayoutChange(
       base::FeatureList::IsEnabled(features::kNoHoverAfterLayoutChange));
+
+  WebRuntimeFeatures::EnableNoHoverDuringScroll(
+      base::FeatureList::IsEnabled(features::kNoHoverDuringScroll));
 }
 
 }  // namespace content
diff --git a/content/common/service_worker/service_worker_container.mojom b/content/common/service_worker/service_worker_container.mojom
index c6a7e1e..fd67fc7a 100644
--- a/content/common/service_worker/service_worker_container.mojom
+++ b/content/common/service_worker/service_worker_container.mojom
@@ -34,7 +34,6 @@
 // - For shared workers (non-S13nSW):
 //       Associated with ServiceWorkerDispatcherHost, which is on the
 //       channel-associated interface to the renderer process.
-//       IPC channel.
 // - For documents:
 //       Associated with ServiceWorkerDispatcherHost, which is on the
 //       channel-associated interface to the renderer process.
@@ -91,9 +90,8 @@
                                 ControllerServiceWorkerPurpose purpose);
 
   // S13nServiceWorker:
-  // Clones the Mojo end point to the ServiceWorker container host. This is
-  // used to communicate with the host from dedicated and shared workers.
-  CloneForWorker(ServiceWorkerContainerHost& container_host_for_worker);
+  // Makes a new endpoint to this ServiceWorkerContainerHost.
+  CloneContainerHost(ServiceWorkerContainerHost& container_host);
 
   // Does nothing but calls the callback. Useful for pumping the message pipe
   // for this interface and associated interfaces: when the callback is called,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 0dff056..3672df015 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -33,11 +33,6 @@
     "AllowSignedHTTPExchangeCertsWithoutExtension",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables asm.js to WebAssembly V8 backend.
-// http://asmjs.org/spec/latest/
-const base::Feature kAsmJsToWebAssembly{"AsmJsToWebAssembly",
-                                        base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Creates audio output and input streams using the audio service.
 const base::Feature kAudioServiceAudioStreams{
     "AudioServiceAudioStreams", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 00fd287..874fc98f 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -21,7 +21,6 @@
     kAllowContentInitiatedDataUrlNavigations;
 CONTENT_EXPORT extern const base::Feature
     kAllowSignedHTTPExchangeCertsWithoutExtension;
-CONTENT_EXPORT extern const base::Feature kAsmJsToWebAssembly;
 CONTENT_EXPORT extern const base::Feature kAudioServiceAudioStreams;
 CONTENT_EXPORT extern const base::Feature kAudioServiceLaunchOnStartup;
 CONTENT_EXPORT extern const base::Feature kAudioServiceOutOfProcess;
diff --git a/content/renderer/appcache/appcache_frontend_impl.cc b/content/renderer/appcache/appcache_frontend_impl.cc
index 20afc6b..75e398f8 100644
--- a/content/renderer/appcache/appcache_frontend_impl.cc
+++ b/content/renderer/appcache/appcache_frontend_impl.cc
@@ -27,8 +27,7 @@
 
 void AppCacheFrontendImpl::OnStatusChanged(const std::vector<int>& host_ids,
                                            AppCacheStatus status) {
-  for (std::vector<int>::const_iterator i = host_ids.begin();
-       i != host_ids.end(); ++i) {
+  for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
       host->OnStatusChanged(status);
@@ -42,8 +41,7 @@
       AppCacheEventID::APPCACHE_PROGRESS_EVENT);  // See OnProgressEventRaised.
   DCHECK_NE(event_id,
             AppCacheEventID::APPCACHE_ERROR_EVENT);  // See OnErrorEventRaised.
-  for (std::vector<int>::const_iterator i = host_ids.begin();
-       i != host_ids.end(); ++i) {
+  for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
       host->OnEventRaised(event_id);
@@ -55,8 +53,7 @@
     const GURL& url,
     int num_total,
     int num_complete) {
-  for (std::vector<int>::const_iterator i = host_ids.begin();
-       i != host_ids.end(); ++i) {
+  for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
       host->OnProgressEventRaised(url, num_total, num_complete);
@@ -66,8 +63,7 @@
 void AppCacheFrontendImpl::OnErrorEventRaised(
     const std::vector<int>& host_ids,
     const AppCacheErrorDetails& details) {
-  for (std::vector<int>::const_iterator i = host_ids.begin();
-       i != host_ids.end(); ++i) {
+  for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
       host->OnErrorEventRaised(details);
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 5252ab6b..9c5002e1 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -78,7 +78,7 @@
     return nullptr;
 
   PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer();
-  PluginContainerMap::iterator it = browser_plugins->find(container);
+  auto it = browser_plugins->find(container);
   return it == browser_plugins->end() ? nullptr : it->second;
 }
 
diff --git a/content/renderer/categorized_worker_pool.cc b/content/renderer/categorized_worker_pool.cc
index 0235547..30a1737a 100644
--- a/content/renderer/categorized_worker_pool.cc
+++ b/content/renderer/categorized_worker_pool.cc
@@ -221,7 +221,7 @@
   DCHECK(completed_tasks_.empty());
   CollectCompletedTasksWithLockAcquired(namespace_token_, &completed_tasks_);
 
-  cc::Task::Vector::iterator end = std::remove_if(
+  auto end = std::remove_if(
       tasks_.begin(), tasks_.end(), [this](const scoped_refptr<cc::Task>& e) {
         return std::find(this->completed_tasks_.begin(),
                          this->completed_tasks_.end(),
diff --git a/content/renderer/dom_serializer_browsertest.cc b/content/renderer/dom_serializer_browsertest.cc
index 50347bd..14e32bb5 100644
--- a/content/renderer/dom_serializer_browsertest.cc
+++ b/content/renderer/dom_serializer_browsertest.cc
@@ -140,9 +140,9 @@
       WebData data(contents.data(), contents.length());
       GetMainFrame()->CommitDataNavigation(
           blink::WebURLRequest(base_url), data, "text/html", encoding_info,
-          WebURL(), false /* replace */, blink::WebFrameLoadType::kStandard,
-          blink::WebHistoryItem(), false /* is_client_redirect */,
-          nullptr /* navigation_params */, nullptr /* navigation_data */);
+          WebURL(), blink::WebFrameLoadType::kStandard, blink::WebHistoryItem(),
+          false /* is_client_redirect */, nullptr /* navigation_params */,
+          nullptr /* navigation_data */);
     }
     base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
     waiter.Wait();
diff --git a/content/renderer/dom_storage/dom_storage_cached_area.cc b/content/renderer/dom_storage/dom_storage_cached_area.cc
index 402c2772..a7cdf90 100644
--- a/content/renderer/dom_storage/dom_storage_cached_area.cc
+++ b/content/renderer/dom_storage/dom_storage_cached_area.cc
@@ -144,8 +144,7 @@
 
     // We have to retain local additions which happened after this
     // clear operation from another process.
-    std::map<base::string16, int>::iterator iter =
-        ignore_key_mutations_.begin();
+    auto iter = ignore_key_mutations_.begin();
     while (iter != ignore_key_mutations_.end()) {
       base::NullableString16 value = old->GetItem(iter->first);
       if (!value.is_null()) {
@@ -240,8 +239,7 @@
     Reset();
     return;
   }
-  std::map<base::string16, int>::iterator found =
-      ignore_key_mutations_.find(key);
+  auto found = ignore_key_mutations_.find(key);
   DCHECK(found != ignore_key_mutations_.end());
   if (--found->second == 0)
     ignore_key_mutations_.erase(found);
@@ -252,8 +250,7 @@
     blink::WebScopedVirtualTimePauser,
     bool success) {
   DCHECK(success);
-  std::map<base::string16, int>::iterator found =
-      ignore_key_mutations_.find(key);
+  auto found = ignore_key_mutations_.find(key);
   DCHECK(found != ignore_key_mutations_.end());
   if (--found->second == 0)
     ignore_key_mutations_.erase(found);
diff --git a/content/renderer/dom_storage/dom_storage_dispatcher.cc b/content/renderer/dom_storage/dom_storage_dispatcher.cc
index 0a8b4be..f7fcb66 100644
--- a/content/renderer/dom_storage/dom_storage_dispatcher.cc
+++ b/content/renderer/dom_storage/dom_storage_dispatcher.cc
@@ -171,7 +171,7 @@
   }
 
   CachedAreaHolder* GetAreaHolder(const std::string& key) {
-    CachedAreaMap::iterator found = cached_areas_.find(key);
+    auto found = cached_areas_.find(key);
     if (found == cached_areas_.end())
       return nullptr;
     return &(found->second);
diff --git a/content/renderer/image_downloader/image_downloader_impl.cc b/content/renderer/image_downloader/image_downloader_impl.cc
index f20ad65..164406a 100644
--- a/content/renderer/image_downloader/image_downloader_impl.cc
+++ b/content/renderer/image_downloader/image_downloader_impl.cc
@@ -54,8 +54,7 @@
   uint32_t min_image_size = std::numeric_limits<uint32_t>::max();
   // Filter the images by |max_image_size|, and also identify the smallest image
   // in case all the images are bigger than |max_image_size|.
-  for (std::vector<SkBitmap>::const_iterator it = unfiltered.begin();
-       it != unfiltered.end(); ++it) {
+  for (auto it = unfiltered.begin(); it != unfiltered.end(); ++it) {
     const SkBitmap& image = *it;
     uint32_t current_size = std::max(it->width(), it->height());
     if (current_size < min_image_size) {
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index f8b7704..4fc82f3 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -281,7 +281,7 @@
 
 ResourceDispatcher::PendingRequestInfo*
 ResourceDispatcher::GetPendingRequestInfo(int request_id) {
-  PendingRequestMap::iterator it = pending_requests_.find(request_id);
+  auto it = pending_requests_.find(request_id);
   if (it == pending_requests_.end())
     return nullptr;
   return it->second.get();
@@ -548,7 +548,7 @@
 bool ResourceDispatcher::RemovePendingRequest(
     int request_id,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  PendingRequestMap::iterator it = pending_requests_.find(request_id);
+  auto it = pending_requests_.find(request_id);
   if (it == pending_requests_.end())
     return false;
 
@@ -579,7 +579,7 @@
 void ResourceDispatcher::Cancel(
     int request_id,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  PendingRequestMap::iterator it = pending_requests_.find(request_id);
+  auto it = pending_requests_.find(request_id);
   if (it == pending_requests_.end()) {
     DLOG(ERROR) << "unknown request";
     return;
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 576cfdf4..e6e299b 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -1250,15 +1250,14 @@
         info.raw_request_response_info->response_headers_text));
     const HeadersVector& request_headers =
         info.raw_request_response_info->request_headers;
-    for (HeadersVector::const_iterator it = request_headers.begin();
-         it != request_headers.end(); ++it) {
+    for (auto it = request_headers.begin(); it != request_headers.end(); ++it) {
       load_info.AddRequestHeader(WebString::FromLatin1(it->first),
                                  WebString::FromLatin1(it->second));
     }
     const HeadersVector& response_headers =
         info.raw_request_response_info->response_headers;
-    for (HeadersVector::const_iterator it = response_headers.begin();
-         it != response_headers.end(); ++it) {
+    for (auto it = response_headers.begin(); it != response_headers.end();
+         ++it) {
       load_info.AddResponseHeader(WebString::FromLatin1(it->first),
                                   WebString::FromLatin1(it->second));
     }
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index dd6409b..fc03ca1 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -201,7 +201,7 @@
 
   mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info;
   if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    service_worker_container_host_->CloneForWorker(
+    service_worker_container_host_->CloneContainerHost(
         mojo::MakeRequest(&host_ptr_info));
   }
 
@@ -449,7 +449,7 @@
 
   network::mojom::URLLoaderFactoryPtr service_worker_url_loader_factory;
   mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info;
-  service_worker_container_host_->CloneForWorker(
+  service_worker_container_host_->CloneContainerHost(
       mojo::MakeRequest(&host_ptr_info));
   // To avoid potential dead-lock while synchronous loading, create the
   // SubresourceLoaderFactory on a background thread.
diff --git a/content/renderer/media/audio/audio_renderer_mixer_manager.cc b/content/renderer/media/audio/audio_renderer_mixer_manager.cc
index b457f7c..a3211106 100644
--- a/content/renderer/media/audio/audio_renderer_mixer_manager.cc
+++ b/content/renderer/media/audio/audio_renderer_mixer_manager.cc
@@ -187,7 +187,7 @@
                              latency_map_.to_ulong());
   }
 
-  AudioRendererMixerMap::iterator it = mixers_.find(key);
+  auto it = mixers_.find(key);
   if (it != mixers_.end()) {
     if (device_status)
       *device_status = media::OUTPUT_DEVICE_STATUS_OK;
@@ -223,7 +223,7 @@
 
 void AudioRendererMixerManager::ReturnMixer(media::AudioRendererMixer* mixer) {
   base::AutoLock auto_lock(mixers_lock_);
-  AudioRendererMixerMap::iterator it = std::find_if(
+  auto it = std::find_if(
       mixers_.begin(), mixers_.end(),
       [mixer](const std::pair<MixerKey, AudioRendererMixerReference>& val) {
         return val.second.mixer == mixer;
diff --git a/content/renderer/media/media_permission_dispatcher.cc b/content/renderer/media/media_permission_dispatcher.cc
index 64203183..96f057c 100644
--- a/content/renderer/media/media_permission_dispatcher.cc
+++ b/content/renderer/media/media_permission_dispatcher.cc
@@ -144,7 +144,7 @@
   DVLOG(2) << __func__ << ": (" << request_id << ", " << status << ")";
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
-  RequestMap::iterator iter = requests_.find(request_id);
+  auto iter = requests_.find(request_id);
   DCHECK(iter != requests_.end()) << "Request not found.";
 
   PermissionStatusCB permission_status_cb = iter->second;
diff --git a/content/renderer/media/midi/midi_message_filter.cc b/content/renderer/media/midi/midi_message_filter.cc
index b96e36e8..bb485d6 100644
--- a/content/renderer/media/midi/midi_message_filter.cc
+++ b/content/renderer/media/midi/midi_message_filter.cc
@@ -57,9 +57,8 @@
       << "RemoveClient call was not ballanced with AddClient call";
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   clients_.erase(client);
-  ClientsQueue::iterator it = std::find(clients_waiting_session_queue_.begin(),
-                                        clients_waiting_session_queue_.end(),
-                                        client);
+  auto it = std::find(clients_waiting_session_queue_.begin(),
+                      clients_waiting_session_queue_.end(), client);
   if (it != clients_waiting_session_queue_.end())
     clients_waiting_session_queue_.erase(it);
   if (clients_.empty() && clients_waiting_session_queue_.empty()) {
diff --git a/content/renderer/media/stream/aec_dump_message_filter.cc b/content/renderer/media/stream/aec_dump_message_filter.cc
index 5c1312e..bb1f429 100644
--- a/content/renderer/media/stream/aec_dump_message_filter.cc
+++ b/content/renderer/media/stream/aec_dump_message_filter.cc
@@ -148,7 +148,7 @@
     int id,
     IPC::PlatformFileForTransit file_handle) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-  DelegateMap::iterator it = delegates_.find(id);
+  auto it = delegates_.find(id);
   if (it != delegates_.end()) {
     it->second->OnAecDumpFile(file_handle);
   } else {
@@ -161,16 +161,14 @@
 
 void AecDumpMessageFilter::DoDisableAecDump() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-  for (DelegateMap::iterator it = delegates_.begin();
-       it != delegates_.end(); ++it) {
+  for (auto it = delegates_.begin(); it != delegates_.end(); ++it) {
     it->second->OnDisableAecDump();
   }
 }
 
 void AecDumpMessageFilter::DoChannelClosingOnDelegates() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-  for (DelegateMap::iterator it = delegates_.begin();
-       it != delegates_.end(); ++it) {
+  for (auto it = delegates_.begin(); it != delegates_.end(); ++it) {
     it->second->OnIpcClosing();
   }
   delegates_.clear();
@@ -179,8 +177,7 @@
 int AecDumpMessageFilter::GetIdForDelegate(
     AecDumpMessageFilter::AecDumpDelegate* delegate) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
-  for (DelegateMap::iterator it = delegates_.begin();
-       it != delegates_.end(); ++it) {
+  for (auto it = delegates_.begin(); it != delegates_.end(); ++it) {
     if (it->second == delegate)
       return it->first;
   }
diff --git a/content/renderer/media/stream/media_stream_device_observer.cc b/content/renderer/media/stream/media_stream_device_observer.cc
index 80a3e5df96..df23fbe6 100644
--- a/content/renderer/media/stream/media_stream_device_observer.cc
+++ b/content/renderer/media/stream/media_stream_device_observer.cc
@@ -22,8 +22,8 @@
 
 bool RemoveStreamDeviceFromArray(const MediaStreamDevice& device,
                                  MediaStreamDevices* devices) {
-  for (MediaStreamDevices::iterator device_it = devices->begin();
-       device_it != devices->end(); ++device_it) {
+  for (auto device_it = devices->begin(); device_it != devices->end();
+       ++device_it) {
     if (device_it->IsSameDevice(device)) {
       devices->erase(device_it);
       return true;
diff --git a/content/renderer/media/stream/media_stream_video_source.cc b/content/renderer/media/stream/media_stream_video_source.cc
index 74ba3b0..b3a4ddf7 100644
--- a/content/renderer/media/stream/media_stream_video_source.cc
+++ b/content/renderer/media/stream/media_stream_video_source.cc
@@ -85,8 +85,7 @@
                                          base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   {
-    std::vector<MediaStreamVideoTrack*>::iterator it =
-        std::find(tracks_.begin(), tracks_.end(), video_track);
+    auto it = std::find(tracks_.begin(), tracks_.end(), video_track);
     DCHECK(it != tracks_.end());
     tracks_.erase(it);
   }
diff --git a/content/renderer/media/stream/media_stream_video_track.cc b/content/renderer/media/stream/media_stream_video_track.cc
index 38a3319..fcfd43bd 100644
--- a/content/renderer/media/stream/media_stream_video_track.cc
+++ b/content/renderer/media/stream/media_stream_video_track.cc
@@ -128,7 +128,7 @@
     VideoSinkId id,
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
-  std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
+  auto it = callbacks_.begin();
   for (; it != callbacks_.end(); ++it) {
     if (it->first == id) {
       // Callback is copied to heap and then deleted on the target thread.
@@ -318,8 +318,7 @@
 
 void MediaStreamVideoTrack::RemoveSink(MediaStreamVideoSink* sink) {
   DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  std::vector<MediaStreamVideoSink*>::iterator it =
-      std::find(sinks_.begin(), sinks_.end(), sink);
+  auto it = std::find(sinks_.begin(), sinks_.end(), sink);
   DCHECK(it != sinks_.end());
   sinks_.erase(it);
   frame_deliverer_->RemoveCallback(sink);
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index 36d2961a..86c723b0 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -1252,7 +1252,7 @@
     const blink::WebMediaStreamSource& source) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  for (LocalStreamSources::iterator device_it = local_sources_.begin();
+  for (auto device_it = local_sources_.begin();
        device_it != local_sources_.end(); ++device_it) {
     if (IsSameSource(*device_it, source)) {
       local_sources_.erase(device_it);
@@ -1261,7 +1261,7 @@
   }
 
   // Check if the source was pending.
-  for (LocalStreamSources::iterator device_it = pending_local_sources_.begin();
+  for (auto device_it = pending_local_sources_.begin();
        device_it != pending_local_sources_.end(); ++device_it) {
     if (IsSameSource(*device_it, source)) {
       MediaStreamSource* const source_extra_data =
diff --git a/content/renderer/media/stream/video_track_adapter.cc b/content/renderer/media/stream/video_track_adapter.cc
index 28727ef..cbe15d6 100644
--- a/content/renderer/media/stream/video_track_adapter.cc
+++ b/content/renderer/media/stream/video_track_adapter.cc
@@ -179,7 +179,7 @@
 void VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveAndReleaseCallback(
     const MediaStreamVideoTrack* track) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
+  auto it = callbacks_.begin();
   for (; it != callbacks_.end(); ++it) {
     if (it->first == track) {
       // Make sure the VideoCaptureDeliverFrameCB is released on the main
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index d2b6ef2..8d5a6ab 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -829,6 +829,7 @@
       compositor_->GetCurrentFrameWithoutUpdatingStatistics();
 
   media::Context3D context_3d;
+  gpu::ContextSupport* context_support = nullptr;
   if (frame && frame->HasTextures()) {
     auto* provider =
         RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
@@ -836,11 +837,11 @@
     if (!provider)
       return;
     context_3d = media::Context3D(provider->ContextGL(), provider->GrContext());
-    DCHECK(context_3d.gl);
+    context_support = provider->ContextSupport();
   }
   const gfx::RectF dest_rect(rect.x, rect.y, rect.width, rect.height);
   video_renderer_.Paint(frame, canvas, dest_rect, flags, video_rotation_,
-                        context_3d);
+                        context_3d, context_support);
 }
 
 bool WebMediaPlayerMS::DidGetOpaqueResponseFromServiceWorker() const {
@@ -1016,8 +1017,8 @@
   DCHECK(context_3d.gl);
 
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, gl, video_frame.get(), target, texture, internal_format,
-      format, type, level, premultiply_alpha, flip_y);
+      context_3d, provider->ContextSupport(), gl, video_frame.get(), target,
+      texture, internal_format, format, type, level, premultiply_alpha, flip_y);
 }
 
 bool WebMediaPlayerMS::CopyVideoYUVDataToPlatformTexture(
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
index c53c0be2..92b6b60 100644
--- a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
@@ -68,7 +68,8 @@
     DCHECK(provider->ContextGL());
     video_renderer->Copy(
         frame.get(), &paint_canvas,
-        media::Context3D(provider->ContextGL(), provider->GrContext()));
+        media::Context3D(provider->ContextGL(), provider->GrContext()),
+        provider->ContextSupport());
 
     SkPixmap pixmap;
     const bool result = bitmap.peekPixels(&pixmap);
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
index 8a895d2c..2a48dee4 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
@@ -31,7 +31,7 @@
 template <class V>
 static typename V::iterator FindTrack(V* vector,
                                       const std::string& track_id) {
-  typename V::iterator it = vector->begin();
+  auto it = vector->begin();
   for (; it != vector->end(); ++it) {
     if ((*it)->id() == track_id) {
       break;
@@ -68,8 +68,7 @@
 }
 
 bool MockMediaStream::RemoveTrack(AudioTrackInterface* track) {
-  AudioTrackVector::iterator it = FindTrack(&audio_track_vector_,
-                                            track->id());
+  auto it = FindTrack(&audio_track_vector_, track->id());
   if (it == audio_track_vector_.end())
     return false;
   audio_track_vector_.erase(it);
@@ -78,8 +77,7 @@
 }
 
 bool MockMediaStream::RemoveTrack(VideoTrackInterface* track) {
-  VideoTrackVector::iterator it = FindTrack(&video_track_vector_,
-                                            track->id());
+  auto it = FindTrack(&video_track_vector_, track->id());
   if (it == video_track_vector_.end())
     return false;
   video_track_vector_.erase(it);
@@ -101,13 +99,13 @@
 
 rtc::scoped_refptr<AudioTrackInterface> MockMediaStream::FindAudioTrack(
     const std::string& track_id) {
-  AudioTrackVector::iterator it = FindTrack(&audio_track_vector_, track_id);
+  auto it = FindTrack(&audio_track_vector_, track_id);
   return it == audio_track_vector_.end() ? nullptr : *it;
 }
 
 rtc::scoped_refptr<VideoTrackInterface> MockMediaStream::FindVideoTrack(
     const std::string& track_id) {
-  VideoTrackVector::iterator it = FindTrack(&video_track_vector_, track_id);
+  auto it = FindTrack(&video_track_vector_, track_id);
   return it == video_track_vector_.end() ? nullptr : *it;
 }
 
@@ -117,14 +115,13 @@
 }
 
 void MockMediaStream::UnregisterObserver(ObserverInterface* observer) {
-  ObserverSet::iterator it = observers_.find(observer);
+  auto it = observers_.find(observer);
   DCHECK(it != observers_.end());
   observers_.erase(it);
 }
 
 void MockMediaStream::NotifyObservers() {
-  for (ObserverSet::iterator it = observers_.begin(); it != observers_.end();
-       ++it) {
+  for (auto it = observers_.begin(); it != observers_.end(); ++it) {
     (*it)->OnChanged();
   }
 }
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index efed2e4..e27341c 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -64,7 +64,7 @@
     streams_.push_back(stream);
   }
   void RemoveStream(MediaStreamInterface* stream) {
-    StreamVector::iterator it = streams_.begin();
+    auto it = streams_.begin();
     for (; it != streams_.end(); ++it) {
       if (it->get() == stream) {
         streams_.erase(it);
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index 76bffec..4af9ed3 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -511,7 +511,7 @@
   DCHECK(main_thread_.CalledOnValidThread());
 
   const std::string empty_track_id;
-  for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
+  for (auto it = peer_connection_id_map_.begin();
        it != peer_connection_id_map_.end(); ++it) {
     rtc::scoped_refptr<InternalStatsObserver> observer(
         new rtc::RefCountedObject<InternalStatsObserver>(
@@ -532,7 +532,7 @@
 
 void PeerConnectionTracker::OnSuspend() {
   DCHECK(main_thread_.CalledOnValidThread());
-  for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
+  for (auto it = peer_connection_id_map_.begin();
        it != peer_connection_id_map_.end(); ++it) {
     it->first->CloseClientPeerConnection();
   }
@@ -611,8 +611,7 @@
   DCHECK(main_thread_.CalledOnValidThread());
   DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
 
-  std::map<RTCPeerConnectionHandler*, int>::iterator it =
-      peer_connection_id_map_.find(pc_handler);
+  auto it = peer_connection_id_map_.find(pc_handler);
 
   if (it == peer_connection_id_map_.end()) {
     // The PeerConnection might not have been registered if its initilization
diff --git a/content/renderer/media/webrtc/rtc_video_decoder.cc b/content/renderer/media/webrtc/rtc_video_decoder.cc
index c42e15b2..cc7016e 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder.cc
@@ -361,8 +361,7 @@
   DVLOG(3) << "DismissPictureBuffer. id=" << id;
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
-  std::map<int32_t, media::PictureBuffer>::iterator it =
-      assigned_picture_buffers_.find(id);
+  auto it = assigned_picture_buffers_.find(id);
   if (it == assigned_picture_buffers_.end()) {
     NOTREACHED() << "Missing picture buffer: " << id;
     return;
@@ -385,8 +384,7 @@
   DVLOG(3) << "PictureReady";
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
-  std::map<int32_t, media::PictureBuffer>::iterator it =
-      assigned_picture_buffers_.find(picture.picture_buffer_id());
+  auto it = assigned_picture_buffers_.find(picture.picture_buffer_id());
   if (it == assigned_picture_buffers_.end()) {
     NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id();
     NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer.cc b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
index ef2de1d..c50c9ad6 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
@@ -515,7 +515,7 @@
   // set to 0.0.
   float volume = 0.0f;
 
-  SourcePlayingStates::iterator entry = source_playing_states_.find(source);
+  auto entry = source_playing_states_.find(source);
   if (entry != source_playing_states_.end()) {
     PlayingStates& states = entry->second;
     for (PlayingStates::const_iterator it = states.begin();
@@ -566,13 +566,12 @@
     PlayingState* state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!state->playing());
-  SourcePlayingStates::iterator found = source_playing_states_.find(source);
+  auto found = source_playing_states_.find(source);
   if (found == source_playing_states_.end())
     return false;
 
   PlayingStates& array = found->second;
-  PlayingStates::iterator state_it =
-      std::find(array.begin(), array.end(), state);
+  auto state_it = std::find(array.begin(), array.end(), state);
   if (state_it == array.end())
     return false;
 
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index c7fb3465..ef28d3170 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -313,7 +313,8 @@
     DCHECK(context_provider->ContextGL());
     video_renderer_->Copy(video_frame.get(), canvas_.get(),
                           media::Context3D(context_provider->ContextGL(),
-                                           context_provider->GrContext()));
+                                           context_provider->GrContext()),
+                          context_provider->ContextSupport());
 
     SkPixmap pixmap;
     if (!bitmap_.peekPixels(&pixmap)) {
diff --git a/content/renderer/p2p/ipc_network_manager.cc b/content/renderer/p2p/ipc_network_manager.cc
index 430f61d..169c27a0 100644
--- a/content/renderer/p2p/ipc_network_manager.cc
+++ b/content/renderer/p2p/ipc_network_manager.cc
@@ -93,8 +93,7 @@
   // rtc::Network uses these prefix_length to compare network
   // interfaces discovered.
   std::vector<rtc::Network*> networks;
-  for (net::NetworkInterfaceList::const_iterator it = list.begin();
-       it != list.end(); it++) {
+  for (auto it = list.begin(); it != list.end(); it++) {
     rtc::IPAddress ip_address =
         jingle_glue::NetIPAddressToRtcIPAddress(it->address);
     DCHECK(!ip_address.IsNil());
diff --git a/content/renderer/pepper/host_globals.cc b/content/renderer/pepper/host_globals.cc
index 5ef70b5..4a43be09f 100644
--- a/content/renderer/pepper/host_globals.cc
+++ b/content/renderer/pepper/host_globals.cc
@@ -43,9 +43,7 @@
 // Adds all WebPluginContainers associated with the given module to the set.
 void GetAllContainersForModule(PluginModule* module, ContainerSet* containers) {
   const PluginModule::PluginInstanceSet& instances = module->GetAllInstances();
-  for (PluginModule::PluginInstanceSet::const_iterator i = instances.begin();
-       i != instances.end();
-       ++i) {
+  for (auto i = instances.begin(); i != instances.end(); ++i) {
     WebPluginContainer* container = (*i)->container();
     // If "Delete" is called on an instance, the instance sets its container to
     // NULL, but the instance may actually outlive its container. Callers of
@@ -107,7 +105,7 @@
 
 ppapi::CallbackTracker* HostGlobals::GetCallbackTrackerForInstance(
     PP_Instance instance) {
-  InstanceMap::iterator found = instance_map_.find(instance);
+  auto found = instance_map_.find(instance);
   if (found == instance_map_.end())
     return nullptr;
   return found->second->module()->GetCallbackTracker().get();
@@ -182,8 +180,7 @@
   }
 
   WebConsoleMessage message = MakeLogMessage(level, source, value);
-  for (ContainerSet::iterator i = containers.begin(); i != containers.end();
-       ++i) {
+  for (auto i = containers.begin(); i != containers.end(); ++i) {
     WebLocalFrame* frame = (*i)->GetDocument().GetFrame();
     if (frame)
       frame->AddMessageToConsole(message);
@@ -222,7 +219,7 @@
 void HostGlobals::ModuleDeleted(PP_Module module) {
   DLOG_IF(ERROR, !CheckIdType(module, ppapi::PP_ID_TYPE_MODULE))
       << module << " is not a PP_Module.";
-  ModuleMap::iterator found = module_map_.find(module);
+  auto found = module_map_.find(module);
   if (found == module_map_.end()) {
     NOTREACHED();
     return;
@@ -233,7 +230,7 @@
 PluginModule* HostGlobals::GetModule(PP_Module module) {
   DLOG_IF(ERROR, !CheckIdType(module, ppapi::PP_ID_TYPE_MODULE))
       << module << " is not a PP_Module.";
-  ModuleMap::iterator found = module_map_.find(module);
+  auto found = module_map_.find(module);
   if (found == module_map_.end())
     return nullptr;
   return found->second;
@@ -274,7 +271,7 @@
 PepperPluginInstanceImpl* HostGlobals::GetInstance(PP_Instance instance) {
   DLOG_IF(ERROR, !CheckIdType(instance, ppapi::PP_ID_TYPE_INSTANCE))
       << instance << " is not a PP_Instance.";
-  InstanceMap::iterator found = instance_map_.find(instance);
+  auto found = instance_map_.find(instance);
   if (found == instance_map_.end())
     return nullptr;
   return found->second;
diff --git a/content/renderer/pepper/host_var_tracker.cc b/content/renderer/pepper/host_var_tracker.cc
index b058576d..0424b3d 100644
--- a/content/renderer/pepper/host_var_tracker.cc
+++ b/content/renderer/pepper/host_var_tracker.cc
@@ -63,8 +63,8 @@
 void HostVarTracker::RemoveV8ObjectVar(V8ObjectVar* object_var) {
   CheckThreadingPreconditions();
   v8::HandleScope handle_scope(object_var->instance()->GetIsolate());
-  ObjectMap::iterator it = GetForV8Object(
-      object_var->instance()->pp_instance(), object_var->GetHandle());
+  auto it = GetForV8Object(object_var->instance()->pp_instance(),
+                           object_var->GetHandle());
   DCHECK(it != object_map_.end());
   object_map_.erase(it);
 }
@@ -120,7 +120,7 @@
   // Use a key with an empty handle to find the v8 object var in the map with
   // the given instance and the lowest hash.
   V8ObjectVarKey key(pp_instance, v8::Local<v8::Object>());
-  ObjectMap::iterator it = object_map_.lower_bound(key);
+  auto it = object_map_.lower_bound(key);
   while (it != object_map_.end() && it->first.instance == pp_instance) {
     ForceReleaseV8Object(it->second);
     object_map_.erase(it++);
@@ -129,7 +129,7 @@
 
 void HostVarTracker::ForceReleaseV8Object(ppapi::V8ObjectVar* object_var) {
   object_var->InstanceDeleted();
-  VarMap::iterator iter = live_vars_.find(object_var->GetExistingVarID());
+  auto iter = live_vars_.find(object_var->GetExistingVarID());
   if (iter == live_vars_.end()) {
     NOTREACHED();
     return;
@@ -145,7 +145,7 @@
   std::pair<ObjectMap::iterator, ObjectMap::iterator> range =
       object_map_.equal_range(V8ObjectVarKey(instance, object));
 
-  for (ObjectMap::iterator it = range.first; it != range.second; ++it) {
+  for (auto it = range.first; it != range.second; ++it) {
     if (object == it->second->GetHandle())
       return it;
   }
@@ -174,7 +174,7 @@
     PP_Instance instance,
     base::SharedMemoryHandle* handle,
     uint32_t* size_in_bytes) {
-  SharedMemoryMap::iterator it = shared_memory_map_.find(id);
+  auto it = shared_memory_map_.find(id);
   if (it == shared_memory_map_.end())
     return false;
   if (it->second.instance != instance)
diff --git a/content/renderer/pepper/pepper_broker.cc b/content/renderer/pepper/pepper_broker.cc
index 1f5cd9f..4d75b47 100644
--- a/content/renderer/pepper/pepper_broker.cc
+++ b/content/renderer/pepper/pepper_broker.cc
@@ -156,8 +156,7 @@
   dispatcher_.reset(dispatcher.release());
 
   // Process all pending channel requests from the plugins.
-  for (ClientMap::iterator i = pending_connects_.begin();
-       i != pending_connects_.end();) {
+  for (auto i = pending_connects_.begin(); i != pending_connects_.end();) {
     base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client;
     if (!i->second.is_authorized) {
       ++i;
@@ -173,7 +172,7 @@
 
 void PepperBroker::OnBrokerPermissionResult(PPB_Broker_Impl* client,
                                             bool result) {
-  ClientMap::iterator entry = pending_connects_.find(client);
+  auto entry = pending_connects_.find(client);
   if (entry == pending_connects_.end())
     return;
 
@@ -213,9 +212,7 @@
 
 void PepperBroker::ReportFailureToClients(int error_code) {
   DCHECK_NE(PP_OK, error_code);
-  for (ClientMap::iterator i = pending_connects_.begin();
-       i != pending_connects_.end();
-       ++i) {
+  for (auto i = pending_connects_.begin(); i != pending_connects_.end(); ++i) {
     base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client;
     if (weak_ptr.get()) {
       weak_ptr->BrokerConnected(
diff --git a/content/renderer/pepper/pepper_browser_connection.cc b/content/renderer/pepper/pepper_browser_connection.cc
index 19ba1cc..71b3e6c 100644
--- a/content/renderer/pepper/pepper_browser_connection.cc
+++ b/content/renderer/pepper/pepper_browser_connection.cc
@@ -74,8 +74,7 @@
     const std::vector<int>& pending_resource_host_ids) {
   // Check that the message is destined for the plugin this object is associated
   // with.
-  std::map<int32_t, PendingResourceIDCallback>::iterator it =
-      pending_create_map_.find(sequence_number);
+  auto it = pending_create_map_.find(sequence_number);
   if (it != pending_create_map_.end()) {
     it->second.Run(pending_resource_host_ids);
     pending_create_map_.erase(it);
diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc
index 606cec3..7e09fcc 100644
--- a/content/renderer/pepper/pepper_media_device_manager.cc
+++ b/content/renderer/pepper/pepper_media_device_manager.cc
@@ -232,7 +232,7 @@
                                               bool success,
                                               const std::string& label,
                                               const MediaStreamDevice& device) {
-  OpenCallbackMap::iterator iter = open_callbacks_.find(request_id);
+  auto iter = open_callbacks_.find(request_id);
   if (iter == open_callbacks_.end()) {
     // The callback may have been unregistered.
     return;
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 5babe8a6..1ed2844 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -438,8 +438,7 @@
 
 void PepperPluginInstanceImpl::ExternalDocumentLoader::ReplayReceivedData(
     WebAssociatedURLLoaderClient* document_loader) {
-  for (std::list<std::string>::iterator it = data_.begin(); it != data_.end();
-       ++it) {
+  for (auto it = data_.begin(); it != data_.end(); ++it) {
     document_loader->DidReceiveData(it->c_str(), it->length());
   }
   if (finished_loading_) {
@@ -591,8 +590,7 @@
   // unregister themselves inside the delete call).
   PluginObjectSet plugin_object_copy;
   live_plugin_objects_.swap(plugin_object_copy);
-  for (PluginObjectSet::iterator i = plugin_object_copy.begin();
-       i != plugin_object_copy.end();
+  for (auto i = plugin_object_copy.begin(); i != plugin_object_copy.end();
        ++i) {
     (*i)->InstanceDeleted();
   }
@@ -1020,10 +1018,9 @@
   for (size_t i = 0; i < ime_text_spans.size(); ++i) {
     if (ime_text_spans[i].thickness ==
         ws::mojom::ImeTextSpanThickness::kThick) {
-      std::vector<uint32_t>::iterator it =
-          std::find(event.composition_segment_offsets.begin(),
-                    event.composition_segment_offsets.end(),
-                    utf8_offsets[2 * i + 2]);
+      auto it = std::find(event.composition_segment_offsets.begin(),
+                          event.composition_segment_offsets.end(),
+                          utf8_offsets[2 * i + 2]);
       if (it != event.composition_segment_offsets.end()) {
         event.composition_target_segment =
             it - event.composition_segment_offsets.begin();
@@ -2370,9 +2367,7 @@
       CreateSimulatedWebInputEvents(
           input_event, view_data_.rect.point.x + view_data_.rect.size.width / 2,
           view_data_.rect.point.y + view_data_.rect.size.height / 2);
-  for (std::vector<std::unique_ptr<WebInputEvent>>::iterator it =
-           events.begin();
-       it != events.end(); ++it) {
+  for (auto it = events.begin(); it != events.end(); ++it) {
     widget->HandleInputEvent(blink::WebCoalescedInputEvent(*it->get()));
   }
   if (input_event.event_type == PP_INPUTEVENT_TYPE_TOUCHSTART ||
diff --git a/content/renderer/pepper/pepper_plugin_registry.cc b/content/renderer/pepper/pepper_plugin_registry.cc
index 73ddbac..0f87597 100644
--- a/content/renderer/pepper/pepper_plugin_registry.cc
+++ b/content/renderer/pepper/pepper_plugin_registry.cc
@@ -48,8 +48,7 @@
 PluginModule* PepperPluginRegistry::GetLiveModule(
     const base::FilePath& path,
     const base::Optional<url::Origin>& origin_lock) {
-  NonOwningModuleMap::iterator module_iter =
-      live_modules_.find({path, origin_lock});
+  auto module_iter = live_modules_.find({path, origin_lock});
   if (module_iter == live_modules_.end())
     return nullptr;
 
@@ -64,8 +63,7 @@
   if (instance_set.empty())
     return module_iter->second;
 
-  PluginModule::PluginInstanceSet::const_iterator instance_iter =
-      instance_set.begin();
+  auto instance_iter = instance_set.begin();
   while (instance_iter != instance_set.end()) {
     if (!(*instance_iter)->is_deleted())
       return module_iter->second;
@@ -88,9 +86,7 @@
 
   // Modules aren't destroyed very often and there are normally at most a
   // couple of them. So for now we just do a brute-force search.
-  for (NonOwningModuleMap::iterator i = live_modules_.begin();
-       i != live_modules_.end();
-       ++i) {
+  for (auto i = live_modules_.begin(); i != live_modules_.end(); ++i) {
     if (i->second == dead_module) {
       live_modules_.erase(i);
       return;
diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc
index 5201b8d..ae99e928 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.cc
+++ b/content/renderer/pepper/pepper_video_decoder_host.cc
@@ -319,7 +319,7 @@
     return PP_ERROR_FAILED;
   DCHECK(decoder_);
 
-  PictureBufferMap::iterator it = picture_buffer_map_.find(texture_id);
+  auto it = picture_buffer_map_.find(texture_id);
   if (it == picture_buffer_map_.end())
     return PP_ERROR_BADARGUMENT;
 
@@ -388,8 +388,7 @@
 }
 
 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
-  PictureBufferMap::iterator it =
-      picture_buffer_map_.find(picture.picture_buffer_id());
+  auto it = picture_buffer_map_.find(picture.picture_buffer_id());
   DCHECK(it != picture_buffer_map_.end());
   DCHECK(it->second == PictureBufferState::ASSIGNED);
   it->second = PictureBufferState::IN_USE;
@@ -404,7 +403,7 @@
 }
 
 void PepperVideoDecoderHost::DismissPictureBuffer(int32_t picture_buffer_id) {
-  PictureBufferMap::iterator it = picture_buffer_map_.find(picture_buffer_id);
+  auto it = picture_buffer_map_.find(picture_buffer_id);
   DCHECK(it != picture_buffer_map_.end());
 
   // If the texture is still used by the plugin keep it until the plugin
@@ -423,7 +422,7 @@
 
 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
     int32_t bitstream_buffer_id) {
-  PendingDecodeList::iterator it = GetPendingDecodeById(bitstream_buffer_id);
+  auto it = GetPendingDecodeById(bitstream_buffer_id);
   if (it == pending_decodes_.end()) {
     NOTREACHED();
     return;
diff --git a/content/renderer/pepper/pepper_websocket_host.cc b/content/renderer/pepper/pepper_websocket_host.cc
index 0a8e50a..0a6e752 100644
--- a/content/renderer/pepper/pepper_websocket_host.cc
+++ b/content/renderer/pepper/pepper_websocket_host.cc
@@ -216,10 +216,8 @@
 
   // Validate protocols.
   std::string protocol_string;
-  for (std::vector<std::string>::const_iterator vector_it = protocols.begin();
-       vector_it != protocols.end();
+  for (auto vector_it = protocols.begin(); vector_it != protocols.end();
        ++vector_it) {
-
     // Check containing characters.
     for (std::string::const_iterator string_it = vector_it->begin();
          string_it != vector_it->end();
diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc
index c0e6fb2..cf6e7d16 100644
--- a/content/renderer/pepper/plugin_module.cc
+++ b/content/renderer/pepper/plugin_module.cc
@@ -679,9 +679,7 @@
   is_crashed_ = true;
 
   // Notify all instances that they crashed.
-  for (PluginInstanceSet::iterator i = instances_.begin();
-       i != instances_.end();
-       ++i)
+  for (auto i = instances_.begin(); i != instances_.end(); ++i)
     (*i)->InstanceCrashed();
 
   PepperPluginRegistry::GetInstance()->PluginModuleDead(this);
diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc
index 93cdbdc..ca81eb01 100644
--- a/content/renderer/pepper/v8_var_converter.cc
+++ b/content/renderer/pepper/v8_var_converter.cc
@@ -94,7 +94,7 @@
   if (ppapi::VarTracker::IsVarTypeRefcounted(var.type)) {
     if (parent_ids->count(var.value.as_id) != 0)
       return false;
-    VarHandleMap::iterator it = visited_ids->find(var.value.as_id);
+    auto it = visited_ids->find(var.value.as_id);
     if (it != visited_ids->end()) {
       *result = it->second;
       return true;
@@ -401,10 +401,8 @@
       DCHECK(current_v8->IsObject());
       v8::Local<v8::Object> v8_object = current_v8.As<v8::Object>();
 
-      for (DictionaryVar::KeyValueMap::const_iterator iter =
-               dict_var->key_value_map().begin();
-           iter != dict_var->key_value_map().end();
-           ++iter) {
+      for (auto iter = dict_var->key_value_map().begin();
+           iter != dict_var->key_value_map().end(); ++iter) {
         const std::string& key = iter->first;
         const PP_Var& child_var = iter->second.get();
         v8::Local<v8::Value> child_v8;
diff --git a/content/renderer/pepper/v8_var_converter_unittest.cc b/content/renderer/pepper/v8_var_converter_unittest.cc
index 6c4bfd95..3de4b45 100644
--- a/content/renderer/pepper/v8_var_converter_unittest.cc
+++ b/content/renderer/pepper/v8_var_converter_unittest.cc
@@ -80,7 +80,7 @@
             v8::Local<v8::Value> val,
             VarHandleMap* visited_ids) {
   if (ppapi::VarTracker::IsVarTypeRefcounted(var.type)) {
-    VarHandleMap::iterator it = visited_ids->find(var.value.as_id);
+    auto it = visited_ids->find(var.value.as_id);
     if (it != visited_ids->end())
       return it->second == val;
     (*visited_ids)[var.value.as_id] = val;
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index ac45730..b7ea303d 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -848,7 +848,7 @@
 VideoDecoderShim::~VideoDecoderShim() {
   DCHECK(RenderThreadImpl::current());
   // Delete any remaining textures.
-  TextureIdMap::iterator it = texture_id_map_.begin();
+  auto it = texture_id_map_.begin();
   for (; it != texture_id_map_.end(); ++it)
     DeleteTexture(it->second);
   texture_id_map_.clear();
@@ -1024,9 +1024,8 @@
            ++it) {
         textures_to_dismiss_.insert(it->first);
       }
-      for (TextureIdSet::const_iterator it = available_textures_.begin();
-           it != available_textures_.end();
-           ++it) {
+      for (auto it = available_textures_.begin();
+           it != available_textures_.end(); ++it) {
         DismissTexture(*it);
       }
       available_textures_.clear();
@@ -1049,7 +1048,7 @@
   while (!pending_frames_.empty() && !available_textures_.empty()) {
     const std::unique_ptr<PendingFrame>& frame = pending_frames_.front();
 
-    TextureIdSet::iterator it = available_textures_.begin();
+    auto it = available_textures_.begin();
     uint32_t texture_id = *it;
     available_textures_.erase(it);
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 9619fcf..0549df6b 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1250,8 +1250,7 @@
 
 // static
 RenderFrameImpl* RenderFrameImpl::FromRoutingID(int routing_id) {
-  RoutingIDFrameMap::iterator iter =
-      g_routing_id_frame_map.Get().find(routing_id);
+  auto iter = g_routing_id_frame_map.Get().find(routing_id);
   if (iter != g_routing_id_frame_map.Get().end())
     return iter->second;
   return nullptr;
@@ -1528,7 +1527,7 @@
 // static
 void RenderFrame::ForEach(RenderFrameVisitor* visitor) {
   FrameMap* frames = g_frame_map.Pointer();
-  for (FrameMap::iterator it = frames->begin(); it != frames->end(); ++it) {
+  for (auto it = frames->begin(); it != frames->end(); ++it) {
     if (!visitor->Visit(it->second))
       return;
   }
@@ -1547,7 +1546,7 @@
 
 // static
 RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) {
-  FrameMap::iterator iter = g_frame_map.Get().find(web_frame);
+  auto iter = g_frame_map.Get().find(web_frame);
   if (iter != g_frame_map.Get().end())
     return iter->second;
   return nullptr;
@@ -2848,6 +2847,8 @@
                     : blink::WebFrameLoadType::kStandard;
   const blink::WebHistoryItem& history_item =
       history_entry ? history_entry->root() : blink::WebHistoryItem();
+  if (replace && frame_load_type == WebFrameLoadType::kStandard)
+    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
 
   // Failed navigations will always provide a |failed_request|.  Error induced
   // by the client/renderer side after a commit won't have a |failed_request|.
@@ -2862,10 +2863,10 @@
   // should not inherit the cache mode from |failed_request|).
   new_request.SetCacheMode(blink::mojom::FetchCacheMode::kNoStore);
 
-  frame_->CommitDataNavigation(
-      new_request, error_html, "text/html", "UTF-8", error_url, replace,
-      frame_load_type, history_item, is_client_redirect,
-      std::move(navigation_params), std::move(navigation_data));
+  frame_->CommitDataNavigation(new_request, error_html, "text/html", "UTF-8",
+                               error_url, frame_load_type, history_item,
+                               is_client_redirect, std::move(navigation_params),
+                               std::move(navigation_data));
 }
 
 void RenderFrameImpl::DidMeaningfulLayout(
@@ -3743,11 +3744,6 @@
     service_worker_client_request = mojo::MakeRequest(&worker_client_ptr);
     provider_context->RegisterWorkerClient(std::move(worker_client_ptr));
 
-    // TODO(horo): Use this host pointer also when S13nServiceWorker is not
-    // enabled once we support navigator.serviceWorker on dedicated workers:
-    // crbug.com/371690. Currently we use this only to call
-    // GetControllerServiceWorker() from the worker thread if S13nServiceWorker
-    // is enabled.
     if (blink::ServiceWorkerUtils::IsServicificationEnabled())
       container_host_ptr_info = provider_context->CloneContainerHostPtrInfo();
   }
@@ -4009,7 +4005,7 @@
   // the RenderFrameImpl.  In contrast, the main frame is owned by its
   // containing RenderViewHost (so that they have the same lifetime), so only
   // removal from the map is needed and no deletion.
-  FrameMap::iterator it = g_frame_map.Get().find(frame_);
+  auto it = g_frame_map.Get().find(frame_);
   CHECK(it != g_frame_map.Get().end());
   CHECK_EQ(it->second, this);
   g_frame_map.Get().erase(it);
@@ -5687,8 +5683,7 @@
 
     // Set zoom level, but don't do it for full-page plugin since they don't use
     // the same zoom settings.
-    HostZoomLevels::iterator host_zoom =
-        host_zoom_levels_.find(GetLoadingUrl());
+    auto host_zoom = host_zoom_levels_.find(GetLoadingUrl());
     if (render_view_->webview()->MainFrame()->IsWebLocalFrame() &&
         render_view_->webview()
             ->MainFrame()
@@ -6891,14 +6886,12 @@
     const GURL base_url = common_params.base_url_for_data_url.is_empty()
                               ? common_params.url
                               : common_params.base_url_for_data_url;
-    bool replace = load_type == WebFrameLoadType::kReloadBypassingCache ||
-                   load_type == WebFrameLoadType::kReload;
 
     frame_->CommitDataNavigation(
         WebURLRequest(base_url), WebData(data.c_str(), data.length()),
         WebString::FromUTF8(mime_type), WebString::FromUTF8(charset),
         // Needed so that history-url-only changes don't become reloads.
-        common_params.history_url_for_data_url, replace, load_type,
+        common_params.history_url_for_data_url, load_type,
         item_for_history_navigation, is_client_redirect,
         BuildNavigationParams(
             common_params, request_params,
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index e1df1b76..ade104d 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -179,7 +179,7 @@
 // static
 RenderFrameProxy* RenderFrameProxy::FromRoutingID(int32_t routing_id) {
   RoutingIDProxyMap* proxies = g_routing_id_proxy_map.Pointer();
-  RoutingIDProxyMap::iterator it = proxies->find(routing_id);
+  auto it = proxies->find(routing_id);
   return it == proxies->end() ? NULL : it->second;
 }
 
@@ -188,7 +188,7 @@
     blink::WebRemoteFrame* web_frame) {
   // TODO(dcheng): Turn this into a DCHECK() if it doesn't crash on canary.
   CHECK(web_frame);
-  FrameProxyMap::iterator iter = g_frame_proxy_map.Get().find(web_frame);
+  auto iter = g_frame_proxy_map.Get().find(web_frame);
   if (iter != g_frame_proxy_map.Get().end()) {
     RenderFrameProxy* proxy = iter->second;
     DCHECK_EQ(web_frame, proxy->web_frame());
@@ -730,7 +730,7 @@
 
   // Remove the entry in the WebFrame->RenderFrameProxy map, as the |web_frame_|
   // is no longer valid.
-  FrameProxyMap::iterator it = g_frame_proxy_map.Get().find(web_frame_);
+  auto it = g_frame_proxy_map.Get().find(web_frame_);
   CHECK(it != g_frame_proxy_map.Get().end());
   CHECK_EQ(it->second, this);
   g_frame_proxy_map.Get().erase(it);
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index f641614f..b9f38b0 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -136,9 +136,6 @@
       "--harmony-dynamic-import --harmony-import-meta";
   v8::V8::SetFlagsFromString(kModuleFlags, sizeof(kModuleFlags));
 
-  SetV8FlagIfFeature(features::kAsmJsToWebAssembly, "--validate-asm");
-  SetV8FlagIfNotFeature(features::kAsmJsToWebAssembly, "--no-validate-asm");
-
   SetV8FlagIfFeature(features::kV8Orinoco, "--no-single-threaded-gc");
   SetV8FlagIfNotFeature(features::kV8Orinoco, "--single-threaded-gc");
 
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 6d60eae..a6bdb66 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -380,8 +380,7 @@
 void ApplyFontsFromMap(const ScriptFontFamilyMap& map,
                        SetFontFamilyWrapper setter,
                        WebSettings* settings) {
-  for (ScriptFontFamilyMap::const_iterator it = map.begin(); it != map.end();
-       ++it) {
+  for (auto it = map.begin(); it != map.end(); ++it) {
     int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, (it->first).c_str());
     if (script >= 0 && script < USCRIPT_CODE_LIMIT) {
       UScriptCode code = static_cast<UScriptCode>(script);
@@ -612,7 +611,7 @@
 /*static*/
 RenderViewImpl* RenderViewImpl::FromWebView(WebView* webview) {
   ViewMap* views = g_view_map.Pointer();
-  ViewMap::iterator it = views->find(webview);
+  auto it = views->find(webview);
   return it == views->end() ? NULL : it->second;
 }
 
@@ -624,7 +623,7 @@
 /*static*/
 RenderViewImpl* RenderViewImpl::FromRoutingID(int32_t routing_id) {
   RoutingIDViewMap* views = g_routing_id_view_map.Pointer();
-  RoutingIDViewMap::iterator it = views->find(routing_id);
+  auto it = views->find(routing_id);
   return it == views->end() ? NULL : it->second;
 }
 
@@ -641,7 +640,7 @@
 /*static*/
 void RenderView::ForEach(RenderViewVisitor* visitor) {
   ViewMap* views = g_view_map.Pointer();
-  for (ViewMap::iterator it = views->begin(); it != views->end(); ++it) {
+  for (auto it = views->begin(); it != views->end(); ++it) {
     if (!visitor->Visit(it->second))
       return;
   }
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 055afd0..2d10e5b 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -297,9 +297,7 @@
     item_list.push_back(item);
   }
 
-  for (std::vector<ui::FileInfo>::const_iterator it =
-           drop_data.filenames.begin();
-       it != drop_data.filenames.end();
+  for (auto it = drop_data.filenames.begin(); it != drop_data.filenames.end();
        ++it) {
     WebDragData::Item item;
     item.storage_type = WebDragData::Item::kStorageTypeFilename;
@@ -309,10 +307,8 @@
     item_list.push_back(item);
   }
 
-  for (std::vector<DropData::FileSystemFileInfo>::const_iterator it =
-           drop_data.file_system_files.begin();
-       it != drop_data.file_system_files.end();
-       ++it) {
+  for (auto it = drop_data.file_system_files.begin();
+       it != drop_data.file_system_files.end(); ++it) {
     WebDragData::Item item;
     item.storage_type = WebDragData::Item::kStorageTypeFileSystemFile;
     item.file_system_url = it->url;
@@ -470,7 +466,7 @@
 // static
 RenderWidget* RenderWidget::FromRoutingID(int32_t routing_id) {
   RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer();
-  RoutingIDWidgetMap::iterator it = widgets->find(routing_id);
+  auto it = widgets->find(routing_id);
   return it == widgets->end() ? NULL : it->second;
 }
 
diff --git a/content/renderer/service_worker/controller_service_worker_connector.h b/content/renderer/service_worker/controller_service_worker_connector.h
index 066a87c7..49a66ad 100644
--- a/content/renderer/service_worker/controller_service_worker_connector.h
+++ b/content/renderer/service_worker/controller_service_worker_connector.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_CONTROLLER_SERVICE_WORKER_CONNECTOR_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_CONTROLLER_SERVICE_WORKER_CONNECTOR_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
@@ -97,16 +99,11 @@
 
   mojo::BindingSet<mojom::ControllerServiceWorkerConnector> bindings_;
 
-  // Keeps the mojo end to the browser process on its own.
-  // Non-null only for the service worker clients that are workers (i.e., only
-  // when created for dedicated workers or shared workers).
+  // Connection to the container host in the browser process.
   mojom::ServiceWorkerContainerHostPtr container_host_ptr_;
 
-  // Connection to the ControllerServiceWorker. The consumer of this connection
-  // should not need to know which process this is connected to.
-  // (Currently this is connected to BrowserSideControllerServiceWorker,
-  // but will eventually be directly connected to the controller service worker
-  // in the renderer process)
+  // Connection to the controller service worker, which lives in a renderer
+  // process that's not necessarily the same as this connector.
   mojom::ControllerServiceWorkerPtr controller_service_worker_;
 
   base::ObserverList<Observer>::Unchecked observer_list_;
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 697f3e3..7ee9e0f2 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -204,8 +204,8 @@
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(state_for_client_);
   mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info;
-  // TODO(kinuko): rename this, now this can be used for non-worker clients.
-  container_host_->CloneForWorker(mojo::MakeRequest(&container_host_ptr_info));
+  container_host_->CloneContainerHost(
+      mojo::MakeRequest(&container_host_ptr_info));
   return container_host_ptr_info;
 }
 
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index 197858b..f6773f10 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -139,11 +139,8 @@
       mojom::ServiceWorkerWorkerClientRegistryRequest request) override;
 
   // S13nServiceWorker:
-  // For service worker clients. Creates a ServiceWorkerContainerHostPtrInfo
-  // which can be bound to a ServiceWorkerContainerHostPtr in a (dedicated or
-  // shared) worker thread. WebWorkerFetchContextImpl will use the host pointer
-  // to get the controller service worker by GetControllerServiceWorker() and
-  // send FetchEvents to the service worker.
+  // For service worker clients. Returns a ServiceWorkerContainerHostPtrInfo
+  // to this client's container host.
   mojom::ServiceWorkerContainerHostPtrInfo CloneContainerHostPtrInfo();
 
   // For service worker clients. Returns the registration object described by
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index 7274953c..94925ba2 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -318,7 +318,7 @@
       mojom::ControllerServiceWorkerPurpose purpose) override {
     NOTIMPLEMENTED();
   }
-  void CloneForWorker(
+  void CloneContainerHost(
       mojom::ServiceWorkerContainerHostRequest request) override {
     bindings_.AddBinding(this, std::move(request));
   }
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.h b/content/renderer/service_worker/service_worker_subresource_loader.h
index 28359d5..9cb935a0 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.h
+++ b/content/renderer/service_worker/service_worker_subresource_loader.h
@@ -182,9 +182,8 @@
   // default URLLoaderFactory for network fallback. This should be the
   // URLLoaderFactory that directly goes to network without going through
   // any custom URLLoader factories.
-  // |task_runner| is the runner where this loader runs. (We need to pass
-  // this around because calling base::SequencedTaskRunnerHandle is
-  // prohibited in the renderer :()
+  // |task_runner| is the runner where this loader runs. In production it runs,
+  // on a background thread.
   static void Create(
       scoped_refptr<ControllerServiceWorkerConnector> controller_connector,
       scoped_refptr<network::SharedURLLoaderFactory> fallback_factory,
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 46abdd0..2b98b15 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -4,6 +4,11 @@
 
 #include "content/renderer/service_worker/service_worker_subresource_loader.h"
 
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -377,7 +382,7 @@
       return;
     fake_controller_->Clone(std::move(request));
   }
-  void CloneForWorker(
+  void CloneContainerHost(
       mojom::ServiceWorkerContainerHostRequest request) override {
     bindings_.AddBinding(this, std::move(request));
   }
@@ -432,7 +437,8 @@
   network::mojom::URLLoaderFactoryPtr CreateSubresourceLoaderFactory() {
     if (!connector_) {
       mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info;
-      fake_container_host_.CloneForWorker(mojo::MakeRequest(&host_ptr_info));
+      fake_container_host_.CloneContainerHost(
+          mojo::MakeRequest(&host_ptr_info));
       connector_ = base::MakeRefCounted<ControllerServiceWorkerConnector>(
           std::move(host_ptr_info), nullptr /*controller_ptr*/,
           "" /*client_id*/);
diff --git a/content/renderer/v8_value_converter_impl.cc b/content/renderer/v8_value_converter_impl.cc
index 1afe31ed3..730b660 100644
--- a/content/renderer/v8_value_converter_impl.cc
+++ b/content/renderer/v8_value_converter_impl.cc
@@ -95,7 +95,7 @@
   // hash (key) in the map, because two objects can have the same hash.
   bool AddToUniquenessCheck(v8::Local<v8::Object> handle) {
     int hash;
-    Iterator iter = GetIteratorInMap(handle, &hash);
+    auto iter = GetIteratorInMap(handle, &hash);
     if (iter != unique_map_.end())
       return false;
 
@@ -105,7 +105,7 @@
 
   bool RemoveFromUniquenessCheck(v8::Local<v8::Object> handle) {
     int unused_hash;
-    Iterator iter = GetIteratorInMap(handle, &unused_hash);
+    auto iter = GetIteratorInMap(handle, &unused_hash);
     if (iter == unique_map_.end())
       return false;
     unique_map_.erase(iter);
@@ -126,7 +126,7 @@
     // hash. Different hash obviously means different objects, but two objects
     // in a couple of thousands could have the same identity hash.
     std::pair<Iterator, Iterator> range = unique_map_.equal_range(*hash);
-    for (Iterator it = range.first; it != range.second; ++it) {
+    for (auto it = range.first; it != range.second; ++it) {
       // Operator == for handles actually compares the underlying objects.
       if (it->second == handle)
         return it;
diff --git a/content/renderer/web_ui_extension_data.cc b/content/renderer/web_ui_extension_data.cc
index 43cf15e..9d8c5ed 100644
--- a/content/renderer/web_ui_extension_data.cc
+++ b/content/renderer/web_ui_extension_data.cc
@@ -18,8 +18,7 @@
 }
 
 std::string WebUIExtensionData::GetValue(const std::string& key) const {
-  std::map<std::string, std::string>::const_iterator it =
-      variable_map_.find(key);
+  auto it = variable_map_.find(key);
   if (it == variable_map_.end())
     return std::string();
   return it->second;
diff --git a/content/renderer/worker_thread_registry.cc b/content/renderer/worker_thread_registry.cc
index e5204cd..1984cd2 100644
--- a/content/renderer/worker_thread_registry.cc
+++ b/content/renderer/worker_thread_registry.cc
@@ -129,7 +129,7 @@
 bool WorkerThreadRegistry::PostTask(int id, base::OnceClosure closure) {
   DCHECK(id > 0);
   base::AutoLock locker(task_runner_map_lock_);
-  IDToTaskRunnerMap::iterator found = task_runner_map_.find(id);
+  auto found = task_runner_map_.find(id);
   if (found == task_runner_map_.end())
     return false;
   return found->second->PostTask(FROM_HERE, std::move(closure));
diff --git a/content/test/data/accessibility/html/audio-expected-auralinux.txt b/content/test/data/accessibility/html/audio-expected-auralinux.txt
index a0d4367..1c70d30e 100644
--- a/content/test/data/accessibility/html/audio-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/audio-expected-auralinux.txt
@@ -4,8 +4,8 @@
 ++++++[section]
 ++++++++[tool bar] name='audio' description='audio' horizontal
 ++++++++++[tool bar] name='audio' description='audio' horizontal
-++++++++++++[push button] name='play' description='begin playback' xml-roles:button
+++++++++++++[push button] name='play' xml-roles:button
 ++++++++++++[text] name='0:00'
 ++++++++++++[text] name='/ 0:00'
 ++++++++++++[slider] description='audio time scrubber' horizontal xml-roles:slider
-++++++++++++[push button] name='mute' description='mute audio track' xml-roles:button
+++++++++++++[push button] name='mute' xml-roles:button
diff --git a/content/test/data/accessibility/html/source-expected-auralinux.txt b/content/test/data/accessibility/html/source-expected-auralinux.txt
index da6f0cd..ab0b222 100644
--- a/content/test/data/accessibility/html/source-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/source-expected-auralinux.txt
@@ -4,8 +4,8 @@
 ++++++[section]
 ++++++++[tool bar] name='audio' description='audio' horizontal
 ++++++++++[tool bar] name='audio' description='audio' horizontal
-++++++++++++[push button] name='play' description='begin playback' focusable
+++++++++++++[push button] name='play' focusable
 ++++++++++++[text] name='0:00'
 ++++++++++++[text] name='/ 0:00'
 ++++++++++++[slider] description='audio time scrubber' focusable horizontal
-++++++++++++[push button] name='mute' description='mute audio track'
+++++++++++++[push button] name='mute'
diff --git a/content/test/gpu/gpu_tests/power_measurement_integration_test.py b/content/test/gpu/gpu_tests/power_measurement_integration_test.py
index 64452ff8..14d8ad0 100644
--- a/content/test/gpu/gpu_tests/power_measurement_integration_test.py
+++ b/content/test/gpu/gpu_tests/power_measurement_integration_test.py
@@ -47,13 +47,34 @@
     return rt;
   }
 
-  // Return true if video has started playing.
-  function setupVideoElement() {
+  /**
+   * Set up the video element for testing.
+   * @param {boolean} force_underlay - Whether to add a layer on top so
+   *   the video layer becomes an underlay.
+   * @returns {boolean} true if video has started playing.
+   */
+  function setupVideoElement(force_underlay) {
     var video = locateElement("video");
     if (video) {
       video.muted = true;
       video.loop = true;
       video.autoplay = true;
+      if (force_underlay) {
+        var layer = document.createElement("div");
+        layer.style.border = "thick solid rgb(0,0,255)";
+        layer.style.backgroundColor = "red";
+        layer.style.width = "100px";
+        layer.style.height = "50px";
+        layer.style.position = "absolute";
+        layer.style.zIndex = "1000";
+        var vid_rect = video.getBoundingClientRect();
+        var parent_rect = video.parentNode.getBoundingClientRect();
+        var top = vid_rect.top - parent_rect.top;
+        var left = vid_rect.left - parent_rect.left;
+        layer.style.top = top.toString() + "px";
+        layer.style.left = left.toString() + "px";
+        video.parentNode.appendChild(layer);
+      }
       return video.currentTime > 0;
     }
     return false;
@@ -125,6 +146,9 @@
                       help="specify if the browser goes to fullscreen mode "
                       "automatically, specifically if there is a single video "
                       "element in the page, switch it to fullsrceen mode.")
+    parser.add_option("--underlay", action="store_true", default=False,
+                      help="add a layer on top so the video layer becomes an "
+                      "underlay.")
     parser.add_option("--logdir",
                       help="Speficy where the Intel Power Gadget log file "
                       "should be stored. If specified, the log file name will "
@@ -145,6 +169,7 @@
     yield ('url', options.url, (options.repeat,
                                 options.outliers,
                                 options.fullscreen,
+                                options.underlay,
                                 options.logdir,
                                 options.duration,
                                 options.delay,
@@ -166,10 +191,11 @@
     repeat = args[0]
     outliers = args[1]
     fullscreen = args[2]
-    ipg_logdir = args[3]
-    ipg_duration = args[4]
-    ipg_delay = args[5]
-    ipg_resolution = args[6]
+    underlay = args[3]
+    ipg_logdir = args[4]
+    ipg_duration = args[5]
+    ipg_delay = args[6]
+    ipg_resolution = args[7]
 
     print ""
     print "Total iterations: ", repeat
@@ -180,7 +206,8 @@
       if test_path:
         self.tab.action_runner.Navigate(test_path, fullscreen_script)
         self.tab.WaitForDocumentReadyStateToBeComplete()
-        if not self.tab.action_runner.EvaluateJavaScript('setupVideoElement()'):
+        code = "setupVideoElement(%s)" % ("true" if underlay else "false")
+        if not self.tab.action_runner.EvaluateJavaScript(code):
           # autoplay doesn't work for vimeo.
           # action_runner.PlayMedia doesn't work for vimeo.
           self.tab.action_runner.TapElement(element_function=(
diff --git a/docs/README.md b/docs/README.md
index ffe325ae..f9e5b0f 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -134,8 +134,6 @@
     the window system and low level input and graphics.
 *   [Optimizing Chrome Web UIs](optimizing_web_uis.md) - Notes on making webuis
     more performant
-*   [ES6 Support in Chromium](es6_chromium.md) - Implementation of ECMAScript6
-    features in Chromium
 *   [Adding a new feature flag in chrome://flags](how_to_add_your_feature_flag.md) - Quick
     guide to add a new feature flag to experiment your feature.
 *   [Guidelines for considering branch dates in project planning](release_branch_guidance.md) -
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc
index 9cefafca..7508092b 100644
--- a/extensions/browser/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker_task_queue.cc
@@ -70,8 +70,6 @@
         thread_id(thread_id) {}
 
   WaitingDidStartWorkerTask(WaitingDidStartWorkerTask&& other) = default;
-  WaitingDidStartWorkerTask& operator=(WaitingDidStartWorkerTask&& other) =
-      default;
 
   LazyContextTaskQueue::PendingTask task;
   const ExtensionId extension_id;
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 3200bd1..321f3ec 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -17,7 +17,6 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h"
 #include "gpu/command_buffer/service/path_manager.h"
 #include "gpu/command_buffer/service/program_manager.h"
-#include "gpu/command_buffer/service/progress_reporter.h"
 #include "gpu/command_buffer/service/renderbuffer_manager.h"
 #include "gpu/command_buffer/service/sampler_manager.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
@@ -27,6 +26,7 @@
 #include "gpu/config/gpu_preferences.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_version_info.h"
+#include "ui/gl/progress_reporter.h"
 
 namespace gpu {
 namespace gles2 {
@@ -70,7 +70,7 @@
     bool bind_generates_resource,
     ImageManager* image_manager,
     gpu::ImageFactory* image_factory,
-    ProgressReporter* progress_reporter,
+    gl::ProgressReporter* progress_reporter,
     const GpuFeatureInfo& gpu_feature_info,
     ServiceDiscardableManager* discardable_manager)
     : gpu_preferences_(gpu_preferences),
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 5e47d38f..c119b43f 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -24,6 +24,10 @@
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/gpu_gles2_export.h"
 
+namespace gl {
+class ProgressReporter;
+}
+
 namespace gpu {
 
 class ImageFactory;
@@ -41,7 +45,6 @@
 class RenderbufferManager;
 class PathManager;
 class ProgramManager;
-class ProgressReporter;
 class SamplerManager;
 class ShaderManager;
 class TextureManager;
@@ -67,7 +70,7 @@
                bool bind_generates_resource,
                ImageManager* image_manager,
                gpu::ImageFactory* image_factory,
-               ProgressReporter* progress_reporter,
+               gl::ProgressReporter* progress_reporter,
                const GpuFeatureInfo& gpu_feature_info,
                ServiceDiscardableManager* discardable_manager);
 
@@ -313,7 +316,7 @@
   // Used to notify the watchdog thread of progress during destruction,
   // preventing time-outs when destruction takes a long time. May be null when
   // using in-process command buffer.
-  ProgressReporter* progress_reporter_;
+  gl::ProgressReporter* progress_reporter_;
 
   GpuFeatureInfo gpu_feature_info_;
 
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 5fc3e24..2947d0c 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -28,11 +28,11 @@
 #include "gpu/command_buffer/service/decoder_context.h"
 #include "gpu/command_buffer/service/feature_info.h"
 #include "gpu/command_buffer/service/program_cache.h"
-#include "gpu/command_buffer/service/progress_reporter.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 #include "gpu/config/gpu_preferences.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "ui/gl/gl_version_info.h"
+#include "ui/gl/progress_reporter.h"
 
 using base::TimeDelta;
 using base::TimeTicks;
@@ -2597,7 +2597,7 @@
                                uint32_t max_vertex_attribs,
                                const GpuPreferences& gpu_preferences,
                                FeatureInfo* feature_info,
-                               ProgressReporter* progress_reporter)
+                               gl::ProgressReporter* progress_reporter)
     : program_count_(0),
       have_context_(true),
       program_cache_(program_cache),
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 77f79eb..7bc019fa 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -652,7 +652,7 @@
                  uint32_t max_vertex_attribs,
                  const GpuPreferences& gpu_preferences,
                  FeatureInfo* feature_info,
-                 ProgressReporter* progress_reporter);
+                 gl::ProgressReporter* progress_reporter);
   ~ProgramManager();
 
   // Must call before destruction.
@@ -737,7 +737,7 @@
   // Used to notify the watchdog thread of progress during destruction,
   // preventing time-outs when destruction takes a long time. May be null when
   // using in-process command buffer.
-  ProgressReporter* progress_reporter_;
+  gl::ProgressReporter* progress_reporter_;
 
   DISALLOW_COPY_AND_ASSIGN(ProgramManager);
 };
diff --git a/gpu/command_buffer/service/progress_reporter.h b/gpu/command_buffer/service/progress_reporter.h
deleted file mode 100644
index 926e4f8..0000000
--- a/gpu/command_buffer/service/progress_reporter.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GPU_COMMAND_BUFFER_SERVICE_PROGRESS_REPORTER_H_
-#define GPU_COMMAND_BUFFER_SERVICE_PROGRESS_REPORTER_H_
-
-#include "gpu/gpu_gles2_export.h"
-
-namespace gpu {
-namespace gles2 {
-
-// ProgressReporter is used by ContextGroup to report when it is making forward
-// progress in execution, delaying activation of the watchdog timeout.
-class GPU_GLES2_EXPORT ProgressReporter {
- public:
-  virtual ~ProgressReporter() = default;
-
-  virtual void ReportProgress() = 0;
-};
-
-}  // namespace gles2
-}  // namespace gpu
-
-#endif  // GPU_COMMAND_BUFFER_SERVICE_PROGRESS_REPORTER_H_
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.cc b/gpu/command_buffer/service/raster_decoder_context_state.cc
index ef4ada86..9b12cb99 100644
--- a/gpu/command_buffer/service/raster_decoder_context_state.cc
+++ b/gpu/command_buffer/service/raster_decoder_context_state.cc
@@ -43,11 +43,13 @@
 void RasterDecoderContextState::InitializeGrContext(
     const GpuDriverBugWorkarounds& workarounds,
     GrContextOptions::PersistentCache* cache,
-    GpuProcessActivityFlags* activity_flags) {
+    GpuProcessActivityFlags* activity_flags,
+    gl::ProgressReporter* progress_reporter) {
   DCHECK(context->IsCurrent(surface.get()));
 
   sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface(
-      *context->GetVersionInfo(), workarounds.use_es2_for_oopr));
+      *context->GetVersionInfo(), workarounds.use_es2_for_oopr,
+      progress_reporter));
   if (!interface) {
     LOG(ERROR) << "OOP raster support disabled: GrGLInterface creation "
                   "failed.";
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.h b/gpu/command_buffer/service/raster_decoder_context_state.h
index f6bb7d5..28321e47 100644
--- a/gpu/command_buffer/service/raster_decoder_context_state.h
+++ b/gpu/command_buffer/service/raster_decoder_context_state.h
@@ -11,6 +11,7 @@
 #include "gpu/command_buffer/common/skia_utils.h"
 #include "gpu/gpu_gles2_export.h"
 #include "third_party/skia/include/gpu/GrContext.h"
+#include "ui/gl/progress_reporter.h"
 
 namespace gl {
 class GLContext;
@@ -35,7 +36,8 @@
                             bool use_virtualized_gl_contexts);
   void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds,
                            GrContextOptions::PersistentCache* cache,
-                           GpuProcessActivityFlags* activity_flags = nullptr);
+                           GpuProcessActivityFlags* activity_flags = nullptr,
+                           gl::ProgressReporter* progress_reporter = nullptr);
   void PurgeMemory(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 4889a7f..44f5b2d 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -10,7 +10,7 @@
 
 #include "base/logging.h"
 #include "base/strings/string_util.h"
-#include "gpu/command_buffer/service/progress_reporter.h"
+#include "ui/gl/progress_reporter.h"
 
 namespace gpu {
 namespace gles2 {
@@ -277,7 +277,7 @@
   return nullptr;
 }
 
-ShaderManager::ShaderManager(ProgressReporter* progress_reporter)
+ShaderManager::ShaderManager(gl::ProgressReporter* progress_reporter)
     : progress_reporter_(progress_reporter) {}
 
 ShaderManager::~ShaderManager() {
diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h
index f36aa32..579dddd 100644
--- a/gpu/command_buffer/service/shader_manager.h
+++ b/gpu/command_buffer/service/shader_manager.h
@@ -15,11 +15,13 @@
 #include "gpu/command_buffer/service/shader_translator.h"
 #include "gpu/gpu_gles2_export.h"
 
+namespace gl {
+class ProgressReporter;
+}
+
 namespace gpu {
 namespace gles2 {
 
-class ProgressReporter;
-
 enum ShaderVariableBaseType {
   SHADER_VARIABLE_INT = 0x01,
   SHADER_VARIABLE_UINT = 0x02,
@@ -279,7 +281,7 @@
 // need to be shared by multiple GLES2Decoders.
 class GPU_GLES2_EXPORT ShaderManager {
  public:
-  ShaderManager(ProgressReporter* progress_reporter);
+  ShaderManager(gl::ProgressReporter* progress_reporter);
   ~ShaderManager();
 
   // Must call before destruction.
@@ -322,7 +324,7 @@
   // Used to notify the watchdog thread of progress during destruction,
   // preventing time-outs when destruction takes a long time. May be null when
   // using in-process command buffer.
-  ProgressReporter* progress_reporter_;
+  gl::ProgressReporter* progress_reporter_;
 
   DISALLOW_COPY_AND_ASSIGN(ShaderManager);
 };
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 0556e350..9bb41247 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -28,12 +28,12 @@
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
-#include "gpu/command_buffer/service/progress_reporter.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_state_restorer.h"
 #include "ui/gl/gl_version_info.h"
+#include "ui/gl/progress_reporter.h"
 #include "ui/gl/trace_util.h"
 
 using base::trace_event::MemoryAllocatorDump;
@@ -1957,7 +1957,7 @@
                                GLint max_3d_texture_size,
                                GLint max_array_texture_layers,
                                bool use_default_textures,
-                               ProgressReporter* progress_reporter,
+                               gl::ProgressReporter* progress_reporter,
                                ServiceDiscardableManager* discardable_manager)
     : memory_type_tracker_(new MemoryTypeTracker(memory_tracker)),
       memory_tracker_(memory_tracker),
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 92777837..99fe91a 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -28,6 +28,10 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gl/gl_image.h"
 
+namespace gl {
+class ProgressReporter;
+}
+
 namespace gpu {
 class DecoderContext;
 class ServiceDiscardableManager;
@@ -40,7 +44,6 @@
 class ErrorState;
 class FeatureInfo;
 class FramebufferManager;
-class ProgressReporter;
 class Texture;
 class TextureManager;
 class TextureRef;
@@ -754,7 +757,7 @@
                  GLsizei max_3d_texture_size,
                  GLsizei max_array_texture_layers,
                  bool use_default_textures,
-                 ProgressReporter* progress_reporter,
+                 gl::ProgressReporter* progress_reporter,
                  ServiceDiscardableManager* discardable_manager);
   ~TextureManager() override;
 
@@ -1247,7 +1250,7 @@
   // Used to notify the watchdog thread of progress during destruction,
   // preventing time-outs when destruction takes a long time. May be null when
   // using in-process command buffer.
-  ProgressReporter* progress_reporter_;
+  gl::ProgressReporter* progress_reporter_;
 
   ServiceDiscardableManager* discardable_manager_;
 
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index b5afb09..1eb57c4 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -236,8 +236,10 @@
                                  OverlayFormatToString(info.overlay_format),
                              info.flags);
   }
-  UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.OverlayFormatUsed",
-                            g_overlay_format_used);
+  if (g_supports_overlays) {
+    UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.OverlayFormatUsed2",
+                              g_overlay_format_used);
+  }
   UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.OverlaysSupported",
                         g_supports_overlays);
 }
@@ -735,8 +737,10 @@
     return false;
   }
 
-  UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.SwapchainFormat",
-                        is_yuv_swapchain_);
+  UMA_HISTOGRAM_ENUMERATION(
+      "GPU.DirectComposition.SwapChainFormat2",
+      is_yuv_swapchain_ ? g_overlay_format_used : OverlayFormat::kBGRA);
+
   frames_since_color_space_change_++;
 
   Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
@@ -943,6 +947,8 @@
 
   frames_since_color_space_change_ = 0;
 
+  const std::string kSwapChainCreationResultUmaPrefix =
+      "GPU.DirectComposition.SwapChainCreationResult.";
   is_yuv_swapchain_ = false;
   // The composition surface handle isn't actually used, but
   // CreateSwapChainForComposition can't create YUV swapchains.
@@ -952,6 +958,9 @@
         swap_chain_.GetAddressOf());
     is_yuv_swapchain_ = SUCCEEDED(hr);
     failed_to_create_yuv_swapchain_ = !is_yuv_swapchain_;
+    base::UmaHistogramBoolean(kSwapChainCreationResultUmaPrefix +
+                                  OverlayFormatToString(g_overlay_format_used),
+                              SUCCEEDED(hr));
     if (FAILED(hr)) {
       DLOG(ERROR) << "Failed to create "
                   << OverlayFormatToString(g_overlay_format_used)
@@ -970,6 +979,9 @@
     HRESULT hr = media_factory->CreateSwapChainForCompositionSurfaceHandle(
         d3d11_device_.Get(), swap_chain_handle_.Get(), &desc, nullptr,
         swap_chain_.GetAddressOf());
+    base::UmaHistogramBoolean(kSwapChainCreationResultUmaPrefix +
+                                  OverlayFormatToString(OverlayFormat::kBGRA),
+                              SUCCEEDED(hr));
     if (FAILED(hr)) {
       DLOG(ERROR) << "Failed to create BGRA swap chain with error " << std::hex
                   << hr;
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 9237b226..1ed58cf 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -31,6 +31,7 @@
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager_delegate.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+#include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_share_group.h"
@@ -422,7 +423,8 @@
       gpu::kGpuFeatureStatusEnabled;
   if (enable_raster_transport) {
     raster_decoder_context_state_->InitializeGrContext(
-        gpu_driver_bug_workarounds_, gr_shader_cache(), &activity_flags_);
+        gpu_driver_bug_workarounds_, gr_shader_cache(), &activity_flags_,
+        watchdog_);
   }
 
   gr_cache_controller_.emplace(raster_decoder_context_state_.get(),
diff --git a/gpu/ipc/service/gpu_watchdog_thread.h b/gpu/ipc/service/gpu_watchdog_thread.h
index 1d2e14a..a7896770 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.h
+++ b/gpu/ipc/service/gpu_watchdog_thread.h
@@ -14,9 +14,9 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "gpu/command_buffer/service/progress_reporter.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gl/progress_reporter.h"
 
 #if defined(USE_X11)
 #include <sys/poll.h>
@@ -28,10 +28,9 @@
 
 // A thread that intermitently sends tasks to a group of watched message loops
 // and deliberately crashes if one of them does not respond after a timeout.
-class GPU_IPC_SERVICE_EXPORT GpuWatchdogThread
-    : public base::Thread,
-      public base::PowerObserver,
-      public gles2::ProgressReporter {
+class GPU_IPC_SERVICE_EXPORT GpuWatchdogThread : public base::Thread,
+                                                 public base::PowerObserver,
+                                                 public gl::ProgressReporter {
  public:
   ~GpuWatchdogThread() override;
 
@@ -43,7 +42,7 @@
   // any thread.
   void AddPowerObserver();
 
-  // gles2::ProgressReporter implementation:
+  // gl::ProgressReporter implementation:
   void ReportProgress() override;
 
   // Notifies the watchdog when Chrome is backgrounded / foregrounded. Should
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 4159f2d..f0e0c9d 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -721,11 +721,6 @@
     builder.SetProxyConfig(std::move(proxy_config));
   }
 
-  if (command_line.HasSwitch(::network::switches::kHostResolverRules)) {
-    builder.SetHostResolverRules(command_line.GetSwitchValueASCII(
-        ::network::switches::kHostResolverRules));
-  }
-
   if (command_line.HasSwitch(switches::kUseGL)) {
     builder.SetGLImplementation(
         command_line.GetSwitchValueASCII(switches::kUseGL));
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index d90b6a6..6cc1e61 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -405,13 +405,6 @@
   return *this;
 }
 
-HeadlessBrowserContext::Builder&
-HeadlessBrowserContext::Builder::SetHostResolverRules(
-    const std::string& host_resolver_rules) {
-  options_->host_resolver_rules_ = host_resolver_rules;
-  return *this;
-}
-
 HeadlessBrowserContext::Builder& HeadlessBrowserContext::Builder::SetWindowSize(
     const gfx::Size& window_size) {
   options_->window_size_ = window_size;
diff --git a/headless/lib/browser/headless_browser_context_options.cc b/headless/lib/browser/headless_browser_context_options.cc
index aedbb0a..e3ab688 100644
--- a/headless/lib/browser/headless_browser_context_options.cc
+++ b/headless/lib/browser/headless_browser_context_options.cc
@@ -52,11 +52,6 @@
   return browser_options_->proxy_config.get();
 }
 
-const std::string& HeadlessBrowserContextOptions::host_resolver_rules() const {
-  return ReturnOverriddenValue(host_resolver_rules_,
-                               browser_options_->host_resolver_rules);
-}
-
 const gfx::Size& HeadlessBrowserContextOptions::window_size() const {
   return ReturnOverriddenValue(window_size_, browser_options_->window_size);
 }
diff --git a/headless/lib/browser/headless_browser_context_options.h b/headless/lib/browser/headless_browser_context_options.h
index a8f3b098..441f884 100644
--- a/headless/lib/browser/headless_browser_context_options.h
+++ b/headless/lib/browser/headless_browser_context_options.h
@@ -34,9 +34,6 @@
   // See HeadlessBrowser::Options::proxy_config.
   const net::ProxyConfig* proxy_config() const;
 
-  // See HeadlessBrowser::Options::host_resolver_rules.
-  const std::string& host_resolver_rules() const;
-
   const gfx::Size& window_size() const;
 
   // See HeadlessBrowser::Options::user_data_dir.
diff --git a/headless/lib/browser/headless_url_request_context_getter.cc b/headless/lib/browser/headless_url_request_context_getter.cc
index 689a154c..efd07c5 100644
--- a/headless/lib/browser/headless_url_request_context_getter.cc
+++ b/headless/lib/browser/headless_url_request_context_getter.cc
@@ -32,6 +32,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
+#include "services/network/public/cpp/network_switches.h"
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 #include "base/command_line.h"
@@ -71,7 +72,6 @@
     : io_task_runner_(std::move(io_task_runner)),
       accept_language_(options->accept_language()),
       user_agent_(options->user_agent()),
-      host_resolver_rules_(options->host_resolver_rules()),
       proxy_config_(options->proxy_config()),
       request_interceptors_(std::move(request_interceptors)),
       user_data_path_(std::move(user_data_path)) {
@@ -87,6 +87,9 @@
   prefs_.SetServerWhitelist(
       command_line->GetSwitchValueASCII(switches::kAuthServerWhitelist));
 
+  host_resolver_rules_ = command_line->GetSwitchValueASCII(
+      ::network::switches::kHostResolverRules);
+
   // We must create the proxy config service on the UI loop on Linux because it
   // must synchronously run on the glib message loop. This will be passed to
   // the URLRequestContextStorage on the IO thread in GetURLRequestContext().
@@ -228,10 +231,6 @@
   return io_task_runner_;
 }
 
-net::HostResolver* HeadlessURLRequestContextGetter::host_resolver() const {
-  return url_request_context_->host_resolver();
-}
-
 void HeadlessURLRequestContextGetter::NotifyContextShuttingDown() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   shut_down_ = true;
diff --git a/headless/lib/browser/headless_url_request_context_getter.h b/headless/lib/browser/headless_url_request_context_getter.h
index 4ea266ad..05aac08d 100644
--- a/headless/lib/browser/headless_url_request_context_getter.h
+++ b/headless/lib/browser/headless_url_request_context_getter.h
@@ -22,7 +22,6 @@
 #include "net/url_request/url_request_job_factory.h"
 
 namespace net {
-class HostResolver;
 class ProxyConfigService;
 }
 
@@ -44,8 +43,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
       const override;
 
-  net::HostResolver* host_resolver() const;
-
   void NotifyContextShuttingDown();
 
  protected:
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc
index 491f2dc..4cd0459 100644
--- a/headless/lib/headless_browser_browsertest.cc
+++ b/headless/lib/headless_browser_browsertest.cc
@@ -231,30 +231,6 @@
   EXPECT_TRUE(browser_context->GetAllWebContents().empty());
 }
 
-IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, SetHostResolverRules) {
-  EXPECT_TRUE(embedded_test_server()->Start());
-
-  std::string host_resolver_rules =
-      base::StringPrintf("MAP not-an-actual-domain.tld 127.0.0.1:%d",
-                         embedded_test_server()->host_port_pair().port());
-
-  HeadlessBrowserContext* browser_context =
-      browser()
-          ->CreateBrowserContextBuilder()
-          .SetHostResolverRules(host_resolver_rules)
-          .Build();
-
-  // Load a page which doesn't actually exist, but which is turned into a valid
-  // address by our host resolver rules.
-  HeadlessWebContents* web_contents =
-      browser_context->CreateWebContentsBuilder()
-          .SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
-          .Build();
-  EXPECT_TRUE(web_contents);
-
-  EXPECT_TRUE(WaitForLoad(web_contents));
-}
-
 IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, WebGLSupported) {
   HeadlessBrowserContext* browser_context =
       browser()->CreateBrowserContextBuilder().Build();
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc
index 22d2dc95..9ad4199 100644
--- a/headless/public/headless_browser.cc
+++ b/headless/public/headless_browser.cc
@@ -99,11 +99,6 @@
   return *this;
 }
 
-Builder& Builder::SetHostResolverRules(const std::string& host_resolver_rules) {
-  options_.host_resolver_rules = host_resolver_rules;
-  return *this;
-}
-
 Builder& Builder::SetSingleProcessMode(bool single_process_mode) {
   options_.single_process_mode = single_process_mode;
   return *this;
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h
index 7b0e30c..130c2ee 100644
--- a/headless/public/headless_browser.h
+++ b/headless/public/headless_browser.h
@@ -161,10 +161,6 @@
   // The ProxyConfig to use. The system proxy settings are used by default.
   std::unique_ptr<net::ProxyConfig> proxy_config = nullptr;
 
-  // Comma-separated list of rules that control how hostnames are mapped. See
-  // chrome::switches::kHostRules for a description for the format.
-  std::string host_resolver_rules;
-
   // Default window size. This is also used to create the window tree host and
   // as initial screen size. Defaults to 800x600.
   gfx::Size window_size;
@@ -257,7 +253,6 @@
   Builder& SetEnableBeginFrameControl(bool enable_begin_frame_control);
   Builder& SetUserAgent(const std::string& user_agent);
   Builder& SetProxyConfig(std::unique_ptr<net::ProxyConfig> proxy_config);
-  Builder& SetHostResolverRules(const std::string& host_resolver_rules);
   Builder& SetWindowSize(const gfx::Size& window_size);
   Builder& SetUserDataDir(const base::FilePath& user_data_dir);
   Builder& SetIncognitoMode(bool incognito_mode);
diff --git a/headless/public/headless_browser_context.h b/headless/public/headless_browser_context.h
index 0a21c55..886e2bb 100644
--- a/headless/public/headless_browser_context.h
+++ b/headless/public/headless_browser_context.h
@@ -102,7 +102,6 @@
   Builder& SetAcceptLanguage(const std::string& accept_language);
   Builder& SetUserAgent(const std::string& user_agent);
   Builder& SetProxyConfig(std::unique_ptr<net::ProxyConfig> proxy_config);
-  Builder& SetHostResolverRules(const std::string& host_resolver_rules);
   Builder& SetWindowSize(const gfx::Size& window_size);
   Builder& SetUserDataDir(const base::FilePath& user_data_dir);
   Builder& SetIncognitoMode(bool incognito_mode);
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index fc46e13..5a25a6047 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -26,6 +26,7 @@
 #include "headless/public/headless_web_contents.h"
 #include "headless/test/headless_browser_test.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
+#include "services/network/public/cpp/network_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/url_util.h"
 
@@ -47,6 +48,13 @@
     EXPECT_TRUE(embedded_test_server()->Start());
   }
 
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(::network::switches::kHostResolverRules,
+                                    "MAP *.test 127.0.0.1");
+    HeadlessAsyncDevTooledBrowserTest::SetUpCommandLine(command_line);
+  }
+
  private:
   // HeadlessWebContentsObserver implementation.
   void DevToolsTargetReady() override {
@@ -173,7 +181,6 @@
     // Make sure the navigations spawn new processes. We run test harness
     // in one process (harness.test) and tests in another.
     builder.SetSitePerProcess(true);
-    builder.SetHostResolverRules("MAP *.test 127.0.0.1");
   }
 
  protected:
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
index c30a780..513dd55 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
@@ -199,9 +199,6 @@
 
   int count = 0;
   for (const BookmarkNode* node : nodes) {
-    if (!node->is_url())
-      continue;
-
     BookmarkHomeNodeItem* nodeItem =
         [[BookmarkHomeNodeItem alloc] initWithType:BookmarkHomeItemTypeBookmark
                                       bookmarkNode:node];
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 070684d..450abda 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -200,7 +200,7 @@
 @property(nonatomic, readonly, weak) id<ApplicationCommands> dispatcher;
 
 // The current search term.  Set to the empty string when no search is active.
-@property(nonatomic, assign) NSString* searchTerm;
+@property(nonatomic, copy) NSString* searchTerm;
 
 // This ViewController's searchController;
 @property(nonatomic, strong) UISearchController* searchController;
@@ -772,6 +772,53 @@
 }
 
 - (void)handleSelectFolderForNavigation:(const bookmarks::BookmarkNode*)folder {
+  if (self.sharedState.currentlyShowingSearchResults) {
+    // Clear bookmark path cache.
+    int64_t unusedFolderId;
+    int unusedIndexPathRow;
+    while ([BookmarkPathCache
+        getBookmarkTopMostRowCacheWithPrefService:self.browserState->GetPrefs()
+                                            model:self.bookmarks
+                                         folderId:&unusedFolderId
+                                       topMostRow:&unusedIndexPathRow]) {
+      [BookmarkPathCache
+          clearBookmarkTopMostRowCacheWithPrefService:self.browserState
+                                                          ->GetPrefs()];
+    }
+
+    // Rebuild folder controller list, going back up the tree.
+    NSMutableArray<BookmarkHomeViewController*>* stack = [NSMutableArray array];
+    std::vector<const bookmarks::BookmarkNode*> nodes;
+    const bookmarks::BookmarkNode* cursor = folder;
+    while (cursor) {
+      // Build reversed list of nodes to restore bookmark path below.
+      nodes.insert(nodes.begin(), cursor);
+
+      // Build reversed list of controllers.
+      BookmarkHomeViewController* controller =
+          [self createControllerWithRootFolder:cursor];
+      [stack insertObject:controller atIndex:0];
+
+      // Setup now, so that the back button labels shows parent folder
+      // title and that we don't show large title everywhere.
+      [self setupNavigationForBookmarkHomeViewController:controller
+                                       usingBookmarkNode:cursor];
+
+      cursor = cursor->parent();
+    }
+
+    // Reconstruct bookmark path cache.
+    for (const bookmarks::BookmarkNode* node : nodes) {
+      [BookmarkPathCache
+          cacheBookmarkTopMostRowWithPrefService:self.browserState->GetPrefs()
+                                        folderId:node->id()
+                                      topMostRow:0];
+    }
+
+    [self navigateAway];
+    [self.navigationController setViewControllers:stack animated:YES];
+    return;
+  }
   BookmarkHomeViewController* controller =
       [self createControllerWithRootFolder:folder];
   [self.navigationController pushViewController:controller animated:YES];
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 957e6af..488e0aa8 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -1729,7 +1729,7 @@
 #pragma mark - BookmarksTestCaseEntries Tests
 
 - (void)testUndoDeleteBookmarkFromSwipe {
-  // TODO(crbug.com/851227): On UIRefresh non Compact Width on iOS11, the
+  // TODO(crbug.com/851227): On Compact Width on iOS11, the
   // bookmark cell is being deleted by grey_swipeFastInDirection.
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
@@ -1781,7 +1781,7 @@
   FLAKY_testSwipeToDeleteDisabledInEditMode
 #endif
 - (void)testSwipeToDeleteDisabledInEditMode {
-  // TODO(crbug.com/851227): On UIRefresh non Compact Width on iOS11, the
+  // TODO(crbug.com/851227): On non Compact Width on iOS11, the
   // bookmark cell is being deleted by grey_swipeFastInDirection.
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
@@ -4115,10 +4115,10 @@
   [[EarlGrey selectElementWithMatcher:SearchIconButton()]
       performAction:grey_typeText(@"o")];
 
-  // Verify that folders are filtered out.
+  // Verify that folders are not filtered out.
   [[EarlGrey
       selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 1")]
-      assertWithMatcher:grey_nil()];
+      assertWithMatcher:grey_notNil()];
 
   // Search 'on'.
   [[EarlGrey selectElementWithMatcher:SearchIconButton()]
@@ -4135,6 +4135,10 @@
   [[EarlGrey
       selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"French URL")]
       assertWithMatcher:grey_nil()];
+  // Verify that folders are not filtered out.
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 1")]
+      assertWithMatcher:grey_nil()];
 
   // Search again for 'ony'.
   [[EarlGrey selectElementWithMatcher:SearchIconButton()]
@@ -4338,9 +4342,57 @@
       assertWithMatcher:grey_notNil()];
 }
 
-// Tests that you can swipe items in search mode.
-- (void)testSearchItemsCanBeSwipedToDelete {
-  // TODO(crbug.com/851227): On UIRefresh non Compact Width on iOS11, the
+// Tests that you can long press and edit a bookmark folder and see edits
+// when going back to search.
+- (void)testSearchLongPressEditOnFolder {
+  [BookmarksTestCase setupStandardBookmarks];
+  [BookmarksTestCase openBookmarks];
+  [BookmarksTestCase openMobileBookmarks];
+
+  NSString* existingFolderTitle = @"Folder 1.1";
+
+  // Search.
+  [[EarlGrey selectElementWithMatcher:SearchIconButton()]
+      performAction:grey_typeText(existingFolderTitle)];
+
+  // Invoke Edit through long press.
+  [[EarlGrey selectElementWithMatcher:TappableBookmarkNodeWithLabel(
+                                          existingFolderTitle)]
+      performAction:grey_longPress()];
+
+  [[EarlGrey
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_IOS_BOOKMARK_CONTEXT_MENU_EDIT_FOLDER)]
+      performAction:grey_tap()];
+
+  // Verify that the editor is present.
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kBookmarkFolderEditViewContainerIdentifier)]
+      assertWithMatcher:grey_notNil()];
+
+  NSString* newFolderTitle = @"n7";
+  [BookmarksTestCase renameBookmarkFolderWithFolderTitle:newFolderTitle];
+
+  [[EarlGrey selectElementWithMatcher:BookmarksSaveEditFolderButton()]
+      performAction:grey_tap()];
+
+  // Verify that the change has been made.
+  [[EarlGrey selectElementWithMatcher:TappableBookmarkNodeWithLabel(
+                                          existingFolderTitle)]
+      assertWithMatcher:grey_nil()];
+
+  [[EarlGrey selectElementWithMatcher:SearchIconButton()]
+      performAction:grey_replaceText(newFolderTitle)];
+
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(newFolderTitle)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that you can swipe URL items in search mode.
+- (void)testSearchUrlCanBeSwipedToDelete {
+  // TODO(crbug.com/851227): On non Compact Width on iOS11, the
   // bookmark cell is being deleted by grey_swipeFastInDirection.
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
@@ -4357,7 +4409,7 @@
 
   // Search.
   [[EarlGrey selectElementWithMatcher:SearchIconButton()]
-      performAction:grey_typeText(@"F")];
+      performAction:grey_typeText(@"First URL")];
 
   [[EarlGrey
       selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"First URL")]
@@ -4368,6 +4420,36 @@
       assertWithMatcher:grey_notNil()];
 }
 
+// Tests that you can swipe folders in search mode.
+- (void)testSearchFolderCanBeSwipedToDelete {
+  // TODO(crbug.com/851227): On non Compact Width on iOS11, the
+  // bookmark cell is being deleted by grey_swipeFastInDirection.
+  // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
+  // fail on devices. Disabling this test under these conditions on the
+  // meantime.
+  if (@available(iOS 11, *)) {
+    if (!IsCompactWidth()) {
+      EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad on iOS11.");
+    }
+  }
+
+  [BookmarksTestCase setupStandardBookmarks];
+  [BookmarksTestCase openBookmarks];
+  [BookmarksTestCase openMobileBookmarks];
+
+  // Search.
+  [[EarlGrey selectElementWithMatcher:SearchIconButton()]
+      performAction:grey_typeText(@"Folder 1")];
+
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 1")]
+      performAction:grey_swipeFastInDirection(kGREYDirectionLeft)];
+
+  // Verify we have a delete button.
+  [[EarlGrey selectElementWithMatcher:BookmarksDeleteSwipeButton()]
+      assertWithMatcher:grey_notNil()];
+}
+
 // Tests that you can't search while in edit mode.
 - (void)testDisablesSearchOnEditMode {
   [BookmarksTestCase setupStandardBookmarks];
@@ -4633,4 +4715,44 @@
       assertWithMatcher:grey_nil()];
 }
 
+// Tests that you can search folders.
+- (void)testSearchFolders {
+  [BookmarksTestCase setupStandardBookmarks];
+  [BookmarksTestCase openBookmarks];
+  [BookmarksTestCase openMobileBookmarks];
+
+  // Go down Folder 1 / Folder 2 / Folder 3.
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 1")]
+      performAction:grey_tap()];
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 2")]
+      performAction:grey_tap()];
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 3")]
+      performAction:grey_tap()];
+
+  // Search and go to folder 1.1.
+  [[EarlGrey selectElementWithMatcher:SearchIconButton()]
+      performAction:grey_typeText(@"Folder 1.1")];
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 1.1")]
+      performAction:grey_tap()];
+
+  // Go back and verify we are in MobileBooknarks. (i.e. not back to Folder 2)
+  [[EarlGrey selectElementWithMatcher:NavigateBackButtonTo(@"Mobile Bookmarks")]
+      performAction:grey_tap()];
+
+  // Search and go to Folder 2.
+  [[EarlGrey selectElementWithMatcher:SearchIconButton()]
+      performAction:grey_typeText(@"Folder 2")];
+  [[EarlGrey
+      selectElementWithMatcher:TappableBookmarkNodeWithLabel(@"Folder 2")]
+      performAction:grey_tap()];
+
+  // Go back and verify we are in Folder 1. (i.e. not back to Mobile Bookmarks)
+  [[EarlGrey selectElementWithMatcher:NavigateBackButtonTo(@"Folder 1")]
+      performAction:grey_tap()];
+}
+
 @end
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 253f3fd..c6d0d94 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1131,10 +1131,12 @@
 
   gfx::Rect gfx_rect(rect);
   Context3D context_3d;
+  gpu::ContextSupport* context_support = nullptr;
   if (video_frame.get() && video_frame->HasTextures()) {
     if (context_provider_) {
       context_3d = Context3D(context_provider_->ContextGL(),
                              context_provider_->GrContext());
+      context_support = context_provider_->ContextSupport();
     }
     if (!context_3d.gl)
       return;  // Unable to get/create a shared main thread context.
@@ -1152,7 +1154,8 @@
   }
   video_renderer_.Paint(
       video_frame, canvas, gfx::RectF(gfx_rect), flags,
-      pipeline_metadata_.video_decoder_config.video_rotation(), context_3d);
+      pipeline_metadata_.video_decoder_config.video_rotation(), context_3d,
+      context_support);
 }
 
 bool WebMediaPlayerImpl::DidGetOpaqueResponseFromServiceWorker() const {
@@ -1238,13 +1241,15 @@
   }
 
   Context3D context_3d;
+  gpu::ContextSupport* context_support = nullptr;
   if (context_provider_) {
     context_3d = Context3D(context_provider_->ContextGL(),
                            context_provider_->GrContext());
+    context_support = context_provider_->ContextSupport();
   }
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, gl, video_frame.get(), target, texture, internal_format,
-      format, type, level, premultiply_alpha, flip_y);
+      context_3d, context_support, gl, video_frame.get(), target, texture,
+      internal_format, format, type, level, premultiply_alpha, flip_y);
 }
 
 // static
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 1894ca4..8c3d681 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -106,7 +106,9 @@
     Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder) {
   if (isVP9(config)) {
     accelerated_video_decoder_ =
-        std::make_unique<VP9Decoder>(std::make_unique<D3D11VP9Accelerator>());
+        std::make_unique<VP9Decoder>(std::make_unique<D3D11VP9Accelerator>(
+            this, media_log_.get(), video_decoder, video_device_,
+            video_context_));
     return;
   }
 
diff --git a/media/gpu/windows/d3d11_vp9_accelerator.cc b/media/gpu/windows/d3d11_vp9_accelerator.cc
index e910c50..52ee2a1 100644
--- a/media/gpu/windows/d3d11_vp9_accelerator.cc
+++ b/media/gpu/windows/d3d11_vp9_accelerator.cc
@@ -4,18 +4,303 @@
 
 #include "media/gpu/windows/d3d11_vp9_accelerator.h"
 
+#include <windows.h>
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "media/gpu/windows/d3d11_vp9_picture.h"
+
 namespace media {
 
-D3D11VP9Accelerator::D3D11VP9Accelerator() {
-  // Pass
-}
+#define RECORD_FAILURE(expr_name, expr_value)           \
+  do {                                                  \
+    media_log_->AddEvent(media_log_->CreateStringEvent( \
+        MediaLogEvent::MEDIA_ERROR_LOG_ENTRY, "error",  \
+        std::string("DX11VP9Failure(") + expr_name +    \
+            ")=" + std::to_string(expr_value)));        \
+  } while (0)
 
-D3D11VP9Accelerator::~D3D11VP9Accelerator() {
-  // Pass
-}
+#define RETURN_ON_HR_FAILURE(expr_name, expr) \
+  do {                                        \
+    HRESULT expr_value = (expr);              \
+    if (FAILED(expr_value)) {                 \
+      RECORD_FAILURE(#expr_name, expr_value); \
+      return false;                           \
+    }                                         \
+  } while (0)
+
+D3D11VP9Accelerator::D3D11VP9Accelerator(
+    D3D11VideoDecoderClient* client,
+    MediaLog* media_log,
+    Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder,
+    Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
+    Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context)
+    : client_(client),
+      media_log_(media_log),
+      status_feedback_(0),
+      video_decoder_(std::move(video_decoder)),
+      video_device_(std::move(video_device)),
+      video_context_(std::move(video_context)) {}
+
+D3D11VP9Accelerator::~D3D11VP9Accelerator() {}
 
 scoped_refptr<VP9Picture> D3D11VP9Accelerator::CreateVP9Picture() {
-  return nullptr;
+  D3D11PictureBuffer* picture_buffer = client_->GetPicture();
+  if (!picture_buffer)
+    return nullptr;
+  return base::MakeRefCounted<D3D11VP9Picture>(picture_buffer);
+}
+
+bool D3D11VP9Accelerator::BeginFrame(D3D11VP9Picture* pic) {
+  Microsoft::WRL::ComPtr<ID3D11VideoDecoderOutputView> output_view;
+  D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {
+      .DecodeProfile = D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
+      .ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D,
+      .Texture2D = {.ArraySlice = (UINT)pic->level()}};
+
+  RETURN_ON_HR_FAILURE(CreateVideoDecoderOutputView,
+                       video_device_->CreateVideoDecoderOutputView(
+                           pic->picture_buffer()->texture().Get(), &view_desc,
+                           output_view.GetAddressOf()));
+
+  HRESULT hr;
+  do {
+    hr = video_context_->DecoderBeginFrame(video_decoder_.Get(),
+                                           output_view.Get(), 0, nullptr);
+  } while (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING);
+
+  if (FAILED(hr)) {
+    RECORD_FAILURE("DecoderBeginFrame", hr);
+    return false;
+  }
+
+  return true;
+}
+
+void D3D11VP9Accelerator::CopyFrameParams(
+    const scoped_refptr<D3D11VP9Picture>& pic,
+    DXVA_PicParams_VP9* pic_params) {
+#define SET_PARAM(a, b) pic_params->a = pic->frame_hdr->b
+#define COPY_PARAM(a) SET_PARAM(a, a)
+
+  COPY_PARAM(profile);
+  COPY_PARAM(show_frame);
+  COPY_PARAM(error_resilient_mode);
+  COPY_PARAM(refresh_frame_context);
+  COPY_PARAM(frame_parallel_decoding_mode);
+  COPY_PARAM(intra_only);
+  COPY_PARAM(frame_context_idx);
+  COPY_PARAM(allow_high_precision_mv);
+  COPY_PARAM(refresh_frame_context);
+  COPY_PARAM(frame_parallel_decoding_mode);
+  COPY_PARAM(intra_only);
+  COPY_PARAM(frame_context_idx);
+  COPY_PARAM(allow_high_precision_mv);
+
+  // extra_plane, BitDepthMinus8Luma, and BitDepthMinus8Chroma are initialized
+  // at 0 already.
+
+  pic_params->CurrPic.Index7Bits = pic->level();
+  pic_params->frame_type = !pic->frame_hdr->IsKeyframe();
+  pic_params->subsampling_x = pic->frame_hdr->subsampling_x == 1;
+  pic_params->subsampling_y = pic->frame_hdr->subsampling_y == 1;
+
+  SET_PARAM(width, frame_width);
+  SET_PARAM(height, frame_height);
+  SET_PARAM(interp_filter, interpolation_filter);
+  SET_PARAM(log2_tile_cols, tile_cols_log2);
+  SET_PARAM(log2_tile_rows, tile_rows_log2);
+#undef COPY_PARAM
+#undef SET_PARAM
+}
+
+void D3D11VP9Accelerator::CopyReferenceFrames(
+    const scoped_refptr<D3D11VP9Picture>& pic,
+    DXVA_PicParams_VP9* pic_params,
+    const std::vector<scoped_refptr<VP9Picture>>& reference_pictures) {
+  D3D11_TEXTURE2D_DESC texture_descriptor;
+  pic->picture_buffer()->texture()->GetDesc(&texture_descriptor);
+
+  for (size_t i = 0; i < base::size(pic_params->ref_frame_map); i++) {
+    if (i < reference_pictures.size() && reference_pictures[i].get()) {
+      scoped_refptr<D3D11VP9Picture> our_ref_pic(
+          static_cast<D3D11VP9Picture*>(reference_pictures[i].get()));
+      pic_params->ref_frame_map[i].Index7Bits = our_ref_pic->level();
+      pic_params->ref_frame_coded_width[i] = texture_descriptor.Width;
+      pic_params->ref_frame_coded_height[i] = texture_descriptor.Height;
+    } else {
+      pic_params->ref_frame_map[i].bPicEntry = 0xff;
+      pic_params->ref_frame_coded_width[i] = 0;
+      pic_params->ref_frame_coded_height[i] = 0;
+    }
+  }
+}
+
+void D3D11VP9Accelerator::CopyFrameRefs(
+    DXVA_PicParams_VP9* pic_params,
+    const scoped_refptr<D3D11VP9Picture>& pic) {
+  for (size_t i = 0; i < base::size(pic_params->frame_refs); i++) {
+    pic_params->frame_refs[i] =
+        pic_params->ref_frame_map[pic->frame_hdr->ref_frame_idx[i]];
+  }
+
+  for (size_t i = 0; i < base::size(pic_params->ref_frame_sign_bias); i++) {
+    pic_params->ref_frame_sign_bias[i] = pic->frame_hdr->ref_frame_sign_bias[i];
+  }
+}
+
+void D3D11VP9Accelerator::CopyLoopFilterParams(
+    DXVA_PicParams_VP9* pic_params,
+    const Vp9LoopFilterParams& loop_filter_params) {
+#define SET_PARAM(a, b) pic_params->a = loop_filter_params.b
+  SET_PARAM(filter_level, level);
+  SET_PARAM(sharpness_level, sharpness);
+  SET_PARAM(mode_ref_delta_enabled, delta_enabled);
+  SET_PARAM(mode_ref_delta_update, delta_update);
+#undef SET_PARAM
+
+  // base::size(...) doesn't work well in an array initializer.
+  DCHECK_EQ(4lu, base::size(pic_params->ref_deltas));
+  int ref_deltas[4] = {0};
+  for (size_t i = 0; i < base::size(pic_params->ref_deltas); i++) {
+    if (loop_filter_params.update_ref_deltas[i])
+      ref_deltas[i] = loop_filter_params.ref_deltas[i];
+    pic_params->ref_deltas[i] = ref_deltas[i];
+  }
+
+  int mode_deltas[2] = {0};
+  DCHECK_EQ(2lu, base::size(pic_params->mode_deltas));
+  for (size_t i = 0; i < base::size(pic_params->mode_deltas); i++) {
+    if (loop_filter_params.update_mode_deltas[i])
+      mode_deltas[i] = loop_filter_params.mode_deltas[i];
+    pic_params->mode_deltas[i] = mode_deltas[i];
+  }
+}
+
+void D3D11VP9Accelerator::CopyQuantParams(
+    DXVA_PicParams_VP9* pic_params,
+    const scoped_refptr<D3D11VP9Picture>& pic) {
+#define SET_PARAM(a, b) pic_params->a = pic->frame_hdr->quant_params.b
+  SET_PARAM(base_qindex, base_q_idx);
+  SET_PARAM(y_dc_delta_q, delta_q_y_dc);
+  SET_PARAM(uv_dc_delta_q, delta_q_uv_dc);
+  SET_PARAM(uv_ac_delta_q, delta_q_uv_ac);
+#undef SET_PARAM
+}
+
+void D3D11VP9Accelerator::CopySegmentationParams(
+    DXVA_PicParams_VP9* pic_params,
+    const Vp9SegmentationParams& segmentation_params) {
+#define SET_PARAM(a, b) pic_params->stVP9Segments.a = segmentation_params.b
+#define COPY_PARAM(a) SET_PARAM(a, a)
+  COPY_PARAM(enabled);
+  COPY_PARAM(update_map);
+  COPY_PARAM(temporal_update);
+  SET_PARAM(abs_delta, abs_or_delta_update);
+
+  for (size_t i = 0; i < base::size(segmentation_params.tree_probs); i++) {
+    COPY_PARAM(tree_probs[i]);
+  }
+
+  for (size_t i = 0; i < base::size(segmentation_params.pred_probs); i++) {
+    COPY_PARAM(pred_probs[i]);
+  }
+
+  for (size_t i = 0; i < 8; i++) {
+    for (size_t j = 0; j < 4; j++) {
+      COPY_PARAM(feature_data[i][j]);
+      if (segmentation_params.feature_enabled[i][j])
+        pic_params->stVP9Segments.feature_mask[i] |= (1 << j);
+    }
+  }
+#undef COPY_PARAM
+#undef SET_PARAM
+}
+
+void D3D11VP9Accelerator::CopyHeaderSizeAndID(
+    DXVA_PicParams_VP9* pic_params,
+    const scoped_refptr<D3D11VP9Picture>& pic) {
+  pic_params->uncompressed_header_size_byte_aligned =
+      static_cast<USHORT>(pic->frame_hdr->uncompressed_header_size);
+  pic_params->first_partition_size =
+      static_cast<USHORT>(pic->frame_hdr->header_size_in_bytes);
+
+  pic_params->StatusReportFeedbackNumber = status_feedback_++;
+}
+
+bool D3D11VP9Accelerator::SubmitDecoderBuffer(
+    const DXVA_PicParams_VP9& pic_params,
+    const scoped_refptr<D3D11VP9Picture>& pic) {
+#define GET_BUFFER(type)                                 \
+  RETURN_ON_HR_FAILURE(GetDecoderBuffer,                 \
+                       video_context_->GetDecoderBuffer( \
+                           video_decoder_.Get(), type, &buffer_size, &buffer))
+#define RELEASE_BUFFER(type) \
+  RETURN_ON_HR_FAILURE(      \
+      ReleaseDecoderBuffer,  \
+      video_context_->ReleaseDecoderBuffer(video_decoder_.Get(), type))
+
+  UINT buffer_size;
+  void* buffer;
+
+  GET_BUFFER(D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS);
+  memcpy(buffer, &pic_params, sizeof(pic_params));
+  RELEASE_BUFFER(D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS);
+
+  size_t buffer_offset = 0;
+  while (buffer_offset < pic->frame_hdr->frame_size) {
+    GET_BUFFER(D3D11_VIDEO_DECODER_BUFFER_BITSTREAM);
+    size_t copy_size = pic->frame_hdr->frame_size - buffer_offset;
+    bool contains_end = true;
+    if (copy_size > buffer_size) {
+      copy_size = buffer_size;
+      contains_end = false;
+    }
+    memcpy(buffer, pic->frame_hdr->data + buffer_offset, copy_size);
+    RELEASE_BUFFER(D3D11_VIDEO_DECODER_BUFFER_BITSTREAM);
+
+    DXVA_Slice_VPx_Short slice_info;
+
+    GET_BUFFER(D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL);
+    slice_info.BSNALunitDataLocation = 0;
+    slice_info.SliceBytesInBuffer = (UINT)copy_size;
+
+    // See the DXVA header specification for values of wBadSliceChopping:
+    // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/dxva/ns-dxva-_dxva_sliceinfo#wBadSliceChopping
+    if (buffer_offset == 0 && contains_end)
+      slice_info.wBadSliceChopping = 0;
+    else if (buffer_offset == 0 && !contains_end)
+      slice_info.wBadSliceChopping = 1;
+    else if (buffer_offset != 0 && contains_end)
+      slice_info.wBadSliceChopping = 2;
+    else if (buffer_offset != 0 && !contains_end)
+      slice_info.wBadSliceChopping = 3;
+
+    memcpy(buffer, &slice_info, sizeof(slice_info));
+    RELEASE_BUFFER(D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL);
+
+    constexpr int buffers_count = 3;
+    D3D11_VIDEO_DECODER_BUFFER_DESC buffers[buffers_count] = {};
+    buffers[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
+    buffers[0].DataOffset = 0;
+    buffers[0].DataSize = sizeof(pic_params);
+    buffers[1].BufferType = D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL;
+    buffers[1].DataOffset = 0;
+    buffers[1].DataSize = sizeof(slice_info);
+    buffers[2].BufferType = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM;
+    buffers[2].DataOffset = 0;
+    buffers[2].DataSize = copy_size;
+
+    RETURN_ON_HR_FAILURE(SubmitDecoderBuffers,
+                         video_context_->SubmitDecoderBuffers(
+                             video_decoder_.Get(), buffers_count, buffers));
+    buffer_offset += copy_size;
+  }
+
+  return true;
+#undef GET_BUFFER
+#undef RELEASE_BUFFER
 }
 
 bool D3D11VP9Accelerator::SubmitDecode(
@@ -24,12 +309,36 @@
     const Vp9LoopFilterParams& loop_filter_params,
     const std::vector<scoped_refptr<VP9Picture>>& reference_pictures,
     const base::Closure& on_finished_cb) {
-  return false;
+  scoped_refptr<D3D11VP9Picture> pic(
+      static_cast<D3D11VP9Picture*>(picture.get()));
+
+  if (!BeginFrame(pic.get()))
+    return false;
+
+  DXVA_PicParams_VP9 pic_params = {};
+  CopyFrameParams(pic, &pic_params);
+  CopyReferenceFrames(pic, &pic_params, reference_pictures);
+  CopyFrameRefs(&pic_params, pic);
+  CopyLoopFilterParams(&pic_params, loop_filter_params);
+  CopyQuantParams(&pic_params, pic);
+  CopySegmentationParams(&pic_params, segmentation_params);
+  CopyHeaderSizeAndID(&pic_params, pic);
+
+  if (!SubmitDecoderBuffer(pic_params, pic))
+    return false;
+
+  RETURN_ON_HR_FAILURE(DecoderEndFrame,
+                       video_context_->DecoderEndFrame(video_decoder_.Get()));
+  if (on_finished_cb)
+    on_finished_cb.Run();
+  return true;
 }
 
 bool D3D11VP9Accelerator::OutputPicture(
     const scoped_refptr<VP9Picture>& picture) {
-  return false;
+  D3D11VP9Picture* pic = static_cast<D3D11VP9Picture*>(picture.get());
+  client_->OutputResult(picture.get(), pic->picture_buffer());
+  return true;
 }
 
 bool D3D11VP9Accelerator::IsFrameContextRequired() const {
diff --git a/media/gpu/windows/d3d11_vp9_accelerator.h b/media/gpu/windows/d3d11_vp9_accelerator.h
index c6e4bda..d3bf967 100644
--- a/media/gpu/windows/d3d11_vp9_accelerator.h
+++ b/media/gpu/windows/d3d11_vp9_accelerator.h
@@ -5,17 +5,30 @@
 #ifndef MEDIA_GPU_WINDOWS_D3D11_VP9_ACCELERATOR_H_
 #define MEDIA_GPU_WINDOWS_D3D11_VP9_ACCELERATOR_H_
 
+#include <d3d11_1.h>
+#include <d3d9.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "media/base/media_log.h"
 #include "media/gpu/vp9_decoder.h"
+#include "media/gpu/windows/d3d11_video_decoder_client.h"
+#include "media/gpu/windows/d3d11_vp9_picture.h"
 
 namespace media {
 
 class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator {
  public:
-  D3D11VP9Accelerator();
+  D3D11VP9Accelerator(D3D11VideoDecoderClient* client,
+                      MediaLog* media_log,
+                      Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder,
+                      Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
+                      Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context);
   ~D3D11VP9Accelerator() override;
 
   scoped_refptr<VP9Picture> CreateVP9Picture() override;
 
+  // TODO(crbug/890054): Use constref instead of scoped_refptr.
   bool SubmitDecode(
       const scoped_refptr<VP9Picture>& picture,
       const Vp9SegmentationParams& segmentation_params,
@@ -31,6 +44,36 @@
                        Vp9FrameContext* frame_context) override;
 
  private:
+  // Helper methods for SubmitDecode
+  bool BeginFrame(D3D11VP9Picture* pic);
+
+  // TODO(crbug/890054): Use constref instead of scoped_refptr.
+  void CopyFrameParams(const scoped_refptr<D3D11VP9Picture>& pic,
+                       DXVA_PicParams_VP9* pic_params);
+  void CopyReferenceFrames(
+      const scoped_refptr<D3D11VP9Picture>& pic,
+      DXVA_PicParams_VP9* pic_params,
+      const std::vector<scoped_refptr<VP9Picture>>& reference_pictures);
+  void CopyFrameRefs(DXVA_PicParams_VP9* pic_params,
+                     const scoped_refptr<D3D11VP9Picture>& picture);
+  void CopyLoopFilterParams(DXVA_PicParams_VP9* pic_params,
+                            const Vp9LoopFilterParams& loop_filter_params);
+  void CopyQuantParams(DXVA_PicParams_VP9* pic_params,
+                       const scoped_refptr<D3D11VP9Picture>& pic);
+  void CopySegmentationParams(DXVA_PicParams_VP9* pic_params,
+                              const Vp9SegmentationParams& segmentation_params);
+  void CopyHeaderSizeAndID(DXVA_PicParams_VP9* pic_params,
+                           const scoped_refptr<D3D11VP9Picture>& pic);
+  bool SubmitDecoderBuffer(const DXVA_PicParams_VP9& pic_params,
+                           const scoped_refptr<D3D11VP9Picture>& pic);
+
+  D3D11VideoDecoderClient* client_;
+  MediaLog* const media_log_;
+  UINT status_feedback_;
+  Microsoft::WRL::ComPtr<ID3D11VideoDecoder> video_decoder_;
+  Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
+  Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context_;
+
   DISALLOW_COPY_AND_ASSIGN(D3D11VP9Accelerator);
 };
 
diff --git a/media/gpu/windows/d3d11_vp9_picture.h b/media/gpu/windows/d3d11_vp9_picture.h
index e4f3afc2..1d2f407 100644
--- a/media/gpu/windows/d3d11_vp9_picture.h
+++ b/media/gpu/windows/d3d11_vp9_picture.h
@@ -17,6 +17,10 @@
  public:
   explicit D3D11VP9Picture(D3D11PictureBuffer* picture_buffer);
 
+  D3D11PictureBuffer* picture_buffer() const { return picture_buffer_; };
+
+  size_t level() const { return level_; };
+
  protected:
   ~D3D11VP9Picture() override;
 
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 8d12b5f..cdaac96 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -14,6 +14,7 @@
 #include "cc/paint/paint_image.h"
 #include "cc/paint/paint_image_builder.h"
 #include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
@@ -275,6 +276,34 @@
   }
 }
 
+void OnQueryDone(scoped_refptr<VideoFrame> video_frame,
+                 gpu::gles2::GLES2Interface* gl,
+                 unsigned query_id) {
+  gl->DeleteQueriesEXT(1, &query_id);
+  // |video_frame| is dropped here.
+}
+
+void SynchronizeVideoFrameRead(scoped_refptr<VideoFrame> video_frame,
+                               gpu::gles2::GLES2Interface* gl,
+                               gpu::ContextSupport* context_support) {
+  DCHECK(gl);
+  SyncTokenClientImpl client(gl);
+  video_frame->UpdateReleaseSyncToken(&client);
+
+  if (video_frame->metadata()->IsTrue(
+          VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)) {
+    // |video_frame| must be kept alive during read operations.
+    DCHECK(context_support);
+    unsigned query_id = 0;
+    gl->GenQueriesEXT(1, &query_id);
+    DCHECK(query_id);
+    gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id);
+    gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+    context_support->SignalQuery(
+        query_id, base::BindOnce(&OnQueryDone, video_frame, gl, query_id));
+  }
+}
+
 }  // anonymous namespace
 
 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
@@ -416,7 +445,8 @@
     const gfx::RectF& dest_rect,
     cc::PaintFlags& flags,
     VideoRotation video_rotation,
-    const Context3D& context_3d) {
+    const Context3D& context_3d,
+    gpu::ContextSupport* context_support) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (flags.getAlpha() == 0) {
     return;
@@ -509,22 +539,23 @@
   canvas->flush();
 
   if (video_frame->HasTextures()) {
-    DCHECK(gl);
-    SyncTokenClientImpl client(gl);
-    video_frame->UpdateReleaseSyncToken(&client);
+    // Synchronize |video_frame| with the read operations in UpdateLastImage(),
+    // which are triggered by canvas->flush().
+    SynchronizeVideoFrameRead(video_frame, gl, context_support);
   }
 }
 
 void PaintCanvasVideoRenderer::Copy(
     const scoped_refptr<VideoFrame>& video_frame,
     cc::PaintCanvas* canvas,
-    const Context3D& context_3d) {
+    const Context3D& context_3d,
+    gpu::ContextSupport* context_support) {
   cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   flags.setFilterQuality(kLow_SkFilterQuality);
   Paint(video_frame, canvas,
         gfx::RectF(gfx::SizeF(video_frame->visible_rect().size())), flags,
-        media::VIDEO_ROTATION_0, context_3d);
+        media::VIDEO_ROTATION_0, context_3d, context_support);
 }
 
 namespace {
@@ -901,14 +932,14 @@
                                     target, texture, internal_format, format,
                                     type, level, premultiply_alpha, flip_y);
   gl->DeleteTextures(1, &source_texture);
-  gl->Flush();
-
-  SyncTokenClientImpl client(gl);
-  video_frame->UpdateReleaseSyncToken(&client);
+  gl->ShallowFlushCHROMIUM();
+  // The caller must call SynchronizeVideoFrameRead() after this operation, but
+  // we can't do that because we don't have the ContextSupport.
 }
 
 bool PaintCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture(
     const Context3D& context_3d,
+    gpu::ContextSupport* context_support,
     gpu::gles2::GLES2Interface* destination_gl,
     const scoped_refptr<VideoFrame>& video_frame,
     unsigned int target,
@@ -922,7 +953,9 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(video_frame);
   DCHECK(video_frame->HasTextures());
-  if (video_frame->NumTextures() > 1) {
+  if (video_frame->NumTextures() > 1 ||
+      video_frame->metadata()->IsTrue(
+          VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)) {
     if (!context_3d.gr_context)
       return false;
     if (!UpdateLastImage(video_frame, context_3d))
@@ -936,7 +969,11 @@
     if (!backend_texture.getGLTextureInfo(&texture_info))
       return false;
 
+    // Synchronize |video_frame| with the read operations in UpdateLastImage(),
+    // which are triggered by getBackendTexture().
     gpu::gles2::GLES2Interface* canvas_gl = context_3d.gl;
+    SynchronizeVideoFrameRead(video_frame, canvas_gl, context_support);
+
     gpu::MailboxHolder mailbox_holder;
     mailbox_holder.texture_target = texture_info.fTarget;
     canvas_gl->ProduceTextureDirectCHROMIUM(texture_info.fID,
@@ -964,13 +1001,11 @@
     gpu::SyncToken dest_sync_token;
     destination_gl->GenUnverifiedSyncTokenCHROMIUM(dest_sync_token.GetData());
     canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData());
-
-    SyncTokenClientImpl client(canvas_gl);
-    video_frame->UpdateReleaseSyncToken(&client);
   } else {
     CopyVideoFrameSingleTextureToGLTexture(
         destination_gl, video_frame.get(), target, texture, internal_format,
         format, type, level, premultiply_alpha, flip_y);
+    SynchronizeVideoFrameRead(video_frame, destination_gl, nullptr);
   }
 
   return true;
@@ -1208,8 +1243,9 @@
       return false;
     last_id_ = video_frame->unique_id();
   }
+
+  DCHECK(last_image_);
   last_image_deleting_timer_.Reset();
-  DCHECK(!!last_image_);
   return true;
 }
 
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index d732111..d4752f3 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -29,6 +29,7 @@
 
 namespace gpu {
 struct Capabilities;
+class ContextSupport;
 }
 
 namespace media {
@@ -39,25 +40,29 @@
   PaintCanvasVideoRenderer();
   ~PaintCanvasVideoRenderer();
 
-  // Paints |video_frame| on |canvas|, scaling and rotating the result to fit
-  // dimensions specified by |dest_rect|.
-  // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
-  // must be provided.
+  // Paints |video_frame| translated and scaled to |dest_rect| on |canvas|.
   //
-  // Black will be painted on |canvas| if |video_frame| is null.
+  // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
+  // and |context_support| must be provided.
+  //
+  // If |video_frame| is nullptr or an unsupported format, |dest_rect| will be
+  // painted black.
   void Paint(const scoped_refptr<VideoFrame>& video_frame,
              cc::PaintCanvas* canvas,
              const gfx::RectF& dest_rect,
              cc::PaintFlags& flags,
              VideoRotation video_rotation,
-             const Context3D& context_3d);
+             const Context3D& context_3d,
+             gpu::ContextSupport* context_support);
 
-  // Copy |video_frame| on |canvas|.
+  // Paints |video_frame| scaled to its visible size on |canvas|.
+  //
   // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
-  // must be provided.
+  // and |context_support| must be provided.
   void Copy(const scoped_refptr<VideoFrame>& video_frame,
             cc::PaintCanvas* canvas,
-            const Context3D& context_3d);
+            const Context3D& context_3d,
+            gpu::ContextSupport* context_support);
 
   // Convert the contents of |video_frame| to raw RGB pixels. |rgb_pixels|
   // should point into a buffer large enough to hold as many 32 bit RGBA pixels
@@ -82,15 +87,12 @@
       bool premultiply_alpha,
       bool flip_y);
 
-  // Copy the contents of texture of |video_frame| to texture |texture| in
-  // context |destination_gl|.
-  // |level|, |internal_format|, |type| specify target texture |texture|.
+  // Copy the contents of |video_frame| to |texture| of |destination_gl|.
+  //
   // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
-  // |context_3d| has a GrContext that may be used during the copy.
-  // CorrectLastImageDimensions() ensures that the source texture will be
-  // cropped to |visible_rect|. Returns true on success.
   bool CopyVideoFrameTexturesToGLTexture(
       const Context3D& context_3d,
+      gpu::ContextSupport* context_support,
       gpu::gles2::GLES2Interface* destination_gl,
       const scoped_refptr<VideoFrame>& video_frame,
       unsigned int target,
@@ -168,8 +170,6 @@
   // never be painted again, so we can release the resource.
   void ResetCache();
 
-  void CorrectLastImageDimensions(const SkIRect& visible_rect);
-
   // Used for unit test.
   SkISize LastImageDimensionsForTesting();
 
@@ -179,6 +179,8 @@
   bool UpdateLastImage(const scoped_refptr<VideoFrame>& video_frame,
                        const Context3D& context_3d);
 
+  void CorrectLastImageDimensions(const SkIRect& visible_rect);
+
   // Last image used to draw to the canvas.
   cc::PaintImage last_image_;
 
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index 3bca993..bd321305 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -225,7 +225,7 @@
   cc::PaintFlags flags;
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(nullptr, canvas, kNaturalRect, flags, VIDEO_ROTATION_0,
-                  Context3D());
+                  Context3D(), nullptr);
 }
 
 void PaintCanvasVideoRendererTest::Paint(
@@ -260,13 +260,13 @@
   flags.setBlendMode(mode);
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(video_frame, canvas, dest_rect, flags, video_rotation,
-                  Context3D());
+                  Context3D(), nullptr);
 }
 
 void PaintCanvasVideoRendererTest::Copy(
     const scoped_refptr<VideoFrame>& video_frame,
     cc::PaintCanvas* canvas) {
-  renderer_.Copy(video_frame, canvas, Context3D());
+  renderer_.Copy(video_frame, canvas, Context3D(), nullptr);
 }
 
 TEST_F(PaintCanvasVideoRendererTest, NoFrame) {
@@ -564,7 +564,7 @@
   flags.setFilterQuality(kNone_SkFilterQuality);
   renderer_.Paint(video_frame, &canvas,
                   gfx::RectF(bitmap.width(), bitmap.height()), flags,
-                  VIDEO_ROTATION_0, Context3D());
+                  VIDEO_ROTATION_0, Context3D(), nullptr);
   for (int j = 0; j < bitmap.height(); j++) {
     for (int i = 0; i < bitmap.width(); i++) {
       const int value = i + j * bitmap.width();
@@ -657,43 +657,32 @@
   cc::PaintFlags flags;
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(video_frame, &canvas, kNaturalRect, flags, VIDEO_ROTATION_90,
-                  context_3d);
+                  context_3d, nullptr);
 }
 
 void EmptyCallback(const gpu::SyncToken& sync_token) {}
 
 TEST_F(PaintCanvasVideoRendererTest, CorrectFrameSizeToVisibleRect) {
-  int fWidth{16}, fHeight{16};
+  constexpr int fWidth{16}, fHeight{16};
   SkImageInfo imInfo =
       SkImageInfo::MakeN32(fWidth, fHeight, kOpaque_SkAlphaType);
 
-  sk_sp<const GrGLInterface> glInterface(GrGLCreateNullInterface());
-  sk_sp<GrContext> grContext = GrContext::MakeGL(std::move(glInterface));
+  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
 
-  sk_sp<SkSurface> surface =
-      SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, imInfo);
-  cc::SkiaPaintCanvas canvas(surface->getCanvas());
-
-  TestGLES2Interface gles2;
-  Context3D context_3d(&gles2, grContext.get());
   gfx::Size coded_size(fWidth, fHeight);
   gfx::Size visible_size(fWidth / 2, fHeight / 2);
 
-  gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
-  for (size_t i = 0; i < VideoFrame::kMaxPlanes; i++) {
-    mailbox_holders[i] = gpu::MailboxHolder(
-        gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_RECTANGLE_ARB);
-  }
+  uint8_t memory[fWidth * fHeight * 2] = {0};
 
-  auto video_frame = VideoFrame::WrapNativeTextures(
-      PIXEL_FORMAT_I420, mailbox_holders, base::Bind(EmptyCallback), coded_size,
-      gfx::Rect(visible_size), visible_size,
+  auto video_frame = media::VideoFrame::WrapExternalData(
+      media::PIXEL_FORMAT_Y16, coded_size, gfx::Rect(visible_size),
+      visible_size, &memory[0], fWidth * fHeight * 2,
       base::TimeDelta::FromMilliseconds(4));
 
   gfx::RectF visible_rect(visible_size.width(), visible_size.height());
   cc::PaintFlags flags;
   renderer_.Paint(video_frame, &canvas, visible_rect, flags, VIDEO_ROTATION_0,
-                  context_3d);
+                  Context3D(), nullptr);
 
   EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().width());
   EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().height());
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 952c9a7..5b4abce 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -866,7 +866,7 @@
 
         // This is software path, so canvas and video_frame are always backed
         // by software.
-        video_renderer_->Copy(video_frame, &canvas, Context3D());
+        video_renderer_->Copy(video_frame, &canvas, Context3D(), nullptr);
       } else {
         HardwarePlaneResource* hardware_resource = plane_resource->AsHardware();
         size_t bytes_per_row = viz::ResourceSizes::CheckedWidthInBytes<size_t>(
diff --git a/mojo/public/cpp/system/data_pipe.h b/mojo/public/cpp/system/data_pipe.h
index 30c7546..8e1e0b88 100644
--- a/mojo/public/cpp/system/data_pipe.h
+++ b/mojo/public/cpp/system/data_pipe.h
@@ -150,7 +150,7 @@
   MojoResult result =
       CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
   ALLOW_UNUSED_LOCAL(result);
-  DCHECK_EQ(MOJO_RESULT_OK, result);
+  CHECK_EQ(MOJO_RESULT_OK, result);
 }
 
 inline DataPipe::DataPipe(uint32_t capacity_num_bytes) {
@@ -163,14 +163,14 @@
   MojoResult result =
       CreateDataPipe(&options, &producer_handle, &consumer_handle);
   ALLOW_UNUSED_LOCAL(result);
-  DCHECK_EQ(MOJO_RESULT_OK, result);
+  CHECK_EQ(MOJO_RESULT_OK, result);
 }
 
 inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
   MojoResult result =
       CreateDataPipe(&options, &producer_handle, &consumer_handle);
   ALLOW_UNUSED_LOCAL(result);
-  DCHECK_EQ(MOJO_RESULT_OK, result);
+  CHECK_EQ(MOJO_RESULT_OK, result);
 }
 
 inline DataPipe::~DataPipe() {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index ea6c4c1..3991b98 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1332,13 +1332,13 @@
       "third_party/quic/core/frames/quic_blocked_frame.h",
       "third_party/quic/core/frames/quic_connection_close_frame.cc",
       "third_party/quic/core/frames/quic_connection_close_frame.h",
-      "third_party/quic/core/frames/quic_control_frame.h",
       "third_party/quic/core/frames/quic_crypto_frame.cc",
       "third_party/quic/core/frames/quic_crypto_frame.h",
       "third_party/quic/core/frames/quic_frame.cc",
       "third_party/quic/core/frames/quic_frame.h",
       "third_party/quic/core/frames/quic_goaway_frame.cc",
       "third_party/quic/core/frames/quic_goaway_frame.h",
+      "third_party/quic/core/frames/quic_inlined_frame.h",
       "third_party/quic/core/frames/quic_max_stream_id_frame.cc",
       "third_party/quic/core/frames/quic_max_stream_id_frame.h",
       "third_party/quic/core/frames/quic_message_frame.cc",
@@ -1346,6 +1346,8 @@
       "third_party/quic/core/frames/quic_mtu_discovery_frame.h",
       "third_party/quic/core/frames/quic_new_connection_id_frame.cc",
       "third_party/quic/core/frames/quic_new_connection_id_frame.h",
+      "third_party/quic/core/frames/quic_new_token_frame.cc",
+      "third_party/quic/core/frames/quic_new_token_frame.h",
       "third_party/quic/core/frames/quic_padding_frame.cc",
       "third_party/quic/core/frames/quic_padding_frame.h",
       "third_party/quic/core/frames/quic_path_challenge_frame.cc",
@@ -1390,6 +1392,10 @@
       "third_party/quic/core/qpack/qpack_decoder.h",
       "third_party/quic/core/qpack/qpack_encoder.cc",
       "third_party/quic/core/qpack/qpack_encoder.h",
+      "third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc",
+      "third_party/quic/core/qpack/qpack_encoder_stream_receiver.h",
+      "third_party/quic/core/qpack/qpack_encoder_stream_sender.cc",
+      "third_party/quic/core/qpack/qpack_encoder_stream_sender.h",
       "third_party/quic/core/qpack/qpack_header_table.cc",
       "third_party/quic/core/qpack/qpack_header_table.h",
       "third_party/quic/core/quic_ack_listener_interface.cc",
@@ -1534,8 +1540,6 @@
       "third_party/quic/platform/api/quic_text_utils.h",
       "third_party/quic/platform/api/quic_url.cc",
       "third_party/quic/platform/api/quic_url.h",
-      "third_party/quic/platform/api/quic_url_utils.cc",
-      "third_party/quic/platform/api/quic_url_utils.h",
       "third_party/quic/platform/impl/quic_aligned_impl.h",
       "third_party/quic/platform/impl/quic_arraysize_impl.h",
       "third_party/quic/platform/impl/quic_bug_tracker_impl.h",
@@ -1580,8 +1584,6 @@
       "third_party/quic/platform/impl/quic_text_utils_impl.h",
       "third_party/quic/platform/impl/quic_url_impl.cc",
       "third_party/quic/platform/impl/quic_url_impl.h",
-      "third_party/quic/platform/impl/quic_url_utils_impl.cc",
-      "third_party/quic/platform/impl/quic_url_utils_impl.h",
       "third_party/quic/quartc/quartc_factory.cc",
       "third_party/quic/quartc/quartc_factory.h",
       "third_party/quic/quartc/quartc_packet_writer.cc",
@@ -5010,6 +5012,8 @@
     "third_party/quic/core/http/quic_headers_stream_test.cc",
     "third_party/quic/core/packet_number_indexed_queue_test.cc",
     "third_party/quic/core/qpack/qpack_decoder_test.cc",
+    "third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc",
+    "third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc",
     "third_party/quic/core/qpack/qpack_encoder_test.cc",
     "third_party/quic/core/quic_alarm_test.cc",
     "third_party/quic/core/quic_arena_scoped_ptr_test.cc",
@@ -5141,13 +5145,9 @@
     "third_party/quic/platform/api/quic_reference_counted_test.cc",
     "third_party/quic/platform/api/quic_singleton_test.cc",
     "third_party/quic/platform/api/quic_str_cat_test.cc",
-    "third_party/quic/platform/api/quic_test_random.h",
     "third_party/quic/platform/api/quic_text_utils_test.cc",
     "third_party/quic/platform/api/quic_url_test.cc",
     "third_party/quic/platform/impl/quic_chromium_clock_test.cc",
-    "third_party/quic/platform/impl/quic_test_random_impl.cc",
-    "third_party/quic/platform/impl/quic_test_random_impl.h",
-    "third_party/quic/platform/impl/quic_url_utils_impl_test.cc",
     "third_party/quic/quartc/quartc_session_test.cc",
     "third_party/quic/quartc/quartc_stream_test.cc",
     "third_party/quic/quartc/simulated_packet_transport.cc",
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index cafdd106..f4f63b5b 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1487,7 +1487,7 @@
       if (GetNumOpenOutgoingStreams() > 0) {
         UMA_HISTOGRAM_BOOLEAN(
             "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets",
-            connection()->sent_packet_manager().HasUnackedPackets());
+            connection()->sent_packet_manager().HasInFlightPackets());
         UMA_HISTOGRAM_COUNTS_1M(
             "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveRTOCount",
             connection()->sent_packet_manager().GetConsecutiveRtoCount());
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 7e3dd62..fd34e51 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -133,9 +133,6 @@
 // If true, enable experiment for testing PCC congestion-control.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_pcc3, false)
 
-// If true, fix potential crashes in QuicSession::RetransmitLostData.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_retransmit_lost_data, true)
-
 // When true, ensure BBR allows at least one MSS to be sent in response to an
 // ACK in packet conservation.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_one_mss_conservation, false)
@@ -241,7 +238,7 @@
           true)
 
 // This flag fixes a bug where a zombie stream cannot be correctly reset.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_reset_zombie_streams, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_reset_zombie_streams, true)
 
 // When true, fix initialization and updating of
 // |time_of_first_packet_sent_after_receiving_| in QuicConnection.
@@ -266,3 +263,10 @@
 // When the STMP connection option is sent by the client, timestamps in the QUIC
 // ACK frame are sent and processed.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
+
+// If true, use a common stream ID limit checking method.
+QUIC_FLAG(bool, FLAGS_quic_use_common_stream_check, false)
+
+// When true, don't arm the path degrading alarm on the server side and stop
+// using HasUnackedPackets to decide when to arm it.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_path_degrading_alarm, false)
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 263a9f0..decc783 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -142,9 +142,8 @@
 
   request_info_ = request_info;
   if (pushed_stream_id_ != kNoPushedStreamFound) {
-    int error =
-        spdy_session_->GetPushedStream(request_info_->url, pushed_stream_id_,
-                                       priority, &stream_, stream_net_log);
+    int error = spdy_session_->GetPushedStream(
+        request_info_->url, pushed_stream_id_, priority, &stream_);
     if (error != OK)
       return error;
 
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 5f38d336..a656aae 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -920,8 +920,7 @@
 int SpdySession::GetPushedStream(const GURL& url,
                                  spdy::SpdyStreamId pushed_stream_id,
                                  RequestPriority priority,
-                                 SpdyStream** stream,
-                                 const NetLogWithSource& stream_net_log) {
+                                 SpdyStream** stream) {
   CHECK(!in_io_loop_);
   // |pushed_stream_id| must be valid.
   DCHECK_NE(pushed_stream_id, kNoPushedStreamFound);
@@ -930,7 +929,6 @@
             pool_->push_promise_index()->FindStream(url, this));
 
   if (availability_state_ == STATE_DRAINING) {
-    *stream = nullptr;
     return ERR_CONNECTION_CLOSED;
   }
 
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 97f7f58..537f7b5 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -317,31 +317,21 @@
   const SpdySessionKey& spdy_session_key() const {
     return spdy_session_key_;
   }
-  // Get a pushed stream for a given |url|.  If the server initiates a
-  // stream, it might already exist for a given path.  The server
-  // might also not have initiated the stream yet, but indicated it
-  // will via X-Associated-Content.  Returns OK if a stream was found
-  // and put into |spdy_stream|, or if one was not found but it is
-  // okay to create a new stream (in which case |spdy_stream| is
-  // reset).  Returns an error (not ERR_IO_PENDING) otherwise, and
-  // resets |spdy_stream|.
+
+  // Get a pushed stream for a given |url| with stream ID |pushed_stream_id|.
+  // The caller must have already claimed the stream from Http2PushPromiseIndex.
+  // |pushed_stream_id| must not be kNoPushedStreamFound.
   //
-  // If |pushed_stream_id != kNoPushedStreamFound|, then the pushed stream with
-  // pushed_stream_id is used.  An error is returned if that stream is not
-  // available.
-  //
-  // If |pushed_stream_id == kNoPushedStreamFound|, then any matching pushed
-  // stream that has not been claimed by another request can be used.  This can
-  // happen, for example, with http scheme pushed streams, or if the pushed
-  // stream was received from the server in the meanwhile.
-  //
-  // If a stream was found and the stream is still open, the priority
-  // of that stream is updated to match |priority|.
+  // Returns ERR_CONNECTION_CLOSED if the connection is being closed.
+  // Returns ERR_SPDY_PUSHED_STREAM_NOT_AVAILABLE if the pushed stream is not
+  //   available any longer, for example, if the server has reset it.
+  // Returns OK if the stream is still available, and returns the stream in
+  //   |*spdy_stream|.  If the stream is still open, updates its priority to
+  //   |priority|.
   int GetPushedStream(const GURL& url,
                       spdy::SpdyStreamId pushed_stream_id,
                       RequestPriority priority,
-                      SpdyStream** spdy_stream,
-                      const NetLogWithSource& stream_net_log);
+                      SpdyStream** spdy_stream);
 
   // Called when the pushed stream should be cancelled. If the pushed stream is
   // not claimed and active, sends RST to the server to cancel the stream.
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 19d899b..d2ee430 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -1650,7 +1650,7 @@
 
   SpdyStream* spdy_stream2;
   int rv = session_->GetPushedStream(pushed_url, pushed_stream_id, MEDIUM,
-                                     &spdy_stream2, NetLogWithSource());
+                                     &spdy_stream2);
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(spdy_stream2);
 
@@ -5603,7 +5603,7 @@
 
   SpdyStream* pushed_stream;
   int rv = session_->GetPushedStream(pushed_url, pushed_stream_id, IDLE,
-                                     &pushed_stream, NetLogWithSource());
+                                     &pushed_stream);
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(pushed_stream);
   test::StreamDelegateCloseOnHeaders delegate2(pushed_stream->GetWeakPtr());
@@ -5687,7 +5687,7 @@
   const GURL pushed_url(kPushedUrl);
   SpdyStream* pushed_stream;
   int rv = session_->GetPushedStream(pushed_url, 2 /* pushed_stream_id */, IDLE,
-                                     &pushed_stream, NetLogWithSource());
+                                     &pushed_stream);
   EXPECT_THAT(rv, IsError(ERR_SPDY_PUSHED_STREAM_NOT_AVAILABLE));
 
   // Read PUSH_PROMISE.
@@ -5718,13 +5718,13 @@
   // GetPushedStream() should return an error if there does not exist a pushed
   // stream with ID |pushed_stream_id|.
   rv = session_->GetPushedStream(pushed_url, 4 /* pushed_stream_id */, IDLE,
-                                 &pushed_stream, NetLogWithSource());
+                                 &pushed_stream);
   EXPECT_THAT(rv, IsError(ERR_SPDY_PUSHED_STREAM_NOT_AVAILABLE));
 
   // GetPushedStream() should return OK and return the pushed stream in
   // |pushed_stream| outparam if |pushed_stream_id| matches.
   rv = session_->GetPushedStream(pushed_url, 2 /* pushed_stream_id */, IDLE,
-                                 &pushed_stream, NetLogWithSource());
+                                 &pushed_stream);
   EXPECT_THAT(rv, IsOk());
   ASSERT_TRUE(pushed_stream);
   test::StreamDelegateCloseOnHeaders delegate2(pushed_stream->GetWeakPtr());
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index 95f119d..69f2c44 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -357,7 +357,7 @@
 
   SpdyStream* push_stream;
   EXPECT_THAT(session->GetPushedStream(pushed_url, pushed_stream_id, IDLE,
-                                       &push_stream, NetLogWithSource()),
+                                       &push_stream),
               IsOk());
   ASSERT_TRUE(push_stream);
   EXPECT_EQ(kPushUrl, push_stream->url().spec());
diff --git a/net/third_party/quic/core/chlo_extractor.cc b/net/third_party/quic/core/chlo_extractor.cc
index f702b5c..d72158d 100644
--- a/net/third_party/quic/core/chlo_extractor.cc
+++ b/net/third_party/quic/core/chlo_extractor.cc
@@ -51,6 +51,7 @@
   bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
   bool OnApplicationCloseFrame(const QuicApplicationCloseFrame& frame) override;
   bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
   bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
   bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
@@ -220,6 +221,10 @@
   return true;
 }
 
+bool ChloFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+  return true;
+}
+
 bool ChloFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& frame) {
   return true;
 }
diff --git a/net/third_party/quic/core/frames/quic_blocked_frame.cc b/net/third_party/quic/core/frames/quic_blocked_frame.cc
index 3ea12fb8..b4ba2c7 100644
--- a/net/third_party/quic/core/frames/quic_blocked_frame.cc
+++ b/net/third_party/quic/core/frames/quic_blocked_frame.cc
@@ -3,19 +3,21 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_blocked_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 
 namespace quic {
 
-QuicBlockedFrame::QuicBlockedFrame() : stream_id(0), offset(0) {}
+QuicBlockedFrame::QuicBlockedFrame()
+    : control_frame_id(kInvalidControlFrameId), stream_id(0), offset(0) {}
 
 QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id,
                                    QuicStreamId stream_id)
-    : QuicControlFrame(control_frame_id), stream_id(stream_id), offset(0) {}
+    : control_frame_id(control_frame_id), stream_id(stream_id), offset(0) {}
 
 QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id,
                                    QuicStreamId stream_id,
                                    QuicStreamOffset offset)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       stream_id(stream_id),
       offset(offset) {}
 
diff --git a/net/third_party/quic/core/frames/quic_blocked_frame.h b/net/third_party/quic/core/frames/quic_blocked_frame.h
index 70974be..d30dcb6e 100644
--- a/net/third_party/quic/core/frames/quic_blocked_frame.h
+++ b/net/third_party/quic/core/frames/quic_blocked_frame.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
+#include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
 
@@ -15,7 +15,7 @@
 // endpoint believes itself to be flow-control blocked but otherwise ready to
 // send data. The BLOCKED frame is purely advisory and optional.
 // Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28).
-struct QUIC_EXPORT_PRIVATE QuicBlockedFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicBlockedFrame {
   QuicBlockedFrame();
   QuicBlockedFrame(QuicControlFrameId control_frame_id, QuicStreamId stream_id);
   QuicBlockedFrame(QuicControlFrameId control_frame_id,
@@ -26,6 +26,10 @@
       std::ostream& os,
       const QuicBlockedFrame& b);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   // The stream this frame applies to.  0 is a special case meaning the overall
   // connection rather than a specific stream.
   //
diff --git a/net/third_party/quic/core/frames/quic_control_frame.h b/net/third_party/quic/core/frames/quic_control_frame.h
deleted file mode 100644
index 41b3c14..0000000
--- a/net/third_party/quic/core/frames/quic_control_frame.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CONTROL_FRAME_H_
-#define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CONTROL_FRAME_H_
-
-#include "net/third_party/quic/core/quic_constants.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-
-namespace quic {
-
-struct QUIC_EXPORT_PRIVATE QuicControlFrame {
-  QuicControlFrame() : control_frame_id(kInvalidControlFrameId) {}
-  explicit QuicControlFrame(QuicControlFrameId control_frame_id)
-      : control_frame_id(control_frame_id) {}
-
-  // A unique identifier of this control frame. 0 when this frame is received,
-  // and non-zero when sent.
-  QuicControlFrameId control_frame_id;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CONTROL_FRAME_H_
diff --git a/net/third_party/quic/core/frames/quic_frame.cc b/net/third_party/quic/core/frames/quic_frame.cc
index 8ada26e..8d6b132 100644
--- a/net/third_party/quic/core/frames/quic_frame.cc
+++ b/net/third_party/quic/core/frames/quic_frame.cc
@@ -14,7 +14,7 @@
 QuicFrame::QuicFrame() {}
 
 QuicFrame::QuicFrame(QuicPaddingFrame padding_frame)
-    : type(PADDING_FRAME), padding_frame(padding_frame) {}
+    : padding_frame(padding_frame) {}
 
 QuicFrame::QuicFrame(QuicStreamFrame stream_frame)
     : stream_frame(stream_frame) {}
@@ -25,13 +25,12 @@
 QuicFrame::QuicFrame(QuicAckFrame* frame) : type(ACK_FRAME), ack_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicMtuDiscoveryFrame frame)
-    : type(MTU_DISCOVERY_FRAME), mtu_discovery_frame(frame) {}
+    : mtu_discovery_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicStopWaitingFrame* frame)
     : type(STOP_WAITING_FRAME), stop_waiting_frame(frame) {}
 
-QuicFrame::QuicFrame(QuicPingFrame frame)
-    : type(PING_FRAME), ping_frame(frame) {}
+QuicFrame::QuicFrame(QuicPingFrame frame) : ping_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicRstStreamFrame* frame)
     : type(RST_STREAM_FRAME), rst_stream_frame(frame) {}
@@ -54,11 +53,10 @@
 QuicFrame::QuicFrame(QuicNewConnectionIdFrame* frame)
     : type(NEW_CONNECTION_ID_FRAME), new_connection_id_frame(frame) {}
 
-QuicFrame::QuicFrame(QuicMaxStreamIdFrame frame)
-    : type(MAX_STREAM_ID_FRAME), max_stream_id_frame(frame) {}
+QuicFrame::QuicFrame(QuicMaxStreamIdFrame frame) : max_stream_id_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicStreamIdBlockedFrame frame)
-    : type(STREAM_ID_BLOCKED_FRAME), stream_id_blocked_frame(frame) {}
+    : stream_id_blocked_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicPathResponseFrame* frame)
     : type(PATH_RESPONSE_FRAME), path_response_frame(frame) {}
@@ -72,6 +70,9 @@
 QuicFrame::QuicFrame(QuicMessageFrame* frame)
     : type(MESSAGE_FRAME), message_frame(frame) {}
 
+QuicFrame::QuicFrame(QuicNewTokenFrame* frame)
+    : type(NEW_TOKEN_FRAME), new_token_frame(frame) {}
+
 void DeleteFrames(QuicFrames* frames) {
   for (QuicFrame& frame : *frames) {
     DeleteFrame(&frame);
@@ -131,6 +132,9 @@
     case CRYPTO_FRAME:
       delete frame->crypto_frame;
       break;
+    case NEW_TOKEN_FRAME:
+      delete frame->new_token_frame;
+      break;
 
     case NUM_FRAME_TYPES:
       DCHECK(false) << "Cannot delete type: " << frame->type;
@@ -310,6 +314,9 @@
     case MESSAGE_FRAME:
       os << "type { MESSAGE_FRAME }" << *(frame.message_frame);
       break;
+    case NEW_TOKEN_FRAME:
+      os << "type { NEW_TOKEN_FRAME }" << *(frame.new_token_frame);
+      break;
     default: {
       QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type;
       break;
diff --git a/net/third_party/quic/core/frames/quic_frame.h b/net/third_party/quic/core/frames/quic_frame.h
index 5815eac..83d51d5b 100644
--- a/net/third_party/quic/core/frames/quic_frame.h
+++ b/net/third_party/quic/core/frames/quic_frame.h
@@ -18,6 +18,7 @@
 #include "net/third_party/quic/core/frames/quic_message_frame.h"
 #include "net/third_party/quic/core/frames/quic_mtu_discovery_frame.h"
 #include "net/third_party/quic/core/frames/quic_new_connection_id_frame.h"
+#include "net/third_party/quic/core/frames/quic_new_token_frame.h"
 #include "net/third_party/quic/core/frames/quic_padding_frame.h"
 #include "net/third_party/quic/core/frames/quic_path_challenge_frame.h"
 #include "net/third_party/quic/core/frames/quic_path_response_frame.h"
@@ -53,6 +54,7 @@
   explicit QuicFrame(QuicBlockedFrame* frame);
   explicit QuicFrame(QuicApplicationCloseFrame* frame);
   explicit QuicFrame(QuicNewConnectionIdFrame* frame);
+  explicit QuicFrame(QuicNewTokenFrame* frame);
   explicit QuicFrame(QuicPathResponseFrame* frame);
   explicit QuicFrame(QuicPathChallengeFrame* frame);
   explicit QuicFrame(QuicStopSendingFrame* frame);
@@ -63,10 +65,18 @@
                                                       const QuicFrame& frame);
 
   union {
+    // Inlined frames.
     // Overlapping inlined frames have a |type| field at the same 0 offset as
-    // QuicFrame does below, allowing use of the remaining 7 bytes after offset
-    // for frame-type specific fields.
+    // QuicFrame does for out of line frames below, allowing use of the
+    // remaining 7 bytes after offset for frame-type specific fields.
+    QuicPaddingFrame padding_frame;
+    QuicMtuDiscoveryFrame mtu_discovery_frame;
+    QuicPingFrame ping_frame;
+    QuicMaxStreamIdFrame max_stream_id_frame;
+    QuicStreamIdBlockedFrame stream_id_blocked_frame;
     QuicStreamFrame stream_frame;
+
+    // Out of line frames.
     struct {
       QuicFrameType type;
 
@@ -75,14 +85,6 @@
       // QuicWindowUpdateFrame, QuicBlockedFrame, QuicPathResponseFrame,
       // QuicPathChallengeFrame and QuicStopSendingFrame.
       union {
-        // Inlined frames.
-        QuicPaddingFrame padding_frame;
-        QuicMtuDiscoveryFrame mtu_discovery_frame;
-        QuicPingFrame ping_frame;
-        QuicMaxStreamIdFrame max_stream_id_frame;
-        QuicStreamIdBlockedFrame stream_id_blocked_frame;
-
-        // Out of line frames.
         QuicAckFrame* ack_frame;
         QuicStopWaitingFrame* stop_waiting_frame;
         QuicRstStreamFrame* rst_stream_frame;
@@ -97,17 +99,16 @@
         QuicStopSendingFrame* stop_sending_frame;
         QuicMessageFrame* message_frame;
         QuicCryptoFrame* crypto_frame;
+        QuicNewTokenFrame* new_token_frame;
       };
     };
   };
 };
-// In QuicFrame, QuicFrameType consumes 8 bytes with padding.
+
 static_assert(sizeof(QuicFrame) <= 24,
-              "Frames larger than 16 bytes should be referenced by pointer.");
+              "Frames larger than 24 bytes should be referenced by pointer.");
 static_assert(offsetof(QuicStreamFrame, type) == offsetof(QuicFrame, type),
               "Offset of |type| must match in QuicFrame and QuicStreamFrame");
-static_assert(offsetof(QuicStreamFrame, type) == 0,
-              "Illegal stream frame layout");
 
 // A inline size of 1 is chosen to optimize the typical use case of
 // 1-stream-frame in QuicTransmissionInfo.retransmittable_frames.
diff --git a/net/third_party/quic/core/frames/quic_goaway_frame.cc b/net/third_party/quic/core/frames/quic_goaway_frame.cc
index 814f311..e739c028 100644
--- a/net/third_party/quic/core/frames/quic_goaway_frame.cc
+++ b/net/third_party/quic/core/frames/quic_goaway_frame.cc
@@ -3,18 +3,21 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_goaway_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 
 namespace quic {
 
 QuicGoAwayFrame::QuicGoAwayFrame()
-    : error_code(QUIC_NO_ERROR), last_good_stream_id(0) {}
+    : control_frame_id(kInvalidControlFrameId),
+      error_code(QUIC_NO_ERROR),
+      last_good_stream_id(0) {}
 
 QuicGoAwayFrame::QuicGoAwayFrame(QuicControlFrameId control_frame_id,
                                  QuicErrorCode error_code,
                                  QuicStreamId last_good_stream_id,
                                  const QuicString& reason)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       error_code(error_code),
       last_good_stream_id(last_good_stream_id),
       reason_phrase(reason) {}
diff --git a/net/third_party/quic/core/frames/quic_goaway_frame.h b/net/third_party/quic/core/frames/quic_goaway_frame.h
index 62c9dd0..532692d 100644
--- a/net/third_party/quic/core/frames/quic_goaway_frame.h
+++ b/net/third_party/quic/core/frames/quic_goaway_frame.h
@@ -7,13 +7,13 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_error_codes.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 
 namespace quic {
 
-struct QUIC_EXPORT_PRIVATE QuicGoAwayFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicGoAwayFrame {
   QuicGoAwayFrame();
   QuicGoAwayFrame(QuicControlFrameId control_frame_id,
                   QuicErrorCode error_code,
@@ -23,6 +23,9 @@
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                                       const QuicGoAwayFrame& g);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
   QuicErrorCode error_code;
   QuicStreamId last_good_stream_id;
   QuicString reason_phrase;
diff --git a/net/third_party/quic/core/frames/quic_inlined_frame.h b/net/third_party/quic/core/frames/quic_inlined_frame.h
new file mode 100644
index 0000000..5c716eb
--- /dev/null
+++ b/net/third_party/quic/core/frames/quic_inlined_frame.h
@@ -0,0 +1,30 @@
+// Copyright (c) 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 NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
+#define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
+
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicInlinedFrame is the base class of all frame types that is inlined in the
+// QuicFrame class. It gurantees all inlined frame types contain a 'type' field
+// at offset 0, such that QuicFrame.type can get the correct frame type for both
+// inline and out-of-line frame types.
+template <typename DerivedT>
+struct QUIC_EXPORT_PRIVATE QuicInlinedFrame {
+  QuicInlinedFrame(QuicFrameType type) : type(type) {
+    static_assert(offsetof(DerivedT, type) == 0,
+                  "type must be the first field.");
+    static_assert(sizeof(DerivedT) <= 24,
+                  "Frames larger than 24 bytes should not be inlined.");
+  }
+  QuicFrameType type;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
diff --git a/net/third_party/quic/core/frames/quic_max_stream_id_frame.cc b/net/third_party/quic/core/frames/quic_max_stream_id_frame.cc
index 914a15f..0696f68b 100644
--- a/net/third_party/quic/core/frames/quic_max_stream_id_frame.cc
+++ b/net/third_party/quic/core/frames/quic_max_stream_id_frame.cc
@@ -6,11 +6,15 @@
 
 namespace quic {
 
-QuicMaxStreamIdFrame::QuicMaxStreamIdFrame() {}
+QuicMaxStreamIdFrame::QuicMaxStreamIdFrame()
+    : QuicInlinedFrame(MAX_STREAM_ID_FRAME),
+      control_frame_id(kInvalidControlFrameId) {}
 
 QuicMaxStreamIdFrame::QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id,
                                            QuicStreamId max_stream_id)
-    : QuicControlFrame(control_frame_id), max_stream_id(max_stream_id) {}
+    : QuicInlinedFrame(MAX_STREAM_ID_FRAME),
+      control_frame_id(control_frame_id),
+      max_stream_id(max_stream_id) {}
 
 std::ostream& operator<<(std::ostream& os, const QuicMaxStreamIdFrame& frame) {
   os << "{ control_frame_id: " << frame.control_frame_id
diff --git a/net/third_party/quic/core/frames/quic_max_stream_id_frame.h b/net/third_party/quic/core/frames/quic_max_stream_id_frame.h
index 6368afce..98c414a 100644
--- a/net/third_party/quic/core/frames/quic_max_stream_id_frame.h
+++ b/net/third_party/quic/core/frames/quic_max_stream_id_frame.h
@@ -7,14 +7,18 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
 
 namespace quic {
 
 // IETF format MAX_STREAM_ID frame.
 // This frame is used by the sender to inform the peer of the largest
 // stream id that the peer may open and that the sender will accept.
-struct QUIC_EXPORT_PRIVATE QuicMaxStreamIdFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicMaxStreamIdFrame
+    : public QuicInlinedFrame<QuicMaxStreamIdFrame> {
   QuicMaxStreamIdFrame();
   QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id,
                        QuicStreamId max_stream_id);
@@ -23,6 +27,10 @@
       std::ostream& os,
       const QuicMaxStreamIdFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   // The maximum stream id to support.
   QuicStreamId max_stream_id;
 };
diff --git a/net/third_party/quic/core/frames/quic_mtu_discovery_frame.h b/net/third_party/quic/core/frames/quic_mtu_discovery_frame.h
index 29c25a6..de1e461 100644
--- a/net/third_party/quic/core/frames/quic_mtu_discovery_frame.h
+++ b/net/third_party/quic/core/frames/quic_mtu_discovery_frame.h
@@ -5,13 +5,18 @@
 #ifndef NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
 #define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
 
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 
 namespace quic {
 
 // A path MTU discovery frame contains no payload and is serialized as a ping
 // frame.
-struct QUIC_EXPORT_PRIVATE QuicMtuDiscoveryFrame {};
+struct QUIC_EXPORT_PRIVATE QuicMtuDiscoveryFrame
+    : public QuicInlinedFrame<QuicMtuDiscoveryFrame> {
+  QuicMtuDiscoveryFrame() : QuicInlinedFrame(MTU_DISCOVERY_FRAME) {}
+};
 
 }  // namespace quic
 
diff --git a/net/third_party/quic/core/frames/quic_new_connection_id_frame.cc b/net/third_party/quic/core/frames/quic_new_connection_id_frame.cc
index 6629620..82bbcfa7 100644
--- a/net/third_party/quic/core/frames/quic_new_connection_id_frame.cc
+++ b/net/third_party/quic/core/frames/quic_new_connection_id_frame.cc
@@ -3,18 +3,21 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_new_connection_id_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 
 namespace quic {
 
 QuicNewConnectionIdFrame::QuicNewConnectionIdFrame()
-    : QuicControlFrame(0), connection_id(0), sequence_number(0) {}
+    : control_frame_id(kInvalidControlFrameId),
+      connection_id(0),
+      sequence_number(0) {}
 
 QuicNewConnectionIdFrame::QuicNewConnectionIdFrame(
     QuicControlFrameId control_frame_id,
     QuicConnectionId connection_id,
     QuicConnectionIdSequenceNumber sequence_number,
     const QuicUint128 stateless_reset_token)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       connection_id(connection_id),
       sequence_number(sequence_number),
       stateless_reset_token(stateless_reset_token) {}
diff --git a/net/third_party/quic/core/frames/quic_new_connection_id_frame.h b/net/third_party/quic/core/frames/quic_new_connection_id_frame.h
index 81a6517..04a1336 100644
--- a/net/third_party/quic/core/frames/quic_new_connection_id_frame.h
+++ b/net/third_party/quic/core/frames/quic_new_connection_id_frame.h
@@ -7,13 +7,13 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_error_codes.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_uint128.h"
 
 namespace quic {
 
-struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame {
   QuicNewConnectionIdFrame();
   QuicNewConnectionIdFrame(QuicControlFrameId control_frame_id,
                            QuicConnectionId connection_id,
@@ -24,6 +24,9 @@
       std::ostream& os,
       const QuicNewConnectionIdFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
   QuicConnectionId connection_id;
   QuicConnectionIdSequenceNumber sequence_number;
   QuicUint128 stateless_reset_token;
diff --git a/net/third_party/quic/core/frames/quic_new_token_frame.cc b/net/third_party/quic/core/frames/quic_new_token_frame.cc
new file mode 100644
index 0000000..6100790
--- /dev/null
+++ b/net/third_party/quic/core/frames/quic_new_token_frame.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 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 "net/third_party/quic/core/frames/quic_new_token_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
+#include "net/third_party/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicNewTokenFrame::QuicNewTokenFrame()
+    : control_frame_id(kInvalidControlFrameId) {}
+
+QuicNewTokenFrame::QuicNewTokenFrame(QuicControlFrameId control_frame_id,
+                                     QuicString token)
+    : control_frame_id(control_frame_id), token(token) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicNewTokenFrame& s) {
+  os << "{ control_frame_id: " << s.control_frame_id << ", token: " << s.token
+     << " }\n";
+  return os;
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/frames/quic_new_token_frame.h b/net/third_party/quic/core/frames/quic_new_token_frame.h
new file mode 100644
index 0000000..86a391e9
--- /dev/null
+++ b/net/third_party/quic/core/frames/quic_new_token_frame.h
@@ -0,0 +1,37 @@
+// Copyright (c) 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 NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
+#define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicNewTokenFrame {
+  QuicNewTokenFrame();
+  QuicNewTokenFrame(QuicControlFrameId control_frame_id, QuicString token);
+
+  friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+      std::ostream& os,
+      const QuicNewTokenFrame& s);
+
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
+  QuicString token;
+};
+static_assert(sizeof(QuicNewTokenFrame) <= 64,
+              "Keep the QuicNewTokenFrame size to a cacheline.");
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
diff --git a/net/third_party/quic/core/frames/quic_padding_frame.h b/net/third_party/quic/core/frames/quic_padding_frame.h
index 366a160..4313f3e 100644
--- a/net/third_party/quic/core/frames/quic_padding_frame.h
+++ b/net/third_party/quic/core/frames/quic_padding_frame.h
@@ -8,15 +8,18 @@
 #include <cstdint>
 #include <ostream>
 
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 
 namespace quic {
 
 // A padding frame contains no payload.
-struct QUIC_EXPORT_PRIVATE QuicPaddingFrame {
-  QuicPaddingFrame() : num_padding_bytes(-1) {}
+struct QUIC_EXPORT_PRIVATE QuicPaddingFrame
+    : public QuicInlinedFrame<QuicPaddingFrame> {
+  QuicPaddingFrame() : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(-1) {}
   explicit QuicPaddingFrame(int num_padding_bytes)
-      : num_padding_bytes(num_padding_bytes) {}
+      : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(num_padding_bytes) {}
 
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
       std::ostream& os,
diff --git a/net/third_party/quic/core/frames/quic_path_challenge_frame.cc b/net/third_party/quic/core/frames/quic_path_challenge_frame.cc
index 4497411..c47458d1 100644
--- a/net/third_party/quic/core/frames/quic_path_challenge_frame.cc
+++ b/net/third_party/quic/core/frames/quic_path_challenge_frame.cc
@@ -3,16 +3,18 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_path_challenge_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
 
 namespace quic {
 
-QuicPathChallengeFrame::QuicPathChallengeFrame() : QuicControlFrame(0) {}
+QuicPathChallengeFrame::QuicPathChallengeFrame()
+    : control_frame_id(kInvalidControlFrameId) {}
 
 QuicPathChallengeFrame::QuicPathChallengeFrame(
     QuicControlFrameId control_frame_id,
     const QuicPathFrameBuffer& data_buff)
-    : QuicControlFrame(control_frame_id) {
+    : control_frame_id(control_frame_id) {
   memcpy(data_buffer.data(), data_buff.data(), data_buffer.size());
 }
 
diff --git a/net/third_party/quic/core/frames/quic_path_challenge_frame.h b/net/third_party/quic/core/frames/quic_path_challenge_frame.h
index 513ea55..db0fd35 100644
--- a/net/third_party/quic/core/frames/quic_path_challenge_frame.h
+++ b/net/third_party/quic/core/frames/quic_path_challenge_frame.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
@@ -16,7 +15,7 @@
 // Size of the entire IETF Quic Path Challenge frame.
 const size_t kQuicPathChallengeFrameSize = kQuicPathFrameBufferSize;
 
-struct QUIC_EXPORT_PRIVATE QuicPathChallengeFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicPathChallengeFrame {
   QuicPathChallengeFrame();
   QuicPathChallengeFrame(QuicControlFrameId control_frame_id,
                          const QuicPathFrameBuffer& data_buff);
@@ -26,6 +25,10 @@
       std::ostream& os,
       const QuicPathChallengeFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   QuicPathFrameBuffer data_buffer;
 };
 }  // namespace quic
diff --git a/net/third_party/quic/core/frames/quic_path_response_frame.cc b/net/third_party/quic/core/frames/quic_path_response_frame.cc
index 31c95768..d6cd3f4 100644
--- a/net/third_party/quic/core/frames/quic_path_response_frame.cc
+++ b/net/third_party/quic/core/frames/quic_path_response_frame.cc
@@ -3,16 +3,18 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_path_response_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
 
 namespace quic {
 
-QuicPathResponseFrame::QuicPathResponseFrame() : QuicControlFrame(0) {}
+QuicPathResponseFrame::QuicPathResponseFrame()
+    : control_frame_id(kInvalidControlFrameId) {}
 
 QuicPathResponseFrame::QuicPathResponseFrame(
     QuicControlFrameId control_frame_id,
     const QuicPathFrameBuffer& data_buff)
-    : QuicControlFrame(control_frame_id) {
+    : control_frame_id(control_frame_id) {
   memcpy(data_buffer.data(), data_buff.data(), data_buffer.size());
 }
 
diff --git a/net/third_party/quic/core/frames/quic_path_response_frame.h b/net/third_party/quic/core/frames/quic_path_response_frame.h
index ebc624586..19368de1 100644
--- a/net/third_party/quic/core/frames/quic_path_response_frame.h
+++ b/net/third_party/quic/core/frames/quic_path_response_frame.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
@@ -16,7 +15,7 @@
 // Size of the entire IETF Quic Path Response frame.
 const size_t kQuicPathResponseFrameSize = kQuicPathFrameBufferSize;
 
-struct QUIC_EXPORT_PRIVATE QuicPathResponseFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicPathResponseFrame {
   QuicPathResponseFrame();
   QuicPathResponseFrame(QuicControlFrameId control_frame_id,
                         const QuicPathFrameBuffer& data_buff);
@@ -26,6 +25,10 @@
       std::ostream& os,
       const QuicPathResponseFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   QuicPathFrameBuffer data_buffer;
 };
 }  // namespace quic
diff --git a/net/third_party/quic/core/frames/quic_ping_frame.cc b/net/third_party/quic/core/frames/quic_ping_frame.cc
index 3b64f18..4d18752 100644
--- a/net/third_party/quic/core/frames/quic_ping_frame.cc
+++ b/net/third_party/quic/core/frames/quic_ping_frame.cc
@@ -6,10 +6,11 @@
 
 namespace quic {
 
-QuicPingFrame::QuicPingFrame() {}
+QuicPingFrame::QuicPingFrame()
+    : QuicInlinedFrame(PING_FRAME), control_frame_id(kInvalidControlFrameId) {}
 
 QuicPingFrame::QuicPingFrame(QuicControlFrameId control_frame_id)
-    : QuicControlFrame(control_frame_id) {}
+    : QuicInlinedFrame(PING_FRAME), control_frame_id(control_frame_id) {}
 
 std::ostream& operator<<(std::ostream& os, const QuicPingFrame& ping_frame) {
   os << "{ control_frame_id: " << ping_frame.control_frame_id << " }\n";
diff --git a/net/third_party/quic/core/frames/quic_ping_frame.h b/net/third_party/quic/core/frames/quic_ping_frame.h
index 0ee991e..216b88d 100644
--- a/net/third_party/quic/core/frames/quic_ping_frame.h
+++ b/net/third_party/quic/core/frames/quic_ping_frame.h
@@ -5,19 +5,27 @@
 #ifndef NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
 #define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
 
 namespace quic {
 
 // A ping frame contains no payload, though it is retransmittable,
 // and ACK'd just like other normal frames.
-struct QUIC_EXPORT_PRIVATE QuicPingFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicPingFrame
+    : public QuicInlinedFrame<QuicPingFrame> {
   QuicPingFrame();
   explicit QuicPingFrame(QuicControlFrameId control_frame_id);
 
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
       std::ostream& os,
       const QuicPingFrame& ping_frame);
+
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/frames/quic_rst_stream_frame.cc b/net/third_party/quic/core/frames/quic_rst_stream_frame.cc
index fd8cc0bf..aa3563a 100644
--- a/net/third_party/quic/core/frames/quic_rst_stream_frame.cc
+++ b/net/third_party/quic/core/frames/quic_rst_stream_frame.cc
@@ -3,17 +3,21 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_rst_stream_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 
 namespace quic {
 
 QuicRstStreamFrame::QuicRstStreamFrame()
-    : stream_id(0), error_code(QUIC_STREAM_NO_ERROR), byte_offset(0) {}
+    : control_frame_id(kInvalidControlFrameId),
+      stream_id(0),
+      error_code(QUIC_STREAM_NO_ERROR),
+      byte_offset(0) {}
 
 QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id,
                                        QuicStreamId stream_id,
                                        QuicRstStreamErrorCode error_code,
                                        QuicStreamOffset bytes_written)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       stream_id(stream_id),
       error_code(error_code),
       byte_offset(bytes_written) {}
@@ -22,7 +26,7 @@
                                        QuicStreamId stream_id,
                                        uint16_t ietf_error_code,
                                        QuicStreamOffset bytes_written)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       stream_id(stream_id),
       ietf_error_code(ietf_error_code),
       byte_offset(bytes_written) {}
diff --git a/net/third_party/quic/core/frames/quic_rst_stream_frame.h b/net/third_party/quic/core/frames/quic_rst_stream_frame.h
index c21e83a..a03f5f7 100644
--- a/net/third_party/quic/core/frames/quic_rst_stream_frame.h
+++ b/net/third_party/quic/core/frames/quic_rst_stream_frame.h
@@ -7,12 +7,12 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_error_codes.h"
+#include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
 
-struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame {
   QuicRstStreamFrame();
   QuicRstStreamFrame(QuicControlFrameId control_frame_id,
                      QuicStreamId stream_id,
@@ -27,6 +27,10 @@
       std::ostream& os,
       const QuicRstStreamFrame& r);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   QuicStreamId stream_id;
 
   // Caller must know whether IETF- or Google- QUIC is in use and
diff --git a/net/third_party/quic/core/frames/quic_stop_sending_frame.cc b/net/third_party/quic/core/frames/quic_stop_sending_frame.cc
index 00794f0..8f1a71ad 100644
--- a/net/third_party/quic/core/frames/quic_stop_sending_frame.cc
+++ b/net/third_party/quic/core/frames/quic_stop_sending_frame.cc
@@ -3,17 +3,20 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_stop_sending_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 
 namespace quic {
 
 QuicStopSendingFrame::QuicStopSendingFrame()
-    : stream_id(0), application_error_code(0) {}
+    : control_frame_id(kInvalidControlFrameId),
+      stream_id(0),
+      application_error_code(0) {}
 
 QuicStopSendingFrame::QuicStopSendingFrame(
     QuicControlFrameId control_frame_id,
     QuicStreamId stream_id,
     QuicApplicationErrorCode application_error_code)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       stream_id(stream_id),
       application_error_code(application_error_code) {}
 
diff --git a/net/third_party/quic/core/frames/quic_stop_sending_frame.h b/net/third_party/quic/core/frames/quic_stop_sending_frame.h
index 9ade111..52c1931b 100644
--- a/net/third_party/quic/core/frames/quic_stop_sending_frame.h
+++ b/net/third_party/quic/core/frames/quic_stop_sending_frame.h
@@ -7,12 +7,12 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
 #include "net/third_party/quic/core/quic_error_codes.h"
+#include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
 
-struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame {
   QuicStopSendingFrame();
   QuicStopSendingFrame(QuicControlFrameId control_frame_id,
                        QuicStreamId stream_id,
@@ -22,6 +22,9 @@
       std::ostream& os,
       const QuicStopSendingFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
   QuicStreamId stream_id;
   QuicApplicationErrorCode application_error_code;
 };
diff --git a/net/third_party/quic/core/frames/quic_stream_frame.cc b/net/third_party/quic/core/frames/quic_stream_frame.cc
index d0dcbb8..8f9ca7a 100644
--- a/net/third_party/quic/core/frames/quic_stream_frame.cc
+++ b/net/third_party/quic/core/frames/quic_stream_frame.cc
@@ -27,7 +27,7 @@
                                  QuicStreamOffset offset,
                                  const char* data_buffer,
                                  QuicPacketLength data_length)
-    : type(STREAM_FRAME),
+    : QuicInlinedFrame(STREAM_FRAME),
       fin(fin),
       data_length(data_length),
       stream_id(stream_id),
diff --git a/net/third_party/quic/core/frames/quic_stream_frame.h b/net/third_party/quic/core/frames/quic_stream_frame.h
index 71aa120..9e51db02 100644
--- a/net/third_party/quic/core/frames/quic_stream_frame.h
+++ b/net/third_party/quic/core/frames/quic_stream_frame.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <ostream>
 
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
 #include "net/third_party/quic/core/quic_buffer_allocator.h"
 #include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
@@ -15,7 +16,8 @@
 
 namespace quic {
 
-struct QUIC_EXPORT_PRIVATE QuicStreamFrame {
+struct QUIC_EXPORT_PRIVATE QuicStreamFrame
+    : public QuicInlinedFrame<QuicStreamFrame> {
   QuicStreamFrame();
   QuicStreamFrame(QuicStreamId stream_id,
                   bool fin,
@@ -29,11 +31,6 @@
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                                       const QuicStreamFrame& s);
 
-  // The union in QuicFrame requires the QuicStreamFrame |type| field to be the
-  // first field in the frame.
-  QuicFrameType type;
-
-  // QuicStreamFrame fields.
   bool fin;
   QuicPacketLength data_length;
   QuicStreamId stream_id;
diff --git a/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.cc b/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.cc
index 07346a63..1cad68c 100644
--- a/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.cc
+++ b/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.cc
@@ -6,12 +6,16 @@
 
 namespace quic {
 
-QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame() {}
+QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame()
+    : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME),
+      control_frame_id(kInvalidControlFrameId) {}
 
 QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame(
     QuicControlFrameId control_frame_id,
     QuicStreamId stream_id)
-    : QuicControlFrame(control_frame_id), stream_id(stream_id) {}
+    : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME),
+      control_frame_id(control_frame_id),
+      stream_id(stream_id) {}
 
 std::ostream& operator<<(std::ostream& os,
                          const QuicStreamIdBlockedFrame& frame) {
diff --git a/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.h b/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.h
index 47d7a47..c3087450 100644
--- a/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.h
+++ b/net/third_party/quic/core/frames/quic_stream_id_blocked_frame.h
@@ -7,7 +7,10 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
+#include "net/third_party/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
 
 namespace quic {
 
@@ -15,7 +18,8 @@
 // The sender uses this to inform the peer that the sender wished to
 // open a new stream but was blocked from doing so due due to the
 // maximum stream ID limit set by the peer (via a MAX_STREAM_ID frame)
-struct QUIC_EXPORT_PRIVATE QuicStreamIdBlockedFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicStreamIdBlockedFrame
+    : public QuicInlinedFrame<QuicStreamIdBlockedFrame> {
   QuicStreamIdBlockedFrame();
   QuicStreamIdBlockedFrame(QuicControlFrameId control_frame_id,
                            QuicStreamId stream_id);
@@ -24,6 +28,10 @@
       std::ostream& os,
       const QuicStreamIdBlockedFrame& frame);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   QuicStreamId stream_id;
 };
 
diff --git a/net/third_party/quic/core/frames/quic_window_update_frame.cc b/net/third_party/quic/core/frames/quic_window_update_frame.cc
index cbd23442..2445ba8 100644
--- a/net/third_party/quic/core/frames/quic_window_update_frame.cc
+++ b/net/third_party/quic/core/frames/quic_window_update_frame.cc
@@ -3,16 +3,18 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quic/core/quic_constants.h"
 
 namespace quic {
 
-QuicWindowUpdateFrame::QuicWindowUpdateFrame() {}
+QuicWindowUpdateFrame::QuicWindowUpdateFrame()
+    : control_frame_id(kInvalidControlFrameId) {}
 
 QuicWindowUpdateFrame::QuicWindowUpdateFrame(
     QuicControlFrameId control_frame_id,
     QuicStreamId stream_id,
     QuicStreamOffset byte_offset)
-    : QuicControlFrame(control_frame_id),
+    : control_frame_id(control_frame_id),
       stream_id(stream_id),
       byte_offset(byte_offset) {}
 
diff --git a/net/third_party/quic/core/frames/quic_window_update_frame.h b/net/third_party/quic/core/frames/quic_window_update_frame.h
index 73b4448b..bc434f7 100644
--- a/net/third_party/quic/core/frames/quic_window_update_frame.h
+++ b/net/third_party/quic/core/frames/quic_window_update_frame.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "net/third_party/quic/core/frames/quic_control_frame.h"
+#include "net/third_party/quic/core/quic_types.h"
 
 namespace quic {
 
@@ -16,7 +16,7 @@
 // than a window delta.
 // TODO(rjshade): A possible future optimization is to make stream_id and
 //                byte_offset variable length, similar to stream frames.
-struct QUIC_EXPORT_PRIVATE QuicWindowUpdateFrame : public QuicControlFrame {
+struct QUIC_EXPORT_PRIVATE QuicWindowUpdateFrame {
   QuicWindowUpdateFrame();
   QuicWindowUpdateFrame(QuicControlFrameId control_frame_id,
                         QuicStreamId stream_id,
@@ -26,6 +26,10 @@
       std::ostream& os,
       const QuicWindowUpdateFrame& w);
 
+  // A unique identifier of this control frame. 0 when this frame is received,
+  // and non-zero when sent.
+  QuicControlFrameId control_frame_id;
+
   // The stream this frame applies to.  0 is a special case meaning the overall
   // connection rather than a specific stream.
   QuicStreamId stream_id;
diff --git a/net/third_party/quic/core/http/quic_server_session_base.cc b/net/third_party/quic/core/http/quic_server_session_base.cc
index 7778ab7..1326676 100644
--- a/net/third_party/quic/core/http/quic_server_session_base.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base.cc
@@ -8,6 +8,7 @@
 #include "net/third_party/quic/core/quic_connection.h"
 #include "net/third_party/quic/core/quic_stream.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
@@ -216,12 +217,15 @@
     QUIC_BUG << "Encryption not established so no outgoing stream created.";
     return false;
   }
-  if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) {
-    VLOG(1) << "No more streams should be created. "
-            << "Already " << GetNumOpenOutgoingStreams() << " open.";
-    return false;
+  if (!GetQuicFlag(FLAGS_quic_use_common_stream_check)) {
+    if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) {
+      VLOG(1) << "No more streams should be created. "
+              << "Already " << GetNumOpenOutgoingStreams() << " open.";
+      return false;
+    }
   }
-  return true;
+  QUIC_FLAG_COUNT_N(quic_use_common_stream_check, 2, 2);
+  return CanOpenNextOutgoingStream();
 }
 
 QuicCryptoServerStreamBase* QuicServerSessionBase::GetMutableCryptoStream() {
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session.cc b/net/third_party/quic/core/http/quic_spdy_client_session.cc
index 042295c..68266ff 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session.cc
@@ -9,6 +9,8 @@
 #include "net/third_party/quic/core/http/spdy_utils.h"
 #include "net/third_party/quic/core/quic_server_id.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
@@ -44,17 +46,26 @@
     QUIC_DLOG(INFO) << "Encryption not active so no outgoing stream created.";
     return false;
   }
-  if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) {
-    QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
-                    << "Already " << GetNumOpenOutgoingStreams() << " open.";
-    return false;
+  if (!GetQuicFlag(FLAGS_quic_use_common_stream_check)) {
+    if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) {
+      QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+                      << "Already " << GetNumOpenOutgoingStreams() << " open.";
+      return false;
+    }
+    if (goaway_received() && respect_goaway_) {
+      QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+                      << "Already received goaway.";
+      return false;
+    }
+    return true;
   }
   if (goaway_received() && respect_goaway_) {
     QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
                     << "Already received goaway.";
     return false;
   }
-  return true;
+  QUIC_FLAG_COUNT_N(quic_use_common_stream_check, 1, 2);
+  return CanOpenNextOutgoingStream();
 }
 
 QuicSpdyClientStream* QuicSpdyClientSession::CreateOutgoingDynamicStream() {
diff --git a/net/third_party/quic/core/http/spdy_utils.cc b/net/third_party/quic/core/http/spdy_utils.cc
index 31e2d09..0477824 100644
--- a/net/third_party/quic/core/http/spdy_utils.cc
+++ b/net/third_party/quic/core/http/spdy_utils.cc
@@ -14,10 +14,10 @@
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
-#include "net/third_party/quic/platform/api/quic_url_utils.h"
 #include "net/third_party/spdy/core/spdy_frame_builder.h"
 #include "net/third_party/spdy/core/spdy_framer.h"
 #include "net/third_party/spdy/core/spdy_protocol.h"
+#include "url/gurl.h"
 
 using spdy::SpdyHeaderBlock;
 
@@ -179,7 +179,7 @@
   }
   QuicStringPiece path = it->second;
 
-  return QuicUrlUtils::GetPushPromiseUrl(scheme, authority, path);
+  return GetPushPromiseUrl(scheme, authority, path);
 }
 
 // static
@@ -187,13 +187,13 @@
     const SpdyHeaderBlock& headers) {
   // TODO(fayang): Consider just checking out the value of the ":authority" key
   // in headers.
-  return QuicUrlUtils::HostName(GetPromisedUrlFromHeaders(headers));
+  return GURL(GetPromisedUrlFromHeaders(headers)).host();
 }
 
 // static
 bool SpdyUtils::PromisedUrlIsValid(const SpdyHeaderBlock& headers) {
   QuicString url(GetPromisedUrlFromHeaders(headers));
-  return !url.empty() && QuicUrlUtils::IsValidUrl(url);
+  return !url.empty() && GURL(url).is_valid();
 }
 
 // static
@@ -217,4 +217,137 @@
   return true;
 }
 
+// static
+QuicString SpdyUtils::GetPushPromiseUrl(QuicStringPiece scheme,
+                                        QuicStringPiece authority,
+                                        QuicStringPiece path) {
+  // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the
+  // path and query parts of the target URI (the "path-absolute" production
+  // and optionally a '?' character followed by the "query" production (see
+  // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the
+  // value '*' for the ":path" pseudo-header field.
+  //
+  // This pseudo-header field MUST NOT be empty for "http" or "https" URIs;
+  // "http" or "https" URIs that do not contain a path MUST include a value of
+  // '/'. The exception to this rule is an OPTIONS request for an "http" or
+  // "https" URI that does not include a path component; these MUST include a
+  // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4).
+  //
+  // In addition to the above restriction from RFC 7540, note that RFC3986
+  // defines the "path-absolute" construction as starting with "/" but not "//".
+  //
+  // RFC 7540, Section  8.2.1:  The header fields in PUSH_PROMISE and any
+  // subsequent CONTINUATION frames MUST be a valid and complete set of request
+  // header fields (Section 8.1.2.3).  The server MUST include a method in the
+  // ":method" pseudo-header field that is safe and cacheable.
+  //
+  // RFC 7231, Section  4.2.1:
+  // ... this specification defines GET, HEAD, and POST as cacheable, ...
+  //
+  // Since the OPTIONS method is not cacheable, it cannot be the method of a
+  // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section
+  // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be
+  // "*").
+  if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) {
+    return QuicString();
+  }
+
+  // Validate the scheme; this is to ensure a scheme of "foo://bar" is not
+  // parsed as a URL of "foo://bar://baz" when combined with a host of "baz".
+  std::string canonical_scheme;
+  url::StdStringCanonOutput canon_output(&canonical_scheme);
+  url::Component canon_component;
+  url::Component scheme_component(0, scheme.size());
+
+  if (!url::CanonicalizeScheme(scheme.data(), scheme_component, &canon_output,
+                               &canon_component) ||
+      !canon_component.is_nonempty() || canon_component.begin != 0) {
+    return QuicString();
+  }
+  canonical_scheme.resize(canon_component.len + 1);
+
+  // Validate the authority; this is to ensure an authority such as
+  // "host/path" is not accepted, as when combined with a scheme like
+  // "http://", could result in a URL of "http://host/path".
+  url::Component auth_component(0, authority.size());
+  url::Component username_component;
+  url::Component password_component;
+  url::Component host_component;
+  url::Component port_component;
+
+  url::ParseAuthority(authority.data(), auth_component, &username_component,
+                      &password_component, &host_component, &port_component);
+
+  // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated
+  // "userinfo" subcomponent for "http" or "https" schemed URIs.
+  //
+  // Note: Although |canonical_scheme| has not yet been checked for that, as
+  // it is performed later in processing, only "http" and "https" schemed
+  // URIs are supported for PUSH.
+  if (username_component.is_valid() || password_component.is_valid()) {
+    return QuicString();
+  }
+
+  // Failed parsing or no host present. ParseAuthority() will ensure that
+  // host_component + port_component cover the entire string, if
+  // username_component and password_component are not present.
+  if (!host_component.is_nonempty()) {
+    return QuicString();
+  }
+
+  // Validate the port (if present; it's optional).
+  int parsed_port_number = url::PORT_INVALID;
+  if (port_component.is_nonempty()) {
+    parsed_port_number = url::ParsePort(authority.data(), port_component);
+    if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) {
+      return QuicString();
+    }
+  }
+
+  // Validate the host by attempting to canoncalize it. Invalid characters
+  // will result in a canonicalization failure (e.g. '/')
+  std::string canon_host;
+  canon_output = url::StdStringCanonOutput(&canon_host);
+  canon_component.reset();
+  if (!url::CanonicalizeHost(authority.data(), host_component, &canon_output,
+                             &canon_component) ||
+      !canon_component.is_nonempty() || canon_component.begin != 0) {
+    return QuicString();
+  }
+
+  // At this point, "authority" has been validated to either be of the form
+  // 'host:port' or 'host', with 'host' being a valid domain or IP address,
+  // and 'port' (if present), being a valid port. Attempt to construct a
+  // URL of just the (scheme, host, port), which should be safe and will not
+  // result in ambiguous parsing.
+  //
+  // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed
+  // URIs, consistent with the other restrictions enforced above.
+  //
+  // Note: url::CanonicalizeScheme() will have added the ':' to
+  // |canonical_scheme|.
+  GURL origin_url(canonical_scheme + "//" + std::string(authority));
+  if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() ||
+      // The following checks are merely defense in depth.
+      origin_url.has_username() || origin_url.has_password() ||
+      (origin_url.has_path() && origin_url.path_piece() != "/") ||
+      origin_url.has_query() || origin_url.has_ref()) {
+    return QuicString();
+  }
+
+  // Attempt to parse the path.
+  std::string spec = origin_url.GetWithEmptyPath().spec();
+  spec.pop_back();  // Remove the '/', as ":path" must contain it.
+  spec.append(std::string(path));
+
+  // Attempt to parse the full URL, with the path as well. Ensure there is no
+  // fragment to the query.
+  GURL full_url(spec);
+  if (!full_url.is_valid() || full_url.has_ref()) {
+    return QuicString();
+  }
+
+  return full_url.spec();
+}
+
 }  // namespace quic
diff --git a/net/third_party/quic/core/http/spdy_utils.h b/net/third_party/quic/core/http/spdy_utils.h
index ec1bf11..1a5881a5 100644
--- a/net/third_party/quic/core/http/spdy_utils.h
+++ b/net/third_party/quic/core/http/spdy_utils.h
@@ -56,6 +56,13 @@
   // which must be fully-qualified.
   static bool PopulateHeaderBlockFromUrl(const QuicString url,
                                          spdy::SpdyHeaderBlock* headers);
+
+  // Returns a canonical, valid URL for a PUSH_PROMISE with the specified
+  // ":scheme", ":authority", and ":path" header fields, or an empty
+  // string if the resulting URL is not valid or supported.
+  static QuicString GetPushPromiseUrl(QuicStringPiece scheme,
+                                      QuicStringPiece authority,
+                                      QuicStringPiece path);
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/http/spdy_utils_test.cc b/net/third_party/quic/core/http/spdy_utils_test.cc
index 3c6b618..b6bf144 100644
--- a/net/third_party/quic/core/http/spdy_utils_test.cc
+++ b/net/third_party/quic/core/http/spdy_utils_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "net/third_party/quic/core/http/spdy_utils.h"
+#include "net/third_party/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
@@ -380,5 +381,121 @@
       SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers));
 }
 
+using PushPromiseUrlTest = QuicTest;
+
+TEST_F(PushPromiseUrlTest, GetPushPromiseUrl) {
+  // Test rejection of various inputs.
+  EXPECT_EQ("",
+            SpdyUtils::GetPushPromiseUrl("file", "localhost", "/etc/password"));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("file", "",
+                                             "/C:/Windows/System32/Config/"));
+  EXPECT_EQ("",
+            SpdyUtils::GetPushPromiseUrl("", "https://www.google.com", "/"));
+
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https://www.google.com",
+                                             "www.google.com", "/"));
+  EXPECT_EQ("",
+            SpdyUtils::GetPushPromiseUrl("https://", "www.google.com", "/"));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "/"));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "www.google.com/"));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com/", "/"));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com", ""));
+  EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google", ".com/"));
+
+  // Test acception/rejection of various input combinations.
+  // |input_headers| is an array of pairs. The first value of each pair is a
+  // string that will be used as one of the inputs of GetPushPromiseUrl(). The
+  // second value of each pair is a bitfield where the lowest 3 bits indicate
+  // for which headers that string is valid (in a PUSH_PROMISE). For example,
+  // the string "http" would be valid for both the ":scheme" and ":authority"
+  // headers, so the bitfield paired with it is set to SCHEME | AUTH.
+  const unsigned char SCHEME = (1u << 0);
+  const unsigned char AUTH = (1u << 1);
+  const unsigned char PATH = (1u << 2);
+  const std::pair<const char*, unsigned char> input_headers[] = {
+      {"http", SCHEME | AUTH},
+      {"https", SCHEME | AUTH},
+      {"hTtP", SCHEME | AUTH},
+      {"HTTPS", SCHEME | AUTH},
+      {"www.google.com", AUTH},
+      {"90af90e0", AUTH},
+      {"12foo%20-bar:00001233", AUTH},
+      {"GOO\u200b\u2060\ufeffgoo", AUTH},
+      {"192.168.0.5", AUTH},
+      {"[::ffff:192.168.0.1.]", AUTH},
+      {"http:", AUTH},
+      {"bife l", AUTH},
+      {"/", PATH},
+      {"/foo/bar/baz", PATH},
+      {"/%20-2DVdkj.cie/foe_.iif/", PATH},
+      {"http://", 0},
+      {":443", 0},
+      {":80/eddd", 0},
+      {"google.com:-0", 0},
+      {"google.com:65536", 0},
+      {"http://google.com", 0},
+      {"http://google.com:39", 0},
+      {"//google.com/foo", 0},
+      {".com/", 0},
+      {"http://www.google.com/", 0},
+      {"http://foo:439", 0},
+      {"[::ffff:192.168", 0},
+      {"]/", 0},
+      {"//", 0}};
+  for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) {
+    bool should_accept = (input_headers[i].second & SCHEME);
+    for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) {
+      bool should_accept_2 = should_accept && (input_headers[j].second & AUTH);
+      for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) {
+        // |should_accept_3| indicates whether or not GetPushPromiseUrl() is
+        // expected to accept this input combination.
+        bool should_accept_3 =
+            should_accept_2 && (input_headers[k].second & PATH);
+
+        std::string url = SpdyUtils::GetPushPromiseUrl(input_headers[i].first,
+                                                       input_headers[j].first,
+                                                       input_headers[k].first);
+
+        ::testing::AssertionResult result = ::testing::AssertionSuccess();
+        if (url.empty() == should_accept_3) {
+          result = ::testing::AssertionFailure()
+                   << "GetPushPromiseUrl() accepted/rejected the inputs when "
+                      "it shouldn't have."
+                   << std::endl
+                   << "     scheme: " << input_headers[i].first << std::endl
+                   << "  authority: " << input_headers[j].first << std::endl
+                   << "       path: " << input_headers[k].first << std::endl
+                   << "Output: " << url << std::endl;
+        }
+        ASSERT_TRUE(result);
+      }
+    }
+  }
+
+  // Test canonicalization of various valid inputs.
+  EXPECT_EQ("http://www.google.com/",
+            SpdyUtils::GetPushPromiseUrl("http", "www.google.com", "/"));
+  EXPECT_EQ(
+      "https://www.goo-gle.com/fOOo/baRR",
+      SpdyUtils::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm", "/fOOo/baRR"));
+  EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce",
+            SpdyUtils::GetPushPromiseUrl("hTtPs", "Www.gOo-Gle.Com:000003278",
+                                         "/pAth/To/reSOurce"));
+  EXPECT_EQ("https://foo%20bar/foo/bar/baz",
+            SpdyUtils::GetPushPromiseUrl("https", "foo bar", "/foo/bar/baz"));
+  EXPECT_EQ("http://foo.com:70/e/",
+            SpdyUtils::GetPushPromiseUrl("http", "foo.com:0000070", "/e/"));
+  EXPECT_EQ(
+      "http://192.168.0.1:70/e/",
+      SpdyUtils::GetPushPromiseUrl("http", "0300.0250.00.01:0070", "/e/"));
+  EXPECT_EQ("http://192.168.0.1/e/",
+            SpdyUtils::GetPushPromiseUrl("http", "0xC0a80001", "/e/"));
+  EXPECT_EQ("http://[::c0a8:1]/",
+            SpdyUtils::GetPushPromiseUrl("http", "[::192.168.0.1]", "/"));
+  EXPECT_EQ(
+      "https://[::ffff:c0a8:1]/",
+      SpdyUtils::GetPushPromiseUrl("https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/"));
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_constants.h b/net/third_party/quic/core/qpack/qpack_constants.h
index 6ae0c9f..585577d 100644
--- a/net/third_party/quic/core/qpack/qpack_constants.h
+++ b/net/third_party/quic/core/qpack/qpack_constants.h
@@ -9,8 +9,39 @@
 
 namespace quic {
 
+// TODO(bnc): Move this into HpackVarintEncoder.
+// The integer encoder can encode up to 2^64-1, which can take up to 10 bytes
+// (each carrying 7 bits) after the prefix.
+const uint8_t kMaxExtensionBytesForVarintEncoding = 10;
+
 // Wire format defined in
-// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.4.2.
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5
+
+// Encoder stream
+
+// 5.2.1 Insert With Name Reference
+const uint8_t kInsertWithNameReferenceOpcode = 0b10000000;
+const uint8_t kInsertWithNameReferenceOpcodeMask = 0b10000000;
+const uint8_t kInsertWithNameReferenceStaticBit = 0b01000000;
+const uint8_t kInsertWithNameReferenceNameIndexPrefixLength = 6;
+
+// 5.2.2 Insert Without Name Reference
+const uint8_t kInsertWithoutNameReferenceOpcode = 0b01000000;
+const uint8_t kInsertWithoutNameReferenceOpcodeMask = 0b11000000;
+const uint8_t kInsertWithoutNameReferenceNameHuffmanBit = 0b00100000;
+const uint8_t kInsertWithoutNameReferenceNameLengthPrefixLength = 5;
+
+// 5.2.3 Duplicate
+const uint8_t kDuplicateOpcode = 0b00000000;
+const uint8_t kDuplicateOpcodeMask = 0b11100000;
+const uint8_t kDuplicateIndexPrefixLength = 5;
+
+// 5.2.4 Dynamic Table Size Update
+const uint8_t kDynamicTableSizeUpdateOpcode = 0b00100000;
+const uint8_t kDynamicTableSizeUpdateOpcodeMask = 0b11100000;
+const uint8_t kDynamicTableSizeUpdateMaxSizePrefixLength = 5;
+
+// Request and push streams
 
 // 5.4.2.1. Indexed Header Field
 const uint8_t kIndexedHeaderFieldOpcode = 0b10000000;
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.cc b/net/third_party/quic/core/qpack/qpack_decoder.cc
index e269855..13d1fc1 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder.cc
@@ -45,8 +45,11 @@
       case State::kVarintDone:
         DoVarintDone();
         break;
-      case State::kNameString:
-        bytes_consumed = DoNameString(data);
+      case State::kReadName:
+        bytes_consumed = DoReadName(data);
+        break;
+      case State::kDecodeName:
+        DoDecodeName();
         break;
       case State::kValueLengthStart:
         bytes_consumed = DoValueLengthStart(data);
@@ -57,8 +60,11 @@
       case State::kValueLengthDone:
         DoValueLengthDone();
         break;
-      case State::kValueString:
-        bytes_consumed = DoValueString(data);
+      case State::kReadValue:
+        bytes_consumed = DoReadValue(data);
+        break;
+      case State::kDecodeValue:
+        DoDecodeValue();
         break;
       case State::kDone:
         DoDone();
@@ -76,7 +82,8 @@
 
     // Stop processing if no more data but next state would require it.
     if (data.empty() && (state_ != State::kVarintDone) &&
-        (state_ != State::kValueLengthDone) && (state_ != State::kDone)) {
+        (state_ != State::kDecodeName) && (state_ != State::kValueLengthDone) &&
+        (state_ != State::kDecodeValue) && (state_ != State::kDone)) {
       return;
     }
   }
@@ -190,7 +197,12 @@
     name_length_ = varint_decoder_.value();
     name_.clear();
     name_.reserve(name_length_);
-    state_ = State::kNameString;
+    // Do not handle empty names differently.  (They are probably forbidden by
+    // higher layers, but it is not enforced in this class.)  If there is no
+    // more data to read, then processing stalls, but the instruction is not
+    // complete without the value, so OnHeaderDecoded() could not be called yet
+    // anyway.
+    state_ = State::kReadName;
     return;
   }
 
@@ -212,17 +224,23 @@
   state_ = State::kStart;
 }
 
-size_t QpackDecoder::ProgressiveDecoder::DoNameString(QuicStringPiece data) {
+size_t QpackDecoder::ProgressiveDecoder::DoReadName(QuicStringPiece data) {
   DCHECK(!data.empty());
+  // |name_length_| might be zero.
   DCHECK_LE(name_.size(), name_length_);
 
   size_t bytes_consumed = std::min(name_length_ - name_.size(), data.size());
   name_.append(data.data(), bytes_consumed);
 
-  if (name_.size() < name_length_) {
-    return bytes_consumed;
+  DCHECK_LE(name_.size(), name_length_);
+  if (name_.size() == name_length_) {
+    state_ = State::kDecodeName;
   }
 
+  return bytes_consumed;
+}
+
+void QpackDecoder::ProgressiveDecoder::DoDecodeName() {
   DCHECK_EQ(name_.size(), name_length_);
 
   if (is_huffman_) {
@@ -232,13 +250,12 @@
     huffman_decoder_.Decode(name_, &decoded_name);
     if (!huffman_decoder_.InputProperlyTerminated()) {
       OnError("Error in Huffman-encoded name.");
-      return bytes_consumed;
+      return;
     }
     name_ = decoded_name;
   }
 
   state_ = State::kValueLengthStart;
-  return bytes_consumed;
 }
 
 size_t QpackDecoder::ProgressiveDecoder::DoValueLengthStart(
@@ -293,26 +310,34 @@
   value_.clear();
   value_length_ = varint_decoder_.value();
 
+  // If value is empty, skip DoReadValue() and DoDecodeValue() and jump directly
+  // to DoDone().  This is so that OnHeaderDecoded() is called even if there is
+  // no more data.
   if (value_length_ == 0) {
     state_ = State::kDone;
     return;
   }
 
   value_.reserve(value_length_);
-  state_ = State::kValueString;
+  state_ = State::kReadValue;
 }
 
-size_t QpackDecoder::ProgressiveDecoder::DoValueString(QuicStringPiece data) {
+size_t QpackDecoder::ProgressiveDecoder::DoReadValue(QuicStringPiece data) {
   DCHECK(!data.empty());
+  DCHECK_LT(0u, value_length_);
   DCHECK_LT(value_.size(), value_length_);
 
   size_t bytes_consumed = std::min(value_length_ - value_.size(), data.size());
   value_.append(data.data(), bytes_consumed);
 
-  if (value_.size() < value_length_) {
-    return bytes_consumed;
+  DCHECK_LE(value_.size(), value_length_);
+  if (value_.size() == value_length_) {
+    state_ = State::kDecodeValue;
   }
+  return bytes_consumed;
+}
 
+void QpackDecoder::ProgressiveDecoder::DoDecodeValue() {
   DCHECK_EQ(value_.size(), value_length_);
 
   if (is_huffman_) {
@@ -322,13 +347,12 @@
     huffman_decoder_.Decode(value_, &decoded_value);
     if (!huffman_decoder_.InputProperlyTerminated()) {
       OnError("Error in Huffman-encoded value.");
-      return bytes_consumed;
+      return;
     }
     value_ = decoded_value;
   }
 
   state_ = State::kDone;
-  return bytes_consumed;
 }
 
 void QpackDecoder::ProgressiveDecoder::DoDone() {
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.h b/net/third_party/quic/core/qpack/qpack_decoder.h
index 1aa554a..3d8e0cb5 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.h
+++ b/net/third_party/quic/core/qpack/qpack_decoder.h
@@ -70,14 +70,18 @@
       kStart,
       kVarintResume,
       kVarintDone,
-      // This might be followed by the name as a string literal.
-      kNameString,
+      // This might be followed by the name as a string literal,
+      // optionally Huffman encoded.
+      kReadName,
+      kDecodeName,
       // This might be followed by the length of the value.
       kValueLengthStart,
       kValueLengthResume,
       kValueLengthDone,
-      // This might be followed by the value as a string literal.
-      kValueString,
+      // This might be followed by the value as a string literal,
+      // optionally Huffman encoded.
+      kReadValue,
+      kDecodeValue,
       kDone,
     };
 
@@ -86,11 +90,13 @@
     size_t DoStart(QuicStringPiece data);
     size_t DoVarintResume(QuicStringPiece data);
     void DoVarintDone();
-    size_t DoNameString(QuicStringPiece data);
+    size_t DoReadName(QuicStringPiece data);
+    void DoDecodeName();
     size_t DoValueLengthStart(QuicStringPiece data);
     size_t DoValueLengthResume(QuicStringPiece data);
     void DoValueLengthDone();
-    size_t DoValueString(QuicStringPiece data);
+    size_t DoReadValue(QuicStringPiece data);
+    void DoDecodeValue();
     void DoDone();
 
     void OnError(QuicStringPiece error_message);
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.cc b/net/third_party/quic/core/qpack/qpack_encoder.cc
index 0b5a77d..eee1f9d 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder.cc
@@ -299,7 +299,7 @@
 }  // namespace
 
 std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder>
-QpackEncoder::EncodeHeaderSet(const spdy::SpdyHeaderBlock* header_list) {
+QpackEncoder::EncodeHeaderList(const spdy::SpdyHeaderBlock* header_list) {
   return std::make_unique<QpackProgressiveEncoder>(&header_table_, header_list);
 }
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.h b/net/third_party/quic/core/qpack/qpack_encoder.h
index 641753f..a8f4825 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder.h
@@ -22,10 +22,10 @@
 // encoder stream, and receive data on the decoder stream.
 class QUIC_EXPORT_PRIVATE QpackEncoder {
  public:
-  // This factory method should be called to start encoding.
+  // This factory method should be called to start encoding a header list.
   // |*header_list| must remain valid and must not change
   // during the lifetime of the returned ProgressiveEncoder instance.
-  std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderSet(
+  std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderList(
       const spdy::SpdyHeaderBlock* header_list);
 
  private:
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
new file mode 100644
index 0000000..628a2e8
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -0,0 +1,393 @@
+// Copyright (c) 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 "net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "net/third_party/http2/decoder/decode_buffer.h"
+#include "net/third_party/http2/decoder/decode_status.h"
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackEncoderStreamReceiver::QpackEncoderStreamReceiver(Delegate* delegate)
+    : delegate_(delegate),
+      state_(State::kStart),
+      is_huffman_(false),
+      is_static_(false),
+      literal_name_(false),
+      name_index_(0),
+      name_length_(0),
+      value_length_(0),
+      error_detected_(false) {
+  DCHECK(delegate_);
+}
+
+void QpackEncoderStreamReceiver::Decode(QuicStringPiece data) {
+  while (!error_detected_) {
+    size_t bytes_consumed = 0;
+
+    switch (state_) {
+      case State::kStart:
+        bytes_consumed = DoStart(data);
+        break;
+      case State::kNameIndexOrLengthResume:
+        bytes_consumed = DoNameIndexOrLengthResume(data);
+        break;
+      case State::kNameIndexOrLengthDone:
+        DoNameIndexOrLengthDone();
+        break;
+      case State::kReadName:
+        bytes_consumed = DoReadName(data);
+        break;
+      case State::kDecodeName:
+        DoDecodeName();
+        break;
+      case State::kValueLengthStart:
+        bytes_consumed = DoValueLengthStart(data);
+        break;
+      case State::kValueLengthResume:
+        bytes_consumed = DoValueLengthResume(data);
+        break;
+      case State::kValueLengthDone:
+        DoValueLengthDone();
+        break;
+      case State::kReadValue:
+        bytes_consumed = DoReadValue(data);
+        break;
+      case State::kDecodeValue:
+        DoDecodeValue();
+        break;
+      case State::kInsertDone:
+        DoInsertDone();
+        break;
+      case State::kIndexResume:
+        bytes_consumed = DoIndexResume(data);
+        break;
+      case State::kIndexDone:
+        DoIndexDone();
+        break;
+      case State::kMaxSizeResume:
+        bytes_consumed = DoMaxSizeResume(data);
+        break;
+      case State::kMaxSizeDone:
+        DoMaxSizeDone();
+        break;
+    }
+
+    DCHECK_LE(bytes_consumed, data.size());
+
+    data = QuicStringPiece(data.data() + bytes_consumed,
+                           data.size() - bytes_consumed);
+
+    // Stop processing if no more data but next state would require it.
+    if (data.empty() && (state_ != State::kNameIndexOrLengthDone) &&
+        (state_ != State::kDecodeName) && (state_ != State::kValueLengthDone) &&
+        (state_ != State::kDecodeValue) && (state_ != State::kInsertDone) &&
+        (state_ != State::kIndexDone) && (state_ != State::kMaxSizeDone)) {
+      return;
+    }
+  }
+}
+
+size_t QpackEncoderStreamReceiver::DoStart(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  size_t prefix_length;
+  State state_varint_in_progress;
+  State state_varint_done;
+  if ((data[0] & kInsertWithNameReferenceOpcodeMask) ==
+      kInsertWithNameReferenceOpcode) {
+    is_static_ = (data[0] & kInsertWithNameReferenceStaticBit) ==
+                 kInsertWithNameReferenceStaticBit;
+
+    prefix_length = kInsertWithNameReferenceNameIndexPrefixLength;
+    literal_name_ = false;
+    state_varint_in_progress = State::kNameIndexOrLengthResume;
+    state_varint_done = State::kNameIndexOrLengthDone;
+  } else if ((data[0] & kInsertWithoutNameReferenceOpcodeMask) ==
+             kInsertWithoutNameReferenceOpcode) {
+    is_huffman_ = (data[0] & kInsertWithoutNameReferenceNameHuffmanBit) ==
+                  kInsertWithoutNameReferenceNameHuffmanBit;
+    prefix_length = kInsertWithoutNameReferenceNameLengthPrefixLength;
+    literal_name_ = true;
+    state_varint_in_progress = State::kNameIndexOrLengthResume;
+    state_varint_done = State::kNameIndexOrLengthDone;
+  } else if ((data[0] & kDuplicateOpcodeMask) == kDuplicateOpcode) {
+    prefix_length = kDuplicateIndexPrefixLength;
+    state_varint_in_progress = State::kIndexResume;
+    state_varint_done = State::kIndexDone;
+  } else {
+    DCHECK_EQ(kDynamicTableSizeUpdateOpcode,
+              data[0] & kDynamicTableSizeUpdateOpcodeMask);
+
+    prefix_length = kDynamicTableSizeUpdateMaxSizePrefixLength;
+    state_varint_in_progress = State::kMaxSizeResume;
+    state_varint_done = State::kMaxSizeDone;
+  }
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], prefix_length, &buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = state_varint_done;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      state_ = state_varint_in_progress;
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      break;
+  }
+  return 1 + buffer.Offset();
+}
+
+size_t QpackEncoderStreamReceiver::DoNameIndexOrLengthResume(
+    QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  http2::DecodeBuffer buffer(data);
+  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kNameIndexOrLengthDone;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      break;
+  }
+  return buffer.Offset();
+}
+
+void QpackEncoderStreamReceiver::DoNameIndexOrLengthDone() {
+  DCHECK(name_.empty());
+
+  if (literal_name_) {
+    name_length_ = varint_decoder_.value();
+    name_.reserve(name_length_);
+    // Do not handle empty names differently.  (They are probably forbidden by
+    // higher layers, but it is not enforced in this class.)  If there is no
+    // more data to read, then processing stalls, but the instruction is not
+    // complete without the value, so OnInsertWithoutNameReference() could not
+    // be called yet anyway.
+    state_ = State::kReadName;
+    return;
+  }
+
+  name_index_ = varint_decoder_.value();
+  state_ = State::kValueLengthStart;
+}
+
+size_t QpackEncoderStreamReceiver::DoReadName(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  // |name_length_| might be zero.
+  DCHECK_LE(name_.size(), name_length_);
+
+  size_t bytes_consumed = std::min(name_length_ - name_.size(), data.size());
+  name_.append(data.data(), bytes_consumed);
+
+  DCHECK_LE(name_.size(), name_length_);
+  if (name_.size() == name_length_) {
+    state_ = State::kDecodeName;
+  }
+
+  return bytes_consumed;
+}
+
+void QpackEncoderStreamReceiver::DoDecodeName() {
+  DCHECK_EQ(name_.size(), name_length_);
+
+  if (is_huffman_) {
+    huffman_decoder_.Reset();
+    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+    QuicString decoded_name;
+    huffman_decoder_.Decode(name_, &decoded_name);
+    if (!huffman_decoder_.InputProperlyTerminated()) {
+      OnError("Error in Huffman-encoded name.");
+      return;
+    }
+    name_ = decoded_name;
+  }
+
+  state_ = State::kValueLengthStart;
+}
+
+size_t QpackEncoderStreamReceiver::DoValueLengthStart(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  is_huffman_ =
+      (data[0] & kLiteralValueHuffmanMask) == kLiteralValueHuffmanMask;
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kValueLengthDone;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      state_ = State::kValueLengthResume;
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("ValueLen too large.");
+      break;
+  }
+  return 1 + buffer.Offset();
+}
+
+size_t QpackEncoderStreamReceiver::DoValueLengthResume(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kValueLengthDone;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("ValueLen too large.");
+      break;
+  }
+  return 1 + buffer.Offset();
+}
+
+void QpackEncoderStreamReceiver::DoValueLengthDone() {
+  DCHECK(value_.empty());
+
+  value_length_ = varint_decoder_.value();
+
+  // If value is empty, skip DoReadValue() and DoDecodeValue() and jump directly
+  // to DoInsertDone().  This is so that OnInsertName*() is called even if there
+  // is no more data.
+  if (value_length_ == 0) {
+    state_ = State::kInsertDone;
+    return;
+  }
+
+  value_.reserve(value_length_);
+  state_ = State::kReadValue;
+}
+
+size_t QpackEncoderStreamReceiver::DoReadValue(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK_LT(0u, value_length_);
+  DCHECK_LT(value_.size(), value_length_);
+
+  size_t bytes_consumed = std::min(value_length_ - value_.size(), data.size());
+  value_.append(data.data(), bytes_consumed);
+
+  DCHECK_LE(value_.size(), value_length_);
+  if (value_.size() == value_length_) {
+    state_ = State::kDecodeValue;
+  }
+
+  return bytes_consumed;
+}
+
+void QpackEncoderStreamReceiver::DoDecodeValue() {
+  DCHECK_EQ(value_.size(), value_length_);
+
+  if (is_huffman_) {
+    huffman_decoder_.Reset();
+    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+    QuicString decoded_value;
+    huffman_decoder_.Decode(value_, &decoded_value);
+    if (!huffman_decoder_.InputProperlyTerminated()) {
+      OnError("Error in Huffman-encoded value.");
+      return;
+    }
+    value_ = decoded_value;
+  }
+
+  state_ = State::kInsertDone;
+}
+
+void QpackEncoderStreamReceiver::DoInsertDone() {
+  if (literal_name_) {
+    delegate_->OnInsertWithoutNameReference(name_, value_);
+    name_.clear();
+    value_.clear();
+  } else {
+    delegate_->OnInsertWithNameReference(is_static_, name_index_, value_);
+    value_.clear();
+  }
+
+  state_ = State::kStart;
+}
+
+size_t QpackEncoderStreamReceiver::DoIndexResume(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kIndexDone;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Index too large.");
+      break;
+  }
+  return 1 + buffer.Offset();
+}
+
+void QpackEncoderStreamReceiver::DoIndexDone() {
+  delegate_->OnDuplicate(varint_decoder_.value());
+
+  state_ = State::kStart;
+}
+
+size_t QpackEncoderStreamReceiver::DoMaxSizeResume(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
+
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kMaxSizeDone;
+      break;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK(buffer.Empty());
+      break;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Maximum table size too large.");
+      break;
+  }
+  return 1 + buffer.Offset();
+}
+
+void QpackEncoderStreamReceiver::DoMaxSizeDone() {
+  delegate_->OnDynamicTableSizeUpdate(varint_decoder_.value());
+
+  state_ = State::kStart;
+}
+
+void QpackEncoderStreamReceiver::OnError(QuicStringPiece error_message) {
+  DCHECK(!error_detected_);
+
+  error_detected_ = true;
+  delegate_->OnErrorDetected(error_message);
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
new file mode 100644
index 0000000..55d47440
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -0,0 +1,138 @@
+// Copyright (c) 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 NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class decodes data received on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver {
+ public:
+  // An interface for handling instructions decoded from the encoder stream, see
+  // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // 5.2.1. Insert With Name Reference
+    virtual void OnInsertWithNameReference(bool is_static,
+                                           uint64_t name_index,
+                                           QuicStringPiece value) = 0;
+    // 5.2.2. Insert Without Name Reference
+    virtual void OnInsertWithoutNameReference(QuicStringPiece name,
+                                              QuicStringPiece value) = 0;
+    // 5.2.3. Duplicate
+    virtual void OnDuplicate(uint64_t index) = 0;
+    // 5.2.4. Dynamic Table Size Update
+    virtual void OnDynamicTableSizeUpdate(uint64_t max_size) = 0;
+    // Decoding error
+    virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
+  };
+
+  explicit QpackEncoderStreamReceiver(Delegate* delegate);
+  QpackEncoderStreamReceiver() = delete;
+  QpackEncoderStreamReceiver(const QpackEncoderStreamReceiver&) = delete;
+  QpackEncoderStreamReceiver& operator=(const QpackEncoderStreamReceiver&) =
+      delete;
+
+  // Decode data and call appropriate Delegate method after each decoded
+  // instruction.  Once an error occurs, Delegate::OnErrorDetected() is called,
+  // and all further data is ignored.
+  void Decode(QuicStringPiece data);
+
+ private:
+  enum class State {
+    // Identify the instruction and start decoding an integer.
+    kStart,
+    // Decode name index or name length.
+    kNameIndexOrLengthResume,
+    kNameIndexOrLengthDone,
+    // Read name string literal, which is optionally Huffman encoded.
+    kReadName,
+    // Optionally decode Huffman encoded name.
+    kDecodeName,
+    // Read value string length.
+    kValueLengthStart,
+    kValueLengthResume,
+    kValueLengthDone,
+    // Read value string literal, which is optionally Huffman encoded.
+    kReadValue,
+    // Optionally decode Huffman encoded value.
+    kDecodeValue,
+    // Done with insertion instruction.
+    kInsertDone,
+    // Read index to duplicate.
+    kIndexResume,
+    kIndexDone,
+    // Read maximum table size.
+    kMaxSizeResume,
+    kMaxSizeDone,
+  };
+
+  // One method for each state.  Some take input data and return the number of
+  // octets processed.  Some only change internal state.
+  size_t DoStart(QuicStringPiece data);
+  size_t DoNameIndexOrLengthResume(QuicStringPiece data);
+  void DoNameIndexOrLengthDone();
+  size_t DoReadName(QuicStringPiece data);
+  void DoDecodeName();
+  size_t DoValueLengthStart(QuicStringPiece data);
+  size_t DoValueLengthResume(QuicStringPiece data);
+  void DoValueLengthDone();
+  size_t DoReadValue(QuicStringPiece data);
+  void DoDecodeValue();
+  void DoInsertDone();
+  size_t DoIndexResume(QuicStringPiece data);
+  void DoIndexDone();
+  size_t DoMaxSizeResume(QuicStringPiece data);
+  void DoMaxSizeDone();
+
+  void OnError(QuicStringPiece error_message);
+
+  Delegate* const delegate_;
+  http2::HpackVarintDecoder varint_decoder_;
+  http2::HpackHuffmanDecoder huffman_decoder_;
+  State state_;
+
+  // True if the currently parsed string (name or value) is Huffman encoded.
+  bool is_huffman_;
+
+  // True if the name index refers to the static table.
+  bool is_static_;
+
+  // True if the header field value is encoded as a string literal.
+  bool literal_name_;
+
+  // Decoded name index.
+  uint64_t name_index_;
+
+  // Decoded length for header name.
+  size_t name_length_;
+
+  // Decoded header name.
+  QuicString name_;
+
+  // Decoded length for header value.
+  size_t value_length_;
+
+  // Decoded header value.
+  QuicString value_;
+
+  // True if a decoding error has been detected.
+  bool error_detected_;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
new file mode 100644
index 0000000..16737f570
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 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 "net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "net/third_party/quic/platform/api/quic_test.h"
+#include "net/third_party/quic/platform/api/quic_text_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QpackEncoderStreamReceiver::Delegate {
+ public:
+  ~MockDelegate() override = default;
+
+  MOCK_METHOD3(OnInsertWithNameReference,
+               void(bool is_static,
+                    uint64_t name_index,
+                    QuicStringPiece value));
+  MOCK_METHOD2(OnInsertWithoutNameReference,
+               void(QuicStringPiece name, QuicStringPiece value));
+  MOCK_METHOD1(OnDuplicate, void(uint64_t index));
+  MOCK_METHOD1(OnDynamicTableSizeUpdate, void(uint64_t max_size));
+  MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
+};
+
+class QpackEncoderStreamReceiverTest : public QuicTest {
+ protected:
+  QpackEncoderStreamReceiverTest() : stream_(&delegate_) {}
+
+  void Decode(QuicStringPiece data) { stream_.Decode(data); }
+  StrictMock<MockDelegate>* delegate() { return &delegate_; }
+
+ private:
+  QpackEncoderStreamReceiver stream_;
+  StrictMock<MockDelegate> delegate_;
+};
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReference) {
+  // Static, index fits in prefix, empty value.
+  EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 5, Eq("")));
+  // Static, index fits in prefix, Huffman encoded value.
+  EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 2, Eq("foo")));
+  // Not static, index does not fit in prefix, not Huffman encoded value.
+  EXPECT_CALL(*delegate(), OnInsertWithNameReference(false, 137, Eq("bar")));
+  // Value length does not fit in prefix.
+  // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+  EXPECT_CALL(*delegate(),
+              OnInsertWithNameReference(false, 42, Eq(QuicString(127, 'Z'))));
+
+  Decode(QuicTextUtils::HexDecode(
+      "c500"
+      "c28294e7"
+      "bf4a03626172"
+      "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceIndexTooLarge) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+  Decode(QuicTextUtils::HexDecode("bfffffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("ValueLen too large.")));
+
+  Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReference) {
+  // Empty name and value.
+  EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq(""), Eq("")));
+  // Huffman encoded short strings.
+  EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("bar"), Eq("bar")));
+  // Not Huffman encoded short strings.
+  EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("foo"), Eq("foo")));
+  // Not Huffman encoded long strings; length does not fit on prefix.
+  // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+  EXPECT_CALL(*delegate(),
+              OnInsertWithoutNameReference(Eq(QuicString(31, 'Z')),
+                                           Eq(QuicString(127, 'Z'))));
+
+  Decode(QuicTextUtils::HexDecode(
+      "4000"
+      "4362617203626172"
+      "6294e78294e7"
+      "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f005a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReferenceNameTooLong) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+  Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReferenceValueTooLong) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("ValueLen too large.")));
+
+  Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, Duplicate) {
+  // Small index fits in prefix.
+  EXPECT_CALL(*delegate(), OnDuplicate(17));
+  // Large index requires two extension bytes.
+  EXPECT_CALL(*delegate(), OnDuplicate(500));
+
+  Decode(QuicTextUtils::HexDecode("111fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, DuplicateIndexTooLarge) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+  Decode(QuicTextUtils::HexDecode("1fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, DynamicTableSizeUpdate) {
+  // Small max size fits in prefix.
+  EXPECT_CALL(*delegate(), OnDynamicTableSizeUpdate(17));
+  // Large max size requires two extension bytes.
+  EXPECT_CALL(*delegate(), OnDynamicTableSizeUpdate(500));
+
+  Decode(QuicTextUtils::HexDecode("313fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, DynamicTableSizeUpdateMaxSizeTooLarge) {
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+  Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
new file mode 100644
index 0000000..a688d94
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 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 "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h"
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quic/platform/api/quic_logging.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+
+namespace quic {
+
+QpackEncoderStreamSender::QpackEncoderStreamSender(Delegate* delegate)
+    : delegate_(delegate) {
+  DCHECK(delegate_);
+}
+
+void QpackEncoderStreamSender::SendInsertWithNameReference(
+    bool is_static,
+    uint64_t name_index,
+    QuicStringPiece value) {
+  QuicString encoded_name_index_and_value_length;
+
+  // Encode instruction code and name index.
+  uint8_t high_bits = kInsertWithNameReferenceOpcode;
+  if (is_static) {
+    high_bits |= kInsertWithNameReferenceStaticBit;
+  }
+  encoded_name_index_and_value_length.push_back(varint_encoder_.StartEncoding(
+      high_bits, kInsertWithNameReferenceNameIndexPrefixLength, name_index));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding,
+                                   &encoded_name_index_and_value_length);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  // Huffman encode value string.
+  QuicString huffman_encoded_value;
+  http2::HuffmanEncode(value, &huffman_encoded_value);
+
+  // Only use Huffman compression if it makes the string shorter.
+  QuicStringPiece value_to_send;
+  if (huffman_encoded_value.size() < value.size()) {
+    value_to_send = huffman_encoded_value;
+    high_bits = kLiteralValueHuffmanMask;
+  } else {
+    value_to_send = value;
+    high_bits = kLiteralValueWithoutHuffmanEncoding;
+  }
+
+  // Encode value length.
+  encoded_name_index_and_value_length.push_back(varint_encoder_.StartEncoding(
+      high_bits, kLiteralValuePrefixLength, value_to_send.size()));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding,
+                                   &encoded_name_index_and_value_length);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  // Write everything.
+  DCHECK(!encoded_name_index_and_value_length.empty());
+  delegate_->Write(encoded_name_index_and_value_length);
+  if (!value_to_send.empty()) {
+    delegate_->Write(value_to_send);
+  }
+}
+
+void QpackEncoderStreamSender::SendInsertWithoutNameReference(
+    QuicStringPiece name,
+    QuicStringPiece value) {
+  QuicString huffman_encoded_name;
+  http2::HuffmanEncode(name, &huffman_encoded_name);
+
+  // Only use Huffman compression if it makes the string shorter.
+  QuicStringPiece name_to_send;
+  uint8_t high_bits = kInsertWithoutNameReferenceOpcode;
+  if (huffman_encoded_name.size() < name.size()) {
+    name_to_send = huffman_encoded_name;
+    high_bits |= kInsertWithoutNameReferenceNameHuffmanBit;
+  } else {
+    name_to_send = name;
+  }
+
+  // Encode instruction code and name length.
+  QuicString encoded_name_length;
+  encoded_name_length.push_back(varint_encoder_.StartEncoding(
+      high_bits, kInsertWithoutNameReferenceNameLengthPrefixLength,
+      name_to_send.size()));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding,
+                                   &encoded_name_length);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  // Write instruction code, name length, and name.
+  DCHECK(!encoded_name_length.empty());
+  delegate_->Write(encoded_name_length);
+  if (!name_to_send.empty()) {
+    delegate_->Write(name_to_send);
+  }
+
+  // Huffman encode value string.
+  QuicString huffman_encoded_value;
+  http2::HuffmanEncode(value, &huffman_encoded_value);
+
+  // Only use Huffman compression if it makes the string shorter.
+  QuicStringPiece value_to_send;
+  if (huffman_encoded_value.size() < value.size()) {
+    value_to_send = huffman_encoded_value;
+    high_bits = kLiteralValueHuffmanMask;
+  } else {
+    value_to_send = value;
+    high_bits = kLiteralValueWithoutHuffmanEncoding;
+  }
+
+  // Encode value length.
+  QuicString encoded_value_length;
+  encoded_value_length.push_back(varint_encoder_.StartEncoding(
+      high_bits, kLiteralValuePrefixLength, value_to_send.size()));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding,
+                                   &encoded_value_length);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  // Write value length and value.
+  DCHECK(!encoded_value_length.empty());
+  delegate_->Write(encoded_value_length);
+  if (!value_to_send.empty()) {
+    delegate_->Write(value_to_send);
+  }
+}
+
+void QpackEncoderStreamSender::SendDuplicate(uint64_t index) {
+  QuicString data;
+  data.push_back(varint_encoder_.StartEncoding(
+      kDuplicateOpcode, kDuplicateIndexPrefixLength, index));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding, &data);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  DCHECK(!data.empty());
+  delegate_->Write(data);
+}
+
+void QpackEncoderStreamSender::SendDynamicTableSizeUpdate(uint64_t max_size) {
+  QuicString data;
+  data.push_back(varint_encoder_.StartEncoding(
+      kDynamicTableSizeUpdateOpcode, kDynamicTableSizeUpdateMaxSizePrefixLength,
+      max_size));
+  if (varint_encoder_.IsEncodingInProgress()) {
+    varint_encoder_.ResumeEncoding(kMaxExtensionBytesForVarintEncoding, &data);
+  }
+  DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+  DCHECK(!data.empty());
+  delegate_->Write(data);
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h
new file mode 100644
index 0000000..a12aef1
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -0,0 +1,56 @@
+// Copyright (c) 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 NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/http2/hpack/varint/hpack_varint_encoder.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class serializes instructions for transmission on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender {
+ public:
+  // An interface for handling encoded data.
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Encoded |data| is ready to be written on the encoder stream.
+    // |data| is guaranteed to be not empty.
+    virtual void Write(QuicStringPiece data) = 0;
+  };
+
+  explicit QpackEncoderStreamSender(Delegate* delegate);
+  QpackEncoderStreamSender() = delete;
+  QpackEncoderStreamSender(const QpackEncoderStreamSender&) = delete;
+  QpackEncoderStreamSender& operator=(const QpackEncoderStreamSender&) = delete;
+
+  // Methods for sending instructions, see
+  // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+
+  // 5.2.1. Insert With Name Reference
+  void SendInsertWithNameReference(bool is_static,
+                                   uint64_t name_index,
+                                   QuicStringPiece value);
+  // 5.2.2. Insert Without Name Reference
+  void SendInsertWithoutNameReference(QuicStringPiece name,
+                                      QuicStringPiece value);
+  // 5.2.3. Duplicate
+  void SendDuplicate(uint64_t index);
+  // 5.2.4. Dynamic Table Size Update
+  void SendDynamicTableSizeUpdate(uint64_t max_size);
+
+ private:
+  Delegate* const delegate_;
+  http2::HpackVarintEncoder varint_encoder_;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
new file mode 100644
index 0000000..de30a4b
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 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 "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include "net/third_party/quic/platform/api/quic_test.h"
+#include "net/third_party/quic/platform/api/quic_text_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestSendingDelegate : public QpackEncoderStreamSender::Delegate {
+ public:
+  ~TestSendingDelegate() override = default;
+
+  void Write(QuicStringPiece data) override {
+    EXPECT_FALSE(data.empty());
+    buffer_.append(data.data(), data.size());
+  }
+
+  const QuicString& buffer() { return buffer_; }
+
+ private:
+  QuicString buffer_;
+};
+
+class QpackEncoderStreamSenderTest : public QuicTest {
+ protected:
+  QpackEncoderStreamSenderTest() : stream_(&delegate_) {}
+
+  QpackEncoderStreamSender* stream() { return &stream_; }
+  const QuicString& buffer() { return delegate_.buffer(); }
+
+ private:
+  TestSendingDelegate delegate_;
+  QpackEncoderStreamSender stream_;
+};
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithNameReference) {
+  // Static, index fits in prefix, empty value.
+  stream()->SendInsertWithNameReference(true, 5, "");
+  // Static, index fits in prefix, Huffman encoded value.
+  stream()->SendInsertWithNameReference(true, 2, "foo");
+  // Not static, index does not fit in prefix, not Huffman encoded value.
+  stream()->SendInsertWithNameReference(false, 137, "bar");
+  // Value length does not fit in prefix.
+  // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+  stream()->SendInsertWithNameReference(false, 42, QuicString(127, 'Z'));
+
+  EXPECT_EQ(
+      QuicTextUtils::HexDecode(
+          "c500"
+          "c28294e7"
+          "bf4a03626172"
+          "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"),
+      buffer());
+}
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithoutNameReference) {
+  // Empty name and value.
+  stream()->SendInsertWithoutNameReference("", "");
+  // Huffman encoded short strings.
+  stream()->SendInsertWithoutNameReference("bar", "bar");
+  // Not Huffman encoded short strings.
+  stream()->SendInsertWithoutNameReference("foo", "foo");
+  // Not Huffman encoded long strings; length does not fit on prefix.
+  // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+  stream()->SendInsertWithoutNameReference(QuicString(31, 'Z'),
+                                           QuicString(127, 'Z'));
+
+  EXPECT_EQ(
+      QuicTextUtils::HexDecode(
+          "4000"
+          "4362617203626172"
+          "6294e78294e7"
+          "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f"
+          "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"),
+      buffer());
+}
+
+TEST_F(QpackEncoderStreamSenderTest, Duplicate) {
+  // Small index fits in prefix.
+  stream()->SendDuplicate(17);
+  // Large index requires two extension bytes.
+  stream()->SendDuplicate(500);
+
+  EXPECT_EQ(QuicTextUtils::HexDecode("111fd503"), buffer());
+}
+
+TEST_F(QpackEncoderStreamSenderTest, DynamicTableSizeUpdate) {
+  // Small max size fits in prefix.
+  stream()->SendDynamicTableSizeUpdate(17);
+  // Large max size requires two extension bytes.
+  stream()->SendDynamicTableSizeUpdate(500);
+
+  EXPECT_EQ(QuicTextUtils::HexDecode("313fd503"), buffer());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
index 1306dfe..2755665 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
@@ -78,7 +78,7 @@
   spdy::SpdyHeaderBlock header_list;
   header_list["foo"] = "bar";
   // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
-  header_list["ZZZZZZZ"] = std::string(127, 'Z');
+  header_list["ZZZZZZZ"] = QuicString(127, 'Z');
   QuicString output = Encode(&header_list);
 
   EXPECT_EQ(
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc
index 32700afa..3f38cff 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.cc
@@ -9,7 +9,7 @@
 QuicString QpackEncode(const FragmentSizeGenerator& fragment_size_generator,
                        const spdy::SpdyHeaderBlock* header_list) {
   QpackEncoder encoder;
-  auto progressive_encoder = encoder.EncodeHeaderSet(header_list);
+  auto progressive_encoder = encoder.EncodeHeaderList(header_list);
 
   QuicString output;
   while (progressive_encoder->HasNext()) {
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index c03374a..2ce683d5 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -1223,6 +1223,10 @@
   return true;
 }
 
+bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+  return true;
+}
+
 bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) {
   DCHECK(connected_);
 
@@ -2402,7 +2406,7 @@
 }
 
 void QuicConnection::OnRetransmissionTimeout() {
-  DCHECK(sent_packet_manager_.HasUnackedPackets());
+  DCHECK(!sent_packet_manager_.unacked_packets().empty());
   if (close_connection_after_five_rtos_ &&
       sent_packet_manager_.GetConsecutiveRtoCount() >= 4) {
     // Close on the 5th consecutive RTO, so after 4 previous RTOs have occurred.
@@ -2740,6 +2744,11 @@
 }
 
 void QuicConnection::SetPathDegradingAlarm() {
+  if (GetQuicReloadableFlag(quic_fix_path_degrading_alarm) &&
+      perspective_ == Perspective::IS_SERVER) {
+    QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_fix_path_degrading_alarm, 1, 2);
+    return;
+  }
   const QuicTime::Delta delay = sent_packet_manager_.GetPathDegradingDelay();
   path_degrading_alarm_->Update(clock_->ApproximateNow() + delay,
                                 QuicTime::Delta::FromMilliseconds(1));
@@ -3208,8 +3217,25 @@
   // Always reset the retransmission alarm when an ack comes in, since we now
   // have a better estimate of the current rtt than when it was set.
   SetRetransmissionAlarm();
+  MaybeSetPathDegradingAlarm(acked_new_packet);
 
-  if (!sent_packet_manager_.HasUnackedPackets()) {
+  // TODO(ianswett): Only increment stop_waiting_count_ if StopWaiting frames
+  // are sent.
+  if (send_stop_waiting) {
+    ++stop_waiting_count_;
+  } else {
+    stop_waiting_count_ = 0;
+  }
+}
+
+void QuicConnection::MaybeSetPathDegradingAlarm(bool acked_new_packet) {
+  bool has_unacked_packets = !sent_packet_manager_.unacked_packets().empty();
+  if (GetQuicReloadableFlag(quic_fix_path_degrading_alarm)) {
+    QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_fix_path_degrading_alarm, 2, 2);
+    // If there in flight packets, an ACK is expected in the future.
+    has_unacked_packets = sent_packet_manager_.HasInFlightPackets();
+  }
+  if (!has_unacked_packets) {
     // There are no retransmittable packets on the wire, so it may be
     // necessary to send a PING to keep a retransmittable packet on the wire.
     if (!retransmittable_on_wire_alarm_->IsSet()) {
@@ -3225,14 +3251,6 @@
     is_path_degrading_ = false;
     SetPathDegradingAlarm();
   }
-
-  // TODO(ianswett): Only increment stop_waiting_count_ if StopWaiting frames
-  // are sent.
-  if (send_stop_waiting) {
-    ++stop_waiting_count_;
-  } else {
-    stop_waiting_count_ = 0;
-  }
 }
 
 void QuicConnection::SetSessionNotifier(
@@ -3258,6 +3276,7 @@
 }
 
 void QuicConnection::SetRetransmittableOnWireAlarm() {
+  // TODO(ianswett): Merge the RetransmittableOnWireAlarm and PingAlarm.
   if (perspective_ == Perspective::IS_SERVER) {
     // Only clients send pings.
     return;
diff --git a/net/third_party/quic/core/quic_connection.h b/net/third_party/quic/core/quic_connection.h
index 97a6a8a..ae856a8 100644
--- a/net/third_party/quic/core/quic_connection.h
+++ b/net/third_party/quic/core/quic_connection.h
@@ -489,6 +489,7 @@
   bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
   bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
   bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
   bool OnMessageFrame(const QuicMessageFrame& frame) override;
   void OnPacketComplete() override;
   bool IsValidStatelessResetToken(QuicUint128 token) const override;
@@ -1006,6 +1007,10 @@
   // |acked_new_packet| is true if a previously-unacked packet was acked.
   void PostProcessAfterAckFrame(bool send_stop_waiting, bool acked_new_packet);
 
+  // Called when an ACK is received to set the path degrading alarm or
+  // retransmittable on wire alarm.
+  void MaybeSetPathDegradingAlarm(bool acked_new_packet);
+
   // Updates the release time into the future.
   void UpdateReleaseTimeIntoFuture();
 
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc
index 36bc4ff..f6ffc51 100644
--- a/net/third_party/quic/core/quic_connection_test.cc
+++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -6696,6 +6696,40 @@
   EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
 }
 
+TEST_P(QuicConnectionTest, NoPathDegradingOnServer) {
+  SetQuicReloadableFlag(quic_fix_path_degrading_alarm, true);
+  set_perspective(Perspective::IS_SERVER);
+  QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+  EXPECT_FALSE(connection_.IsPathDegrading());
+  EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+
+  // Send data.
+  const char data[] = "data";
+  connection_.SendStreamDataWithString(1, data, 0, NO_FIN);
+  EXPECT_FALSE(connection_.IsPathDegrading());
+  EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+
+  // Ack data.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+  QuicAckFrame frame = InitAckFrame({{1u, 2u}});
+  ProcessAckPacket(&frame);
+  EXPECT_FALSE(connection_.IsPathDegrading());
+  EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoPathDegradingAfterSendingAck) {
+  SetQuicReloadableFlag(quic_fix_path_degrading_alarm, true);
+
+  SendAckPacketToPeer();
+  EXPECT_FALSE(connection_.sent_packet_manager().unacked_packets().empty());
+  EXPECT_FALSE(connection_.sent_packet_manager().HasInFlightPackets());
+  EXPECT_FALSE(connection_.IsPathDegrading());
+  EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
 TEST_P(QuicConnectionTest, MultipleCallsToCloseConnection) {
   // Verifies that multiple calls to CloseConnection do not
   // result in multiple attempts to close the connection - it will be marked as
@@ -6965,7 +6999,7 @@
 // there are no outstanding packets.
 TEST_P(QuicConnectionTest,
        SendProbingRetransmissionsFailsWhenNothingToRetransmit) {
-  ASSERT_FALSE(connection_.sent_packet_manager().HasUnackedPackets());
+  ASSERT_TRUE(connection_.sent_packet_manager().unacked_packets().empty());
 
   MockQuicConnectionDebugVisitor debug_visitor;
   connection_.set_debug_visitor(&debug_visitor);
diff --git a/net/third_party/quic/core/quic_constants.h b/net/third_party/quic/core/quic_constants.h
index 58f9d32..cbadc51 100644
--- a/net/third_party/quic/core/quic_constants.h
+++ b/net/third_party/quic/core/quic_constants.h
@@ -226,6 +226,9 @@
 // Minimum length of random bytes in IETF stateless reset packet.
 const size_t kMinRandomBytesLengthInStatelessReset = 20;
 
+// Maximum length allowed for the token in a NEW_TOKEN frame.
+const size_t kMaxNewTokenTokenLength = 0xffff;
+
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_CORE_QUIC_CONSTANTS_H_
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index d71f6a36..04873f3c 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -817,6 +817,11 @@
   return false;
 }
 
+bool QuicDispatcher::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+  DCHECK(false);
+  return false;
+}
+
 bool QuicDispatcher::OnMessageFrame(const QuicMessageFrame& frame) {
   DCHECK(false);
   return false;
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index 29a09b8..cd87edd9 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -158,6 +158,7 @@
   bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
   bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
   bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
   bool OnMessageFrame(const QuicMessageFrame& frame) override;
   void OnPacketComplete() override;
   bool IsValidStatelessResetToken(QuicUint128 token) const override;
diff --git a/net/third_party/quic/core/quic_error_codes.cc b/net/third_party/quic/core/quic_error_codes.cc
index aebc181..a77aa65e 100644
--- a/net/third_party/quic/core/quic_error_codes.cc
+++ b/net/third_party/quic/core/quic_error_codes.cc
@@ -150,6 +150,7 @@
     RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED);
     RETURN_STRING_LITERAL(QUIC_INVALID_MESSAGE_DATA);
     RETURN_STRING_LITERAL(IETF_QUIC_PROTOCOL_VIOLATION);
+    RETURN_STRING_LITERAL(QUIC_INVALID_NEW_TOKEN);
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
     // if we add errors and don't put them here.
diff --git a/net/third_party/quic/core/quic_error_codes.h b/net/third_party/quic/core/quic_error_codes.h
index b0929cce..31c3e29 100644
--- a/net/third_party/quic/core/quic_error_codes.h
+++ b/net/third_party/quic/core/quic_error_codes.h
@@ -304,12 +304,13 @@
   // Error deframing PATH CHALLENGE or PATH RESPONSE frames.
   QUIC_INVALID_PATH_CHALLENGE_DATA = 109,
   QUIC_INVALID_PATH_RESPONSE_DATA = 110,
-
   // This is used to indicate an IETF QUIC PROTOCOL VIOLATION
   // transport error within Google (pre-v99) QUIC.
   IETF_QUIC_PROTOCOL_VIOLATION = 113,
+  QUIC_INVALID_NEW_TOKEN = 114,
+
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 114,
+  QUIC_LAST_ERROR = 115,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index b99e760..4cd48a5ee 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -465,6 +465,8 @@
                  frame.application_close_frame->error_details);
     case NEW_CONNECTION_ID_FRAME:
       return GetNewConnectionIdFrameSize(*frame.new_connection_id_frame);
+    case NEW_TOKEN_FRAME:
+      return GetNewTokenFrameSize(*frame.new_token_frame);
     case MAX_STREAM_ID_FRAME:
       return GetMaxStreamIdFrameSize(version, frame.max_stream_id_frame);
     case STREAM_ID_BLOCKED_FRAME:
@@ -536,6 +538,13 @@
 }
 
 // static
+size_t QuicFramer::GetNewTokenFrameSize(const QuicNewTokenFrame& frame) {
+  return kQuicFrameTypeSize +
+         QuicDataWriter::GetVarInt62Len(frame.token.length()) +
+         frame.token.length();
+}
+
+// static
 size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) {
   return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID +
          number_versions * kQuicVersionSize;
@@ -718,6 +727,10 @@
         set_detailed_error(
             "Attempt to append NEW_CONNECTION_ID frame and not in version 99.");
         return RaiseError(QUIC_INTERNAL_ERROR);
+      case NEW_TOKEN_FRAME:
+        set_detailed_error(
+            "Attempt to append NEW_TOKEN_ID frame and not in version 99.");
+        return RaiseError(QUIC_INTERNAL_ERROR);
       case MAX_STREAM_ID_FRAME:
         set_detailed_error(
             "Attempt to append MAX_STREAM_ID frame and not in version 99.");
@@ -875,6 +888,12 @@
           return 0;
         }
         break;
+      case NEW_TOKEN_FRAME:
+        if (!AppendNewTokenFrame(*frame.new_token_frame, &writer)) {
+          QUIC_BUG << "AppendNewTokenFrame failed";
+          return 0;
+        }
+        break;
       case STOP_SENDING_FRAME:
         if (!AppendStopSendingFrame(*frame.stop_sending_frame, &writer)) {
           QUIC_BUG << "AppendStopSendingFrame failed";
@@ -2313,6 +2332,18 @@
           }
           break;
         }
+        case IETF_NEW_TOKEN: {
+          QuicNewTokenFrame frame;
+          if (!ProcessNewTokenFrame(reader, &frame)) {
+            return RaiseError(QUIC_INVALID_NEW_TOKEN);
+          }
+          if (!visitor_->OnNewTokenFrame(frame)) {
+            QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+            // Returning true since there was no parsing error.
+            return true;
+          }
+          break;
+        }
         case IETF_STOP_SENDING: {
           QuicStopSendingFrame frame;
           if (!ProcessStopSendingFrame(reader, &frame)) {
@@ -3407,6 +3438,10 @@
       set_detailed_error(
           "Attempt to append NEW_CONNECTION_ID frame and not in version 99.");
       return RaiseError(QUIC_INTERNAL_ERROR);
+    case NEW_TOKEN_FRAME:
+      set_detailed_error(
+          "Attempt to append NEW_TOKEN frame and not in version 99.");
+      return RaiseError(QUIC_INTERNAL_ERROR);
     case MAX_STREAM_ID_FRAME:
       set_detailed_error(
           "Attempt to append MAX_STREAM_ID frame and not in version 99.");
@@ -3497,6 +3532,9 @@
     case NEW_CONNECTION_ID_FRAME:
       type_byte = IETF_NEW_CONNECTION_ID;
       break;
+    case NEW_TOKEN_FRAME:
+      type_byte = IETF_NEW_TOKEN;
+      break;
     case MAX_STREAM_ID_FRAME:
       type_byte = IETF_MAX_STREAM_ID;
       break;
@@ -3642,6 +3680,40 @@
   }
   return true;
 }
+bool QuicFramer::AppendNewTokenFrame(const QuicNewTokenFrame& frame,
+                                     QuicDataWriter* writer) {
+  if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.token.length()))) {
+    set_detailed_error("Writing token length failed.");
+    return false;
+  }
+  if (!writer->WriteBytes(frame.token.data(), frame.token.length())) {
+    set_detailed_error("Writing token buffer failed.");
+    return false;
+  }
+  return true;
+}
+
+bool QuicFramer::ProcessNewTokenFrame(QuicDataReader* reader,
+                                      QuicNewTokenFrame* frame) {
+  uint64_t length;
+  if (!reader->ReadVarInt62(&length)) {
+    set_detailed_error("Unable to read new token length.");
+    return false;
+  }
+  if (length > kMaxNewTokenTokenLength) {
+    set_detailed_error("Token length larger than maximum.");
+    return false;
+  }
+
+  // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+  QuicStringPiece data;
+  if (!reader->ReadStringPiece(&data, length)) {
+    set_detailed_error("Unable to read new token data.");
+    return false;
+  }
+  frame->token = QuicString(data);
+  return true;
+}
 
 // Add a new ietf-format stream frame.
 // Bits controlling whether there is a frame-length and frame-offset
diff --git a/net/third_party/quic/core/quic_framer.h b/net/third_party/quic/core/quic_framer.h
index d7ca1c1..09d1293 100644
--- a/net/third_party/quic/core/quic_framer.h
+++ b/net/third_party/quic/core/quic_framer.h
@@ -174,6 +174,9 @@
   virtual bool OnNewConnectionIdFrame(
       const QuicNewConnectionIdFrame& frame) = 0;
 
+  // Called when a NewTokenFrame has been parsed.
+  virtual bool OnNewTokenFrame(const QuicNewTokenFrame& frame) = 0;
+
   // Called when a message frame has been parsed.
   virtual bool OnMessageFrame(const QuicMessageFrame& frame) = 0;
 
@@ -315,6 +318,9 @@
   static size_t GetNewConnectionIdFrameSize(
       const QuicNewConnectionIdFrame& frame);
 
+  // Size in bytes for a serialized new token frame
+  static size_t GetNewTokenFrameSize(const QuicNewTokenFrame& frame);
+
   // Size in bytes required for a serialized version negotiation packet
   static size_t GetVersionNegotiationPacketSize(size_t number_versions);
 
@@ -752,6 +758,10 @@
   bool ProcessNewConnectionIdFrame(QuicDataReader* reader,
                                    QuicNewConnectionIdFrame* frame);
 
+  bool AppendNewTokenFrame(const QuicNewTokenFrame& frame,
+                           QuicDataWriter* writer);
+  bool ProcessNewTokenFrame(QuicDataReader* reader, QuicNewTokenFrame* frame);
+
   bool RaiseError(QuicErrorCode error);
 
   // Returns true if |header| indicates a version negotiation packet.
diff --git a/net/third_party/quic/core/quic_framer_test.cc b/net/third_party/quic/core/quic_framer_test.cc
index 11a58f4..a1360f2 100644
--- a/net/third_party/quic/core/quic_framer_test.cc
+++ b/net/third_party/quic/core/quic_framer_test.cc
@@ -329,6 +329,11 @@
     return true;
   }
 
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+    new_token_ = frame;
+    return true;
+  }
+
   bool IsValidStatelessResetToken(QuicUint128 token) const override {
     return token == kTestStatelessResetToken;
   }
@@ -370,6 +375,7 @@
   QuicStreamIdBlockedFrame stream_id_blocked_frame_;
   QuicMaxStreamIdFrame max_stream_id_frame_;
   QuicNewConnectionIdFrame new_connection_id_;
+  QuicNewTokenFrame new_token_;
   std::vector<std::unique_ptr<QuicString>> stream_data_;
 };
 
@@ -9269,6 +9275,98 @@
                                       QUIC_ARRAYSIZE(packet99));
 }
 
+TEST_P(QuicFramerTest, NewTokenFrame) {
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    // This frame is only for version 99.
+    return;
+  }
+  // clang-format off
+  PacketFragments packet = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x32}},
+      // connection_id
+      {"",
+       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // packet number
+      {"",
+       {0x12, 0x34, 0x56, 0x78}},
+      // frame type (new token frame)
+      {"",
+       {0x19}},
+      // Length
+      {"Unable to read new token length.",
+       {kVarInt62OneByte + 0x08}},
+      {"Unable to read new token data.",
+       {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}
+  };
+  // clang-format on
+  uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03,
+                                    0x04, 0x05, 0x06, 0x07};
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet));
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+  EXPECT_EQ(sizeof(expected_token_value), visitor_.new_token_.token.length());
+  EXPECT_EQ(0, memcmp(expected_token_value, visitor_.new_token_.token.data(),
+                      sizeof(expected_token_value)));
+
+  CheckFramingBoundaries(packet, QUIC_INVALID_NEW_TOKEN);
+}
+
+TEST_P(QuicFramerTest, BuildNewTokenFramePacket) {
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    // This frame is only for version 99.
+    return;
+  }
+  QuicPacketHeader header;
+  header.destination_connection_id = kConnectionId;
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03,
+                                    0x04, 0x05, 0x06, 0x07};
+
+  QuicNewTokenFrame frame(0, QuicString((const char*)(expected_token_value),
+                                        sizeof(expected_token_value)));
+
+  QuicFrames frames = {QuicFrame(&frame)};
+
+  // clang-format off
+  unsigned char packet[] = {
+    // type (short header, 4 byte packet number)
+    0x32,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (new token frame)
+    0x19,
+    // Length and token
+    kVarInt62OneByte + 0x08,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet),
+                                      QUIC_ARRAYSIZE(packet));
+}
+
 TEST_P(QuicFramerTest, IetfStopSendingFrame) {
   // This test is only for version 99.
   if (framer_.transport_version() != QUIC_VERSION_99) {
diff --git a/net/third_party/quic/core/quic_ietf_framer_test.cc b/net/third_party/quic/core/quic_ietf_framer_test.cc
index b32cbab..e56b6a4 100644
--- a/net/third_party/quic/core/quic_ietf_framer_test.cc
+++ b/net/third_party/quic/core/quic_ietf_framer_test.cc
@@ -172,6 +172,8 @@
     return true;
   }
 
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override { return true; }
+
   bool IsValidStatelessResetToken(QuicUint128 token) const override {
     return true;
   }
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.h b/net/third_party/quic/core/quic_sent_packet_manager.h
index 08c96ea..5caf222 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager.h
+++ b/net/third_party/quic/core/quic_sent_packet_manager.h
@@ -152,15 +152,16 @@
   // there are pending retransmissions prior to calling this function.
   QuicPendingRetransmission NextPendingRetransmission();
 
-  bool HasUnackedPackets() const {
-    return unacked_packets_.HasUnackedPackets();
-  }
-
   // Returns true if there's outstanding crypto data.
   bool HasUnackedCryptoPackets() const {
     return unacked_packets_.HasPendingCryptoPackets();
   }
 
+  // Returns true if there are packets in flight expecting to be acknowledged.
+  bool HasInFlightPackets() const {
+    return unacked_packets_.HasInFlightPackets();
+  }
+
   // Returns the smallest packet number of a serialized packet which has not
   // been acked by the peer.
   QuicPacketNumber GetLeastUnacked() const {
diff --git a/net/third_party/quic/core/quic_sent_packet_manager_test.cc b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
index 10f6747..a8d9d17 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
@@ -113,13 +113,13 @@
   }
   void VerifyUnackedPackets(QuicPacketNumber* packets, size_t num_packets) {
     if (num_packets == 0) {
-      EXPECT_FALSE(manager_.HasUnackedPackets());
+      EXPECT_TRUE(manager_.unacked_packets().empty());
       EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
                         &manager_));
       return;
     }
 
-    EXPECT_TRUE(manager_.HasUnackedPackets());
+    EXPECT_FALSE(manager_.unacked_packets().empty());
     EXPECT_EQ(packets[0], manager_.GetLeastUnacked());
     for (size_t i = 0; i < num_packets; ++i) {
       EXPECT_TRUE(QuicSentPacketManagerPeer::IsUnacked(&manager_, packets[i]))
@@ -1612,6 +1612,7 @@
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, RetransmitFrames(_, _))
         .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+    // When session decides what to write, crypto_packet_send_time gets updated.
     crypto_packet_send_time = clock_.Now();
   }
   manager_.OnRetransmissionTimeout();
@@ -1622,6 +1623,23 @@
   // The retransmission time should now be twice as far in the future.
   expected_time = crypto_packet_send_time + srtt * 2 * 1.5;
   EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+  // Retransmit the packet for the 2nd time.
+  clock_.AdvanceTime(2 * 1.5 * srtt);
+  if (manager_.session_decides_what_to_write()) {
+    EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+        .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); }));
+    // When session decides what to write, crypto_packet_send_time gets updated.
+    crypto_packet_send_time = clock_.Now();
+  }
+  manager_.OnRetransmissionTimeout();
+  if (!manager_.session_decides_what_to_write()) {
+    RetransmitNextPacket(3);
+  }
+
+  // Verify exponential backoff of the retransmission timeout.
+  expected_time = crypto_packet_send_time + srtt * 4 * 1.5;
+  EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
 }
 
 TEST_P(QuicSentPacketManagerTest,
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index 06a4cd20..17fd031 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -830,6 +830,15 @@
   return id;
 }
 
+bool QuicSession::CanOpenNextOutgoingStream() {
+  if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) {
+    QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+                    << "Already " << GetNumOpenOutgoingStreams() << " open.";
+    return false;
+  }
+  return true;
+}
+
 QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
   StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
   if (it != static_stream_map_.end()) {
@@ -1280,19 +1289,12 @@
       if (stream->HasPendingRetransmission()) {
         // Connection is write blocked.
         break;
-      } else {
-        if (GetQuicReloadableFlag(quic_fix_retransmit_lost_data)) {
-          QUIC_FLAG_COUNT(quic_reloadable_flag_quic_fix_retransmit_lost_data);
-          if (!streams_with_pending_retransmission_.empty() &&
-              streams_with_pending_retransmission_.begin()->first == id) {
-            // Retransmit lost data may cause connection close. If this stream
-            // has not yet sent fin, a RST_STREAM will be sent and it will be
-            // removed from streams_with_pending_retransmission_.
-            streams_with_pending_retransmission_.pop_front();
-          }
-        } else {
-          streams_with_pending_retransmission_.pop_front();
-        }
+      } else if (!streams_with_pending_retransmission_.empty() &&
+                 streams_with_pending_retransmission_.begin()->first == id) {
+        // Retransmit lost data may cause connection close. If this stream
+        // has not yet sent fin, a RST_STREAM will be sent and it will be
+        // removed from streams_with_pending_retransmission_.
+        streams_with_pending_retransmission_.pop_front();
       }
     } else {
       QUIC_BUG << "Try to retransmit data of a closed stream";
diff --git a/net/third_party/quic/core/quic_session.h b/net/third_party/quic/core/quic_session.h
index 3b401b1..d102f6d 100644
--- a/net/third_party/quic/core/quic_session.h
+++ b/net/third_party/quic/core/quic_session.h
@@ -377,6 +377,9 @@
   // underlying counter.
   QuicStreamId GetNextOutgoingStreamId();
 
+  // Indicates whether the next outgoing stream ID can be allocated or not.
+  bool CanOpenNextOutgoingStream();
+
   // Returns existing stream with id = |stream_id|. If no such stream exists,
   // and |stream_id| is a peer-created id, then a new stream is created and
   // returned. However if |stream_id| is a locally-created id and no such stream
diff --git a/net/third_party/quic/core/quic_stream_test.cc b/net/third_party/quic/core/quic_stream_test.cc
index 6d1a6cb..670bd9b 100644
--- a/net/third_party/quic/core/quic_stream_test.cc
+++ b/net/third_party/quic/core/quic_stream_test.cc
@@ -41,12 +41,10 @@
 const char kData1[] = "FooAndBar";
 const char kData2[] = "EepAndBaz";
 const size_t kDataLen = 9;
-const bool kShouldProcessData = true;
-const bool kShouldNotProcessData = false;
 
 class TestStream : public QuicStream {
  public:
-  TestStream(QuicStreamId id, QuicSession* session, bool should_process_data)
+  TestStream(QuicStreamId id, QuicSession* session)
       : QuicStream(id, session, /*is_static=*/false) {}
 
   void OnDataAvailable() override {}
@@ -74,7 +72,7 @@
         supported_versions_(AllSupportedVersions()) {
   }
 
-  void Initialize(bool stream_should_process_data) {
+  void Initialize() {
     connection_ = new StrictMock<MockQuicConnection>(
         &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions_);
     connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
@@ -85,8 +83,7 @@
     QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
         session_->config(), initial_flow_control_window_bytes_);
 
-    stream_ = new TestStream(kTestStreamId, session_.get(),
-                             stream_should_process_data);
+    stream_ = new TestStream(kTestStreamId, session_.get());
     // session_ now owns stream_.
     session_->ActivateStream(QuicWrapUnique(stream_));
     // Ignore resetting when session_ is terminated.
@@ -136,7 +133,7 @@
 };
 
 TEST_F(QuicStreamTest, WriteAllData) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   size_t length =
       1 + QuicPacketCreator::StreamFramePacketOverhead(
@@ -152,7 +149,7 @@
 }
 
 TEST_F(QuicStreamTest, NoBlockingIfNoDataOrFin) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Write no data and no fin.  If we consume nothing we should not be write
   // blocked.
@@ -162,7 +159,7 @@
 }
 
 TEST_F(QuicStreamTest, BlockIfOnlySomeDataConsumed) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Write some data and no fin.  If we consume some but not all of the data,
   // we should be write blocked a not all the data was consumed.
@@ -177,7 +174,7 @@
 }
 
 TEST_F(QuicStreamTest, BlockIfFinNotConsumedWithData) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Write some data and no fin.  If we consume all the data but not the fin,
   // we should be write blocked because the fin was not consumed.
@@ -193,7 +190,7 @@
 }
 
 TEST_F(QuicStreamTest, BlockIfSoloFinNotConsumed) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Write no data and a fin.  If we consume nothing we should be write blocked,
   // as the fin was not consumed.
@@ -204,7 +201,7 @@
 }
 
 TEST_F(QuicStreamTest, CloseOnPartialWrite) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Write some data and no fin. However, while writing the data
   // close the stream and verify that MarkConnectionLevelWriteBlocked does not
@@ -216,7 +213,7 @@
 }
 
 TEST_F(QuicStreamTest, WriteOrBufferData) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_FALSE(HasWriteBlockedStreams());
   size_t length =
@@ -258,7 +255,7 @@
 
 TEST_F(QuicStreamTest, WriteOrBufferDataReachStreamLimit) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicString data("aaaaa");
   QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(),
                                         stream_);
@@ -271,7 +268,7 @@
 }
 
 TEST_F(QuicStreamTest, ConnectionCloseAfterStreamClose) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   QuicStreamPeer::CloseReadSide(stream_);
   stream_->CloseWriteSide();
@@ -288,7 +285,7 @@
   // before termination.
   // Test that if no FIN has been sent, we send a RST.
 
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_FALSE(fin_sent());
   EXPECT_FALSE(rst_sent());
 
@@ -314,7 +311,7 @@
   // before termination.
   // Test that if a FIN has been sent, we don't also send a RST.
 
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_FALSE(fin_sent());
   EXPECT_FALSE(rst_sent());
 
@@ -340,7 +337,7 @@
   // Test that if a stream sends a RST, it doesn't send an additional RST during
   // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...)
 
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_FALSE(fin_sent());
   EXPECT_FALSE(rst_sent());
 
@@ -361,7 +358,7 @@
 TEST_F(QuicStreamTest, StreamFlowControlMultipleWindowUpdates) {
   set_initial_flow_control_window_bytes(1000);
 
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // If we receive multiple WINDOW_UPDATES (potentially out of order), then we
   // want to make sure we latch the largest offset we see.
@@ -396,7 +393,7 @@
 }
 
 TEST_F(QuicStreamTest, FrameStats) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_EQ(0, stream_->num_frames_received());
   EXPECT_EQ(0, stream_->num_duplicate_frames_received());
@@ -413,7 +410,7 @@
 // too much data on the stream) that the stream sequencer never sees this frame,
 // as we check for violation and close the connection early.
 TEST_F(QuicStreamTest, StreamSequencerNeverSeesPacketsViolatingFlowControl) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Receive a stream frame that violates flow control: the byte offset is
   // higher than the receive window offset.
@@ -432,7 +429,7 @@
 // Verify that after the consumer calls StopReading(), the stream still sends
 // flow control updates.
 TEST_F(QuicStreamTest, StopReadingSendsFlowControl) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   stream_->StopReading();
 
@@ -457,7 +454,7 @@
 }
 
 TEST_F(QuicStreamTest, FinalByteOffsetFromFin) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
 
@@ -473,7 +470,7 @@
 }
 
 TEST_F(QuicStreamTest, FinalByteOffsetFromRst) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
@@ -483,7 +480,7 @@
 }
 
 TEST_F(QuicStreamTest, InvalidFinalByteOffsetFromRst) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
@@ -503,7 +500,7 @@
   // current flow control limits. Flow control should only be concerned with
   // data that has actually been sent/received, so verify that flow control
   // ignores such a stream frame.
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
   const QuicStreamOffset kByteOffsetExceedingFlowControlWindow =
@@ -536,7 +533,7 @@
 
 TEST_F(QuicStreamTest, OnStreamResetOffsetOverflow) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
                                QUIC_STREAM_CANCELLED, kMaxStreamLength + 1);
   EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
@@ -545,7 +542,7 @@
 
 TEST_F(QuicStreamTest, OnStreamFrameUpperLimit) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Modify receive window offset and sequencer buffer total_bytes_read_ to
   // avoid flow control violation.
@@ -568,7 +565,7 @@
 
 TEST_F(QuicStreamTest, StreamTooLong) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _))
       .Times(1);
   QuicStreamFrame stream_frame(stream_->id(), false, kMaxStreamLength,
@@ -579,7 +576,7 @@
 
 TEST_F(QuicStreamTest, SetDrainingIncomingOutgoing) {
   // Don't have incoming data consumed.
-  Initialize(kShouldNotProcessData);
+  Initialize();
 
   // Incoming data with FIN.
   QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
@@ -608,7 +605,7 @@
 
 TEST_F(QuicStreamTest, SetDrainingOutgoingIncoming) {
   // Don't have incoming data consumed.
-  Initialize(kShouldNotProcessData);
+  Initialize();
 
   // Outgoing data with FIN.
   EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
@@ -639,7 +636,7 @@
   // Verify that if the server completes the response before reading the end of
   // the request, the received FIN is recorded.
 
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
@@ -661,7 +658,7 @@
 }
 
 TEST_F(QuicStreamTest, StreamWaitsForAcks) {
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
       new StrictMock<MockAckListener>);
   stream_->set_ack_listener(mock_ack_listener);
@@ -712,7 +709,7 @@
 }
 
 TEST_F(QuicStreamTest, StreamDataGetAckedOutOfOrder) {
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   // Send data.
@@ -740,7 +737,7 @@
 }
 
 TEST_F(QuicStreamTest, CancelStream) {
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
@@ -763,7 +760,7 @@
 }
 
 TEST_F(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) {
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
@@ -786,7 +783,7 @@
 }
 
 TEST_F(QuicStreamTest, RstFrameReceivedStreamFinishSending) {
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
@@ -806,7 +803,7 @@
 }
 
 TEST_F(QuicStreamTest, ConnectionClosed) {
-  Initialize(kShouldProcessData);
+  Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
@@ -830,7 +827,7 @@
   // Do not stream level flow control block this stream.
   set_initial_flow_control_window_bytes(500000);
 
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicString data(1024, 'a');
   EXPECT_TRUE(stream_->CanWriteNewData());
 
@@ -926,7 +923,7 @@
 
 TEST_F(QuicStreamTest, WritevDataReachStreamLimit) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicString data("aaaaa");
   QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(),
                                         stream_);
@@ -947,7 +944,7 @@
   // Do not flow control block this stream.
   set_initial_flow_control_window_bytes(500000);
 
-  Initialize(kShouldProcessData);
+  Initialize();
   char data[1024];
   std::vector<std::pair<char*, size_t>> buffers;
   buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
@@ -1009,7 +1006,7 @@
 
 TEST_F(QuicStreamTest, WriteMemSlicesReachStreamLimit) {
   SetQuicReloadableFlag(quic_stream_too_long, true);
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - 5u, stream_);
   char data[5];
   std::vector<std::pair<char*, size_t>> buffers;
@@ -1035,7 +1032,7 @@
 }
 
 TEST_F(QuicStreamTest, StreamDataGetAckedMultipleTimes) {
-  Initialize(kShouldProcessData);
+  Initialize();
   QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
       new StrictMock<MockAckListener>);
   stream_->set_ack_listener(mock_ack_listener);
@@ -1091,7 +1088,7 @@
 }
 
 TEST_F(QuicStreamTest, OnStreamFrameLost) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Send [0, 9).
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
@@ -1156,7 +1153,7 @@
 }
 
 TEST_F(QuicStreamTest, CannotBundleLostFin) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   // Send [0, 18) and fin.
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
@@ -1184,7 +1181,7 @@
 TEST_F(QuicStreamTest, MarkConnectionLevelWriteBlockedOnWindowUpdateFrame) {
   // Set a small initial control window size.
   set_initial_flow_control_window_bytes(100);
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
@@ -1209,7 +1206,7 @@
   // Set a small initial flow control window size.
   const uint32_t kSmallWindow = 100;
   set_initial_flow_control_window_bytes(kSmallWindow);
-  Initialize(kShouldProcessData);
+  Initialize();
 
   QuicString data(kSmallWindow, '.');
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
@@ -1228,7 +1225,7 @@
 }
 
 TEST_F(QuicStreamTest, RetransmitStreamData) {
-  Initialize(kShouldProcessData);
+  Initialize();
   InSequence s;
 
   // Send [0, 18) with fin.
@@ -1264,7 +1261,7 @@
 }
 
 TEST_F(QuicStreamTest, ResetStreamOnTtlExpiresRetransmitLostData) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN))
       .WillOnce(Invoke(MockQuicSession::ConsumeData));
@@ -1288,7 +1285,7 @@
 }
 
 TEST_F(QuicStreamTest, ResetStreamOnTtlExpiresEarlyRetransmitData) {
-  Initialize(kShouldProcessData);
+  Initialize();
 
   EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN))
       .WillOnce(Invoke(MockQuicSession::ConsumeData));
diff --git a/net/third_party/quic/core/quic_trace_visitor.cc b/net/third_party/quic/core/quic_trace_visitor.cc
index 4768bc9..a6951dd 100644
--- a/net/third_party/quic/core/quic_trace_visitor.cc
+++ b/net/third_party/quic/core/quic_trace_visitor.cc
@@ -89,6 +89,7 @@
       case STOP_SENDING_FRAME:
       case MESSAGE_FRAME:
       case CRYPTO_FRAME:
+      case NEW_TOKEN_FRAME:
         break;
 
       // Ignore gQUIC-specific frames.
@@ -215,6 +216,7 @@
     case STOP_SENDING_FRAME:
     case MESSAGE_FRAME:
     case CRYPTO_FRAME:
+    case NEW_TOKEN_FRAME:
       break;
 
     case NUM_FRAME_TYPES:
diff --git a/net/third_party/quic/core/quic_types.h b/net/third_party/quic/core/quic_types.h
index 5776ed2..00b95e3 100644
--- a/net/third_party/quic/core/quic_types.h
+++ b/net/third_party/quic/core/quic_types.h
@@ -185,6 +185,7 @@
   STOP_SENDING_FRAME,
   MESSAGE_FRAME,
   CRYPTO_FRAME,
+  NEW_TOKEN_FRAME,
 
   NUM_FRAME_TYPES
 };
@@ -225,9 +226,8 @@
   // bit specifically when/as needed.
   IETF_STREAM = 0x10,
   IETF_CRYPTO = 0x18,
-  // TODO(fkastenholz): When the NEW_TOKEN frame type value is added, need
-  // to update the test in ProcessIetfFrameData that checks to see if the frame
-  // type has not been minimally encoded.
+  IETF_NEW_TOKEN = 0x19,
+
   // MESSAGE frame type is not yet determined, use 0x2x temporarily to give
   // stream frame some wiggle room.
   IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20,
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.cc b/net/third_party/quic/core/quic_unacked_packet_map.cc
index 0114fbbb..a7b509d7f 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map.cc
@@ -299,10 +299,6 @@
   }
 }
 
-bool QuicUnackedPacketMap::HasUnackedPackets() const {
-  return !unacked_packets_.empty();
-}
-
 bool QuicUnackedPacketMap::HasInFlightPackets() const {
   return bytes_in_flight_ > 0;
 }
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.h b/net/third_party/quic/core/quic_unacked_packet_map.h
index 7d4c5481..b1c4fbd 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map.h
+++ b/net/third_party/quic/core/quic_unacked_packet_map.h
@@ -81,13 +81,13 @@
   // acked.
   bool HasRetransmittableFrames(const QuicTransmissionInfo& info) const;
 
-  // Returns true if there are any unacked packets.
-  bool HasUnackedPackets() const;
-
   // Returns true if there are any unacked packets which have retransmittable
   // frames.
   bool HasUnackedRetransmittableFrames() const;
 
+  // Returns true if there are no packets present in the unacked packet map.
+  bool empty() const { return unacked_packets_.empty(); }
+
   // Returns the largest packet number that has been sent.
   QuicPacketNumber largest_sent_packet() const { return largest_sent_packet_; }
 
diff --git a/net/third_party/quic/core/quic_unacked_packet_map_test.cc b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
index 0d8d325f..cdb7e6e7 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map_test.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
@@ -98,13 +98,13 @@
   void VerifyUnackedPackets(QuicPacketNumber* packets, size_t num_packets) {
     unacked_packets_.RemoveObsoletePackets();
     if (num_packets == 0) {
-      EXPECT_FALSE(unacked_packets_.HasUnackedPackets());
+      EXPECT_TRUE(unacked_packets_.empty());
       if (!GetQuicReloadableFlag(quic_optimize_inflight_check)) {
         EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames());
       }
       return;
     }
-    EXPECT_TRUE(unacked_packets_.HasUnackedPackets());
+    EXPECT_FALSE(unacked_packets_.empty());
     for (size_t i = 0; i < num_packets; ++i) {
       EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i];
     }
diff --git a/net/third_party/quic/core/quic_write_blocked_list.h b/net/third_party/quic/core/quic_write_blocked_list.h
index fc3e844..5e41d32 100644
--- a/net/third_party/quic/core/quic_write_blocked_list.h
+++ b/net/third_party/quic/core/quic_write_blocked_list.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "net/third_party/quic/core/quic_packets.h"
+#include "net/third_party/quic/platform/api/quic_containers.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/platform/api/quic_map_util.h"
 #include "net/third_party/spdy/core/priority_write_scheduler.h"
@@ -177,13 +178,12 @@
       bool is_blocked;
     };
 
-    std::vector<StreamIdBlockedPair>::const_iterator begin() const {
-      return streams_.cbegin();
-    }
+    // Optimized for the typical case of 2 static streams per session.
+    typedef QuicInlinedVector<StreamIdBlockedPair, 2> StreamsVector;
 
-    std::vector<StreamIdBlockedPair>::const_iterator end() const {
-      return streams_.cend();
-    }
+    StreamsVector::const_iterator begin() const { return streams_.cbegin(); }
+
+    StreamsVector::const_iterator end() const { return streams_.cend(); }
 
     size_t num_blocked() const { return num_blocked_; }
 
@@ -254,7 +254,7 @@
 
    private:
     size_t num_blocked_ = 0;
-    std::vector<StreamIdBlockedPair> streams_;
+    StreamsVector streams_;
   };
 
   StaticStreamCollection static_stream_collection_;
diff --git a/net/third_party/quic/platform/api/quic_test_random.h b/net/third_party/quic/platform/api/quic_test_random.h
deleted file mode 100644
index abb65c96..0000000
--- a/net/third_party/quic/platform/api/quic_test_random.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_TEST_RANDOM_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_TEST_RANDOM_H_
-
-#include <stdint.h>
-#include <limits>
-
-#include "net/third_party/quic/platform/api/quic_string.h"
-#include "net/third_party/quic/platform/impl/quic_test_random_impl.h"
-
-namespace quic {
-namespace test {
-
-using QuicTestRandom = QuicTestRandomImpl;
-
-}  // namespace test
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_TEST_RANDOM_H_
diff --git a/net/third_party/quic/platform/api/quic_url_utils.cc b/net/third_party/quic/platform/api/quic_url_utils.cc
deleted file mode 100644
index 4cc8f74..0000000
--- a/net/third_party/quic/platform/api/quic_url_utils.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/third_party/quic/platform/api/quic_url_utils.h"
-#include "net/third_party/quic/platform/api/quic_string.h"
-
-namespace quic {
-
-// static
-QuicString QuicUrlUtils::HostName(QuicStringPiece url) {
-  return QuicUrlUtilsImpl::HostName(url);
-}
-
-// static
-bool QuicUrlUtils::IsValidUrl(QuicStringPiece url) {
-  return QuicUrlUtilsImpl::IsValidUrl(url);
-}
-
-// static
-QuicString QuicUrlUtils::GetPushPromiseUrl(QuicStringPiece scheme,
-                                           QuicStringPiece authority,
-                                           QuicStringPiece path) {
-  return QuicUrlUtilsImpl::GetPushPromiseUrl(scheme, authority, path);
-}
-
-}  // namespace quic
diff --git a/net/third_party/quic/platform/api/quic_url_utils.h b/net/third_party/quic/platform/api/quic_url_utils.h
deleted file mode 100644
index 65a1467..0000000
--- a/net/third_party/quic/platform/api/quic_url_utils.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_UTILS_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_UTILS_H_
-
-#include "base/macros.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_string.h"
-#include "net/third_party/quic/platform/api/quic_string_piece.h"
-#include "net/third_party/quic/platform/impl/quic_url_utils_impl.h"
-
-namespace quic {
-
-class QUIC_EXPORT_PRIVATE QuicUrlUtils {
- public:
-  QuicUrlUtils() = delete;
-
-  // Returns hostname, or empty std::string if missing.
-  static QuicString HostName(QuicStringPiece url);
-
-  // Returns false if any of these conditions occur: (1) Host name too long; (2)
-  // Invalid characters in host name, path or params; (3) Invalid port number
-  // (e.g. greater than 65535).
-  static bool IsValidUrl(QuicStringPiece url);
-
-  // Returns a canonical, valid URL for a PUSH_PROMISE with the specified
-  // ":scheme", ":authority", and ":path" header fields, or an empty
-  // string if the resulting URL is not valid or supported.
-  static QuicString GetPushPromiseUrl(QuicStringPiece scheme,
-                                      QuicStringPiece authority,
-                                      QuicStringPiece path);
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_UTILS_H_
diff --git a/net/third_party/quic/platform/impl/quic_test_random_impl.cc b/net/third_party/quic/platform/impl/quic_test_random_impl.cc
deleted file mode 100644
index 0a0965cb..0000000
--- a/net/third_party/quic/platform/impl/quic_test_random_impl.cc
+++ /dev/null
@@ -1,58 +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/third_party/quic/platform/api/quic_test_random.h"
-
-#include <memory>
-
-#include "base/rand_util.h"
-
-namespace quic {
-namespace test {
-
-bool QuicTestRandom::OneIn(int n) {
-  return base::RandGenerator(n) == 0;
-}
-
-int32_t QuicTestRandom::Uniform(int32_t n) {
-  return base::RandGenerator(n);
-}
-
-uint8_t QuicTestRandom::Rand8() {
-  return base::RandGenerator(
-      static_cast<uint64_t>(std::numeric_limits<uint8_t>::max()) + 1);
-}
-
-uint16_t QuicTestRandom::Rand16() {
-  return base::RandGenerator(
-      static_cast<uint64_t>(std::numeric_limits<uint16_t>::max()) + 1);
-}
-
-uint32_t QuicTestRandom::Rand32() {
-  return base::RandGenerator(
-      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1);
-}
-
-uint64_t QuicTestRandom::Rand64() {
-  return base::RandUint64();
-}
-
-int32_t QuicTestRandom::Next() {
-  return Rand32();
-}
-
-int32_t QuicTestRandom::Skewed(int max_log) {
-  const uint32_t base = Rand32() % (max_log + 1);
-  const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
-  return Rand32() & mask;
-}
-
-QuicString QuicTestRandom::RandString(int length) {
-  std::unique_ptr<char[]> buffer(new char[length]);
-  base::RandBytes(buffer.get(), length);
-  return QuicString(buffer.get(), length);
-}
-
-}  // namespace test
-}  // namespace quic
diff --git a/net/third_party/quic/platform/impl/quic_test_random_impl.h b/net/third_party/quic/platform/impl/quic_test_random_impl.h
deleted file mode 100644
index e8112f3..0000000
--- a/net/third_party/quic/platform/impl/quic_test_random_impl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_TEST_RANDOM_IMPL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_TEST_RANDOM_IMPL_H_
-
-namespace quic {
-namespace test {
-
-class QuicTestRandomBaseImpl {
- public:
-  virtual ~QuicTestRandomBaseImpl() {}
-  virtual bool OneIn(int n) = 0;
-  virtual int32_t Uniform(int32_t n) = 0;
-  virtual uint8_t Rand8() = 0;
-  virtual uint16_t Rand16() = 0;
-  virtual uint32_t Rand32() = 0;
-  virtual uint64_t Rand64() = 0;
-  virtual int32_t Next() = 0;
-  virtual int32_t Skewed(int max_log) = 0;
-  virtual QuicString RandString(int length) = 0;
-
-  // STL UniformRandomNumberGenerator implementation.
-  typedef uint32_t result_type;
-  static constexpr result_type min() { return 0; }
-  static constexpr result_type max() {
-    return std::numeric_limits<uint32_t>::max();
-  }
-  result_type operator()() { return Rand32(); }
-};
-
-// QuicTestRandom holds no state: instances use the same base::RandGenerator
-// with a global state.
-class QuicTestRandomImpl : public QuicTestRandomBaseImpl {
- public:
-  ~QuicTestRandomImpl() override {}
-  bool OneIn(int n) override;
-  int32_t Uniform(int32_t n) override;
-  uint8_t Rand8() override;
-  uint16_t Rand16() override;
-  uint32_t Rand32() override;
-  uint64_t Rand64() override;
-  int32_t Next() override;
-  int32_t Skewed(int max_log) override;
-  QuicString RandString(int length) override;
-};
-
-}  // namespace test
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_TEST_RANDOM_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_url_utils_impl.cc b/net/third_party/quic/platform/impl/quic_url_utils_impl.cc
deleted file mode 100644
index daf5792..0000000
--- a/net/third_party/quic/platform/impl/quic_url_utils_impl.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/third_party/quic/platform/impl/quic_url_utils_impl.h"
-
-#include "url/gurl.h"
-
-namespace quic {
-
-// static
-std::string QuicUrlUtilsImpl::HostName(QuicStringPiece url) {
-  return GURL(url).host();
-}
-
-// static
-bool QuicUrlUtilsImpl::IsValidUrl(QuicStringPiece url) {
-  return GURL(url).is_valid();
-}
-
-// static
-std::string QuicUrlUtilsImpl::GetPushPromiseUrl(QuicStringPiece scheme,
-                                                QuicStringPiece authority,
-                                                QuicStringPiece path) {
-  // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the
-  // path and query parts of the target URI (the "path-absolute" production
-  // and optionally a '?' character followed by the "query" production (see
-  // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the
-  // value '*' for the ":path" pseudo-header field.
-  //
-  // This pseudo-header field MUST NOT be empty for "http" or "https" URIs;
-  // "http" or "https" URIs that do not contain a path MUST include a value of
-  // '/'. The exception to this rule is an OPTIONS request for an "http" or
-  // "https" URI that does not include a path component; these MUST include a
-  // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4).
-  //
-  // In addition to the above restriction from RFC 7540, note that RFC3986
-  // defines the "path-absolute" construction as starting with "/" but not "//".
-  //
-  // RFC 7540, Section  8.2.1:  The header fields in PUSH_PROMISE and any
-  // subsequent CONTINUATION frames MUST be a valid and complete set of request
-  // header fields (Section 8.1.2.3).  The server MUST include a method in the
-  // ":method" pseudo-header field that is safe and cacheable.
-  //
-  // RFC 7231, Section  4.2.1:
-  // ... this specification defines GET, HEAD, and POST as cacheable, ...
-  //
-  // Since the OPTIONS method is not cacheable, it cannot be the method of a
-  // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section
-  // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be
-  // "*").
-  if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) {
-    return std::string();
-  }
-
-  // Validate the scheme; this is to ensure a scheme of "foo://bar" is not
-  // parsed as a URL of "foo://bar://baz" when combined with a host of "baz".
-  std::string canonical_scheme;
-  url::StdStringCanonOutput canon_output(&canonical_scheme);
-  url::Component canon_component;
-  url::Component scheme_component(0, scheme.size());
-
-  if (!url::CanonicalizeScheme(scheme.data(), scheme_component, &canon_output,
-                               &canon_component) ||
-      !canon_component.is_nonempty() || canon_component.begin != 0) {
-    return std::string();
-  }
-  canonical_scheme.resize(canon_component.len + 1);
-
-  // Validate the authority; this is to ensure an authority such as
-  // "host/path" is not accepted, as when combined with a scheme like
-  // "http://", could result in a URL of "http://host/path".
-  url::Component auth_component(0, authority.size());
-  url::Component username_component;
-  url::Component password_component;
-  url::Component host_component;
-  url::Component port_component;
-
-  url::ParseAuthority(authority.data(), auth_component, &username_component,
-                      &password_component, &host_component, &port_component);
-
-  // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated
-  // "userinfo" subcomponent for "http" or "https" schemed URIs.
-  //
-  // Note: Although |canonical_scheme| has not yet been checked for that, as
-  // it is performed later in processing, only "http" and "https" schemed
-  // URIs are supported for PUSH.
-  if (username_component.is_valid() || password_component.is_valid()) {
-    return std::string();
-  }
-
-  // Failed parsing or no host present. ParseAuthority() will ensure that
-  // host_component + port_component cover the entire string, if
-  // username_component and password_component are not present.
-  if (!host_component.is_nonempty()) {
-    return std::string();
-  }
-
-  // Validate the port (if present; it's optional).
-  int parsed_port_number = url::PORT_INVALID;
-  if (port_component.is_nonempty()) {
-    parsed_port_number = url::ParsePort(authority.data(), port_component);
-    if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) {
-      return std::string();
-    }
-  }
-
-  // Validate the host by attempting to canoncalize it. Invalid characters
-  // will result in a canonicalization failure (e.g. '/')
-  std::string canon_host;
-  canon_output = url::StdStringCanonOutput(&canon_host);
-  canon_component.reset();
-  if (!url::CanonicalizeHost(authority.data(), host_component, &canon_output,
-                             &canon_component) ||
-      !canon_component.is_nonempty() || canon_component.begin != 0) {
-    return std::string();
-  }
-
-  // At this point, "authority" has been validated to either be of the form
-  // 'host:port' or 'host', with 'host' being a valid domain or IP address,
-  // and 'port' (if present), being a valid port. Attempt to construct a
-  // URL of just the (scheme, host, port), which should be safe and will not
-  // result in ambiguous parsing.
-  //
-  // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed
-  // URIs, consistent with the other restrictions enforced above.
-  //
-  // Note: url::CanonicalizeScheme() will have added the ':' to
-  // |canonical_scheme|.
-  GURL origin_url(canonical_scheme + "//" + std::string(authority));
-  if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() ||
-      // The following checks are merely defense in depth.
-      origin_url.has_username() || origin_url.has_password() ||
-      (origin_url.has_path() && origin_url.path_piece() != "/") ||
-      origin_url.has_query() || origin_url.has_ref()) {
-    return std::string();
-  }
-
-  // Attempt to parse the path.
-  std::string spec = origin_url.GetWithEmptyPath().spec();
-  spec.pop_back();  // Remove the '/', as ":path" must contain it.
-  spec.append(std::string(path));
-
-  // Attempt to parse the full URL, with the path as well. Ensure there is no
-  // fragment to the query.
-  GURL full_url(spec);
-  if (!full_url.is_valid() || full_url.has_ref()) {
-    return std::string();
-  }
-
-  return full_url.spec();
-}
-
-}  // namespace quic
diff --git a/net/third_party/quic/platform/impl/quic_url_utils_impl.h b/net/third_party/quic/platform/impl/quic_url_utils_impl.h
deleted file mode 100644
index 84facd98..0000000
--- a/net/third_party/quic/platform/impl/quic_url_utils_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_UTILS_IMPL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_UTILS_IMPL_H_
-
-#include "base/macros.h"
-#include "net/third_party/quic/core/quic_server_id.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_string_piece.h"
-
-namespace quic {
-
-class QUIC_EXPORT_PRIVATE QuicUrlUtilsImpl {
- public:
-  // Returns hostname, or empty std::string if missing.
-  static std::string HostName(QuicStringPiece url);
-
-  // Returns false if any of these conditions occur: (1) Host name too long; (2)
-  // Invalid characters in host name, path or params; (3) Invalid port number
-  // (e.g. greater than 65535).
-  static bool IsValidUrl(QuicStringPiece url);
-
-  // Returns a canonical, valid URL for a PUSH_PROMISE with the specified
-  // ":scheme", ":authority", and ":path" header fields, or an empty
-  // string if the resulting URL is not valid or supported.
-  static std::string GetPushPromiseUrl(QuicStringPiece scheme,
-                                       QuicStringPiece authority,
-                                       QuicStringPiece path);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(QuicUrlUtilsImpl);
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_UTILS_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_url_utils_impl_test.cc b/net/third_party/quic/platform/impl/quic_url_utils_impl_test.cc
deleted file mode 100644
index d0e4b01..0000000
--- a/net/third_party/quic/platform/impl/quic_url_utils_impl_test.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 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 "net/third_party/quic/platform/impl/quic_url_utils_impl.h"
-
-#include <cstdint>
-
-#include "net/third_party/quic/platform/api/quic_arraysize.h"
-#include "net/third_party/quic/platform/api/quic_test.h"
-
-using std::string;
-
-namespace quic {
-namespace test {
-namespace {
-
-using QuicUrlUtilsImplTest = QuicTest;
-
-TEST_F(QuicUrlUtilsImplTest, GetPushPromiseUrl) {
-  // Test rejection of various inputs.
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl("file", "localhost",
-                                                    "/etc/password"));
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl(
-                    "file", "", "/C:/Windows/System32/Config/"));
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl(
-                    "", "https://www.google.com", "/"));
-
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl("https://www.google.com",
-                                                    "www.google.com", "/"));
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl("https://",
-                                                    "www.google.com", "/"));
-  EXPECT_EQ("", QuicUrlUtilsImpl::GetPushPromiseUrl("https", "", "/"));
-  EXPECT_EQ(
-      "", QuicUrlUtilsImpl::GetPushPromiseUrl("https", "", "www.google.com/"));
-  EXPECT_EQ(
-      "", QuicUrlUtilsImpl::GetPushPromiseUrl("https", "www.google.com/", "/"));
-  EXPECT_EQ("",
-            QuicUrlUtilsImpl::GetPushPromiseUrl("https", "www.google.com", ""));
-  EXPECT_EQ(
-      "", QuicUrlUtilsImpl::GetPushPromiseUrl("https", "www.google", ".com/"));
-
-  // Test acception/rejection of various input combinations.
-  // |input_headers| is an array of pairs. The first value of each pair is a
-  // string that will be used as one of the inputs of GetPushPromiseUrl(). The
-  // second value of each pair is a bitfield where the lowest 3 bits indicate
-  // for which headers that string is valid (in a PUSH_PROMISE). For example,
-  // the string "http" would be valid for both the ":scheme" and ":authority"
-  // headers, so the bitfield paired with it is set to SCHEME | AUTH.
-  const unsigned char SCHEME = (1u << 0);
-  const unsigned char AUTH = (1u << 1);
-  const unsigned char PATH = (1u << 2);
-  const std::pair<const char*, unsigned char> input_headers[] = {
-      {"http", SCHEME | AUTH},
-      {"https", SCHEME | AUTH},
-      {"hTtP", SCHEME | AUTH},
-      {"HTTPS", SCHEME | AUTH},
-      {"www.google.com", AUTH},
-      {"90af90e0", AUTH},
-      {"12foo%20-bar:00001233", AUTH},
-      {"GOO\u200b\u2060\ufeffgoo", AUTH},
-      {"192.168.0.5", AUTH},
-      {"[::ffff:192.168.0.1.]", AUTH},
-      {"http:", AUTH},
-      {"bife l", AUTH},
-      {"/", PATH},
-      {"/foo/bar/baz", PATH},
-      {"/%20-2DVdkj.cie/foe_.iif/", PATH},
-      {"http://", 0},
-      {":443", 0},
-      {":80/eddd", 0},
-      {"google.com:-0", 0},
-      {"google.com:65536", 0},
-      {"http://google.com", 0},
-      {"http://google.com:39", 0},
-      {"//google.com/foo", 0},
-      {".com/", 0},
-      {"http://www.google.com/", 0},
-      {"http://foo:439", 0},
-      {"[::ffff:192.168", 0},
-      {"]/", 0},
-      {"//", 0}};
-  for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) {
-    bool should_accept = (input_headers[i].second & SCHEME);
-    for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) {
-      bool should_accept_2 = should_accept && (input_headers[j].second & AUTH);
-      for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) {
-        // |should_accept_3| indicates whether or not GetPushPromiseUrl() is
-        // expected to accept this input combination.
-        bool should_accept_3 =
-            should_accept_2 && (input_headers[k].second & PATH);
-
-        std::string url = QuicUrlUtilsImpl::GetPushPromiseUrl(
-            input_headers[i].first, input_headers[j].first,
-            input_headers[k].first);
-
-        ::testing::AssertionResult result = ::testing::AssertionSuccess();
-        if (url.empty() == should_accept_3) {
-          result = ::testing::AssertionFailure()
-                   << "GetPushPromiseUrl() accepted/rejected the inputs when "
-                      "it shouldn't have."
-                   << std::endl
-                   << "     scheme: " << input_headers[i].first << std::endl
-                   << "  authority: " << input_headers[j].first << std::endl
-                   << "       path: " << input_headers[k].first << std::endl
-                   << "Output: " << url << std::endl;
-        }
-        ASSERT_TRUE(result);
-      }
-    }
-  }
-
-  // Test canonicalization of various valid inputs.
-  EXPECT_EQ("http://www.google.com/",
-            QuicUrlUtilsImpl::GetPushPromiseUrl("http", "www.google.com", "/"));
-  EXPECT_EQ("https://www.goo-gle.com/fOOo/baRR",
-            QuicUrlUtilsImpl::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm",
-                                                "/fOOo/baRR"));
-  EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce",
-            QuicUrlUtilsImpl::GetPushPromiseUrl(
-                "hTtPs", "Www.gOo-Gle.Com:000003278", "/pAth/To/reSOurce"));
-  EXPECT_EQ(
-      "https://foo%20bar/foo/bar/baz",
-      QuicUrlUtilsImpl::GetPushPromiseUrl("https", "foo bar", "/foo/bar/baz"));
-  EXPECT_EQ("http://foo.com:70/e/", QuicUrlUtilsImpl::GetPushPromiseUrl(
-                                        "http", "foo.com:0000070", "/e/"));
-  EXPECT_EQ("http://192.168.0.1:70/e/",
-            QuicUrlUtilsImpl::GetPushPromiseUrl("http", "0300.0250.00.01:0070",
-                                                "/e/"));
-  EXPECT_EQ("http://192.168.0.1/e/",
-            QuicUrlUtilsImpl::GetPushPromiseUrl("http", "0xC0a80001", "/e/"));
-  EXPECT_EQ("http://[::c0a8:1]/", QuicUrlUtilsImpl::GetPushPromiseUrl(
-                                      "http", "[::192.168.0.1]", "/"));
-  EXPECT_EQ("https://[::ffff:c0a8:1]/",
-            QuicUrlUtilsImpl::GetPushPromiseUrl(
-                "https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/"));
-}
-
-};  // namespace
-};  // namespace test
-};  // namespace quic
\ No newline at end of file
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc
index 66d3873..cdc04b2 100644
--- a/net/third_party/quic/quartc/quartc_factory.cc
+++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -43,6 +43,10 @@
   QuicTagVector copt;
   copt.push_back(kNSTP);
 
+  // Enable and request QUIC to include receive timestamps in ACK frames.
+  SetQuicReloadableFlag(quic_send_timestamps, true);
+  copt.push_back(kSTMP);
+
   // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be
   // false.
   SetQuicReloadableFlag(quic_enable_ack_decimation, false);
diff --git a/net/third_party/quic/test_tools/quic_test_utils.cc b/net/third_party/quic/test_tools/quic_test_utils.cc
index 799c1e0..88f943ec 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.cc
+++ b/net/third_party/quic/test_tools/quic_test_utils.cc
@@ -245,6 +245,10 @@
   return true;
 }
 
+bool NoOpFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+  return true;
+}
+
 bool NoOpFramerVisitor::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
   return true;
 }
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index b681914..bfad4855 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -271,6 +271,7 @@
                bool(const QuicApplicationCloseFrame& frame));
   MOCK_METHOD1(OnNewConnectionIdFrame,
                bool(const QuicNewConnectionIdFrame& frame));
+  MOCK_METHOD1(OnNewTokenFrame, bool(const QuicNewTokenFrame& frame));
   MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
   MOCK_METHOD1(OnPathChallengeFrame, bool(const QuicPathChallengeFrame& frame));
   MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame& frame));
@@ -318,6 +319,7 @@
   bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
   bool OnApplicationCloseFrame(const QuicApplicationCloseFrame& frame) override;
   bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
   bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
   bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
diff --git a/net/third_party/quic/test_tools/simple_quic_framer.cc b/net/third_party/quic/test_tools/simple_quic_framer.cc
index e8dec44..f8ff820 100644
--- a/net/third_party/quic/test_tools/simple_quic_framer.cc
+++ b/net/third_party/quic/test_tools/simple_quic_framer.cc
@@ -127,6 +127,11 @@
     return true;
   }
 
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+    new_token_frames_.push_back(frame);
+    return true;
+  }
+
   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
     stop_sending_frames_.push_back(frame);
     return true;
@@ -249,6 +254,7 @@
   std::vector<QuicWindowUpdateFrame> window_update_frames_;
   std::vector<QuicBlockedFrame> blocked_frames_;
   std::vector<QuicNewConnectionIdFrame> new_connection_id_frames_;
+  std::vector<QuicNewTokenFrame> new_token_frames_;
   std::vector<QuicMessageFrame> message_frames_;
   std::vector<std::unique_ptr<QuicString>> stream_data_;
 };
diff --git a/net/third_party/quic/tools/quic_packet_printer_bin.cc b/net/third_party/quic/tools/quic_packet_printer_bin.cc
index d07b6856..b9c3114 100644
--- a/net/third_party/quic/tools/quic_packet_printer_bin.cc
+++ b/net/third_party/quic/tools/quic_packet_printer_bin.cc
@@ -160,6 +160,10 @@
     std::cerr << "OnNewConnectionIdFrame: " << frame;
     return true;
   }
+  bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+    std::cerr << "OnNewTokenFrame: " << frame;
+    return true;
+  }
   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
     std::cerr << "OnStopSendingFrame: " << frame;
     return true;
diff --git a/remoting/host/input_monitor/local_mouse_input_monitor_win.cc b/remoting/host/input_monitor/local_mouse_input_monitor_win.cc
index ea3605ec..64baa3c 100644
--- a/remoting/host/input_monitor/local_mouse_input_monitor_win.cc
+++ b/remoting/host/input_monitor/local_mouse_input_monitor_win.cc
@@ -42,6 +42,8 @@
   LocalInputMonitor::MouseMoveCallback on_mouse_move_;
   base::OnceClosure disconnect_callback_;
 
+  webrtc::DesktopVector mouse_position_;
+
   // Tracks whether the instance is registered to receive raw input events.
   bool registered_ = false;
 
@@ -99,18 +101,30 @@
 
   // Notify the observer about mouse events generated locally. Remote (injected)
   // mouse events do not specify a device handle (based on observed behavior).
-  if (input->header.dwType == RIM_TYPEMOUSE &&
-      input->header.hDevice != nullptr) {
-    POINT position;
-    if (!GetCursorPos(&position)) {
-      position.x = 0;
-      position.y = 0;
-    }
-
-    caller_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(on_mouse_move_, webrtc::DesktopVector(
-                                                      position.x, position.y)));
+  if (input->header.dwType != RIM_TYPEMOUSE ||
+      input->header.hDevice == nullptr) {
+    return;
   }
+
+  POINT position;
+  if (!GetCursorPos(&position)) {
+    position.x = 0;
+    position.y = 0;
+  }
+  webrtc::DesktopVector new_position(position.x, position.y);
+
+  // Ignore the event if the cursor position or button states have not changed.
+  // Note: If GetCursorPos fails above, we err on the safe side and treat it
+  // like a movement.
+  if (mouse_position_.equals(new_position) &&
+      !input->data.mouse.usButtonFlags && !new_position.is_zero()) {
+    return;
+  }
+
+  mouse_position_ = new_position;
+
+  caller_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(on_mouse_move_, std::move(new_position)));
 }
 
 void MouseRawInputHandlerWin::OnError() {
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index ecda780..bd224a18 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -40,7 +40,7 @@
 // ResourceScheduler just as HTTP/1.1 resources are. However, requests from such
 // servers are not subject to kMaxNumDelayableRequestsPerHostPerClient limit.
 const base::Feature kDelayRequestsOnMultiplexedConnections{
-    "DelayRequestsOnMultiplexedConnections", base::FEATURE_DISABLED_BY_DEFAULT};
+    "DelayRequestsOnMultiplexedConnections", base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace network
diff --git a/services/network/resource_scheduler_params_manager.cc b/services/network/resource_scheduler_params_manager.cc
index 644d785..f363f2a 100644
--- a/services/network/resource_scheduler_params_manager.cc
+++ b/services/network/resource_scheduler_params_manager.cc
@@ -87,8 +87,10 @@
               features::kDelayRequestsOnMultiplexedConnections,
               "MaxEffectiveConnectionType"));
 
-  if (!max_effective_connection_type)
-    return result;
+  if (!max_effective_connection_type) {
+    // Use a default value if one is not set using field trial params.
+    max_effective_connection_type = net::EFFECTIVE_CONNECTION_TYPE_3G;
+  }
 
   for (int ect = net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
        ect <= max_effective_connection_type.value(); ++ect) {
diff --git a/services/network/resource_scheduler_params_manager_unittest.cc b/services/network/resource_scheduler_params_manager_unittest.cc
index 60f71de..742767b 100644
--- a/services/network/resource_scheduler_params_manager_unittest.cc
+++ b/services/network/resource_scheduler_params_manager_unittest.cc
@@ -87,7 +87,6 @@
     switch (effective_connection_type) {
       case net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN:
       case net::EFFECTIVE_CONNECTION_TYPE_OFFLINE:
-      case net::EFFECTIVE_CONNECTION_TYPE_3G:
       case net::EFFECTIVE_CONNECTION_TYPE_4G:
         EXPECT_EQ(10u, resource_scheduler_params_manager
                            .GetParamsForEffectiveConnectionType(
@@ -102,7 +101,20 @@
                 .GetParamsForEffectiveConnectionType(effective_connection_type)
                 .delay_requests_on_multiplexed_connections);
         return;
-
+      case net::EFFECTIVE_CONNECTION_TYPE_3G:
+        EXPECT_EQ(10u, resource_scheduler_params_manager
+                           .GetParamsForEffectiveConnectionType(
+                               effective_connection_type)
+                           .max_delayable_requests);
+        EXPECT_EQ(0.0, resource_scheduler_params_manager
+                           .GetParamsForEffectiveConnectionType(
+                               effective_connection_type)
+                           .non_delayable_weight);
+        EXPECT_TRUE(
+            resource_scheduler_params_manager
+                .GetParamsForEffectiveConnectionType(effective_connection_type)
+                .delay_requests_on_multiplexed_connections);
+        return;
       case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G:
       case net::EFFECTIVE_CONNECTION_TYPE_2G:
         EXPECT_EQ(8u, resource_scheduler_params_manager
@@ -113,7 +125,7 @@
                            .GetParamsForEffectiveConnectionType(
                                effective_connection_type)
                            .non_delayable_weight);
-        EXPECT_FALSE(
+        EXPECT_TRUE(
             resource_scheduler_params_manager
                 .GetParamsForEffectiveConnectionType(effective_connection_type)
                 .delay_requests_on_multiplexed_connections);
@@ -184,6 +196,17 @@
                       .GetParamsForEffectiveConnectionType(ect)
                       .delay_requests_on_multiplexed_connections);
 
+    } else if (effective_connection_type == net::EFFECTIVE_CONNECTION_TYPE_3G) {
+      EXPECT_EQ(10u, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .max_delayable_requests);
+      EXPECT_EQ(0.0, resource_scheduler_params_manager
+                         .GetParamsForEffectiveConnectionType(ect)
+                         .non_delayable_weight);
+      EXPECT_FALSE(resource_scheduler_params_manager
+                       .GetParamsForEffectiveConnectionType(ect)
+                       .delay_requests_on_multiplexed_connections);
+
     } else {
       VerifyDefaultParams(
           resource_scheduler_params_manager,
diff --git a/services/service_manager/public/cpp/service_keepalive.cc b/services/service_manager/public/cpp/service_keepalive.cc
index 4eb0d60..d8f30aa 100644
--- a/services/service_manager/public/cpp/service_keepalive.cc
+++ b/services/service_manager/public/cpp/service_keepalive.cc
@@ -11,7 +11,7 @@
 namespace service_manager {
 
 ServiceKeepalive::ServiceKeepalive(ServiceContext* context,
-                                   base::TimeDelta idle_timeout,
+                                   base::Optional<base::TimeDelta> idle_timeout,
                                    TimeoutObserver* timeout_observer)
     : context_(context),
       idle_timeout_(idle_timeout),
@@ -40,7 +40,9 @@
 }
 
 void ServiceKeepalive::OnRefCountZero() {
-  idle_timer_.Start(FROM_HERE, idle_timeout_,
+  if (!idle_timeout_.has_value())
+    return;
+  idle_timer_.Start(FROM_HERE, idle_timeout_.value(),
                     base::BindRepeating(&ServiceKeepalive::OnTimerExpired,
                                         weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/services/service_manager/public/cpp/service_keepalive.h b/services/service_manager/public/cpp/service_keepalive.h
index af5db0c7..9fb0b9df 100644
--- a/services/service_manager/public/cpp/service_keepalive.h
+++ b/services/service_manager/public/cpp/service_keepalive.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/timer/timer.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 
@@ -39,10 +40,12 @@
   };
 
   // Creates a keepalive which allows the service to be idle for |idle_timeout|
-  // before requesting termination. Both |context| and |timeout_observer| are
-  // not owned and must outlive the ServiceKeepalive instance.
+  // before requesting termination. If |idle_timeout| is not given, the
+  // ServiceKeepalive will never request termination, i.e. the service will
+  // stay alive indefinitely. Both |context| and |timeout_observer| are not
+  // owned and must outlive the ServiceKeepalive instance.
   ServiceKeepalive(ServiceContext* context,
-                   base::TimeDelta idle_timeout,
+                   base::Optional<base::TimeDelta> idle_timeout,
                    TimeoutObserver* timeout_observer = nullptr);
   ~ServiceKeepalive();
 
@@ -55,7 +58,7 @@
   void OnTimerExpired();
 
   ServiceContext* const context_;
-  const base::TimeDelta idle_timeout_;
+  const base::Optional<base::TimeDelta> idle_timeout_;
   TimeoutObserver* const timeout_observer_;
   base::OneShotTimer idle_timer_;
   ServiceContextRefFactory ref_factory_;
diff --git a/services/video_capture/service_impl.cc b/services/video_capture/service_impl.cc
index 9c981ae..0316e85 100644
--- a/services/video_capture/service_impl.cc
+++ b/services/video_capture/service_impl.cc
@@ -4,6 +4,7 @@
 
 #include "services/video_capture/service_impl.h"
 
+#include "build/build_config.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/video_capture/device_factory_provider_impl.h"
@@ -13,9 +14,8 @@
 
 namespace video_capture {
 
-ServiceImpl::ServiceImpl(float shutdown_delay_in_seconds)
-    : shutdown_delay_in_seconds_(shutdown_delay_in_seconds),
-      weak_factory_(this) {}
+ServiceImpl::ServiceImpl(base::Optional<base::TimeDelta> shutdown_delay)
+    : shutdown_delay_(shutdown_delay), weak_factory_(this) {}
 
 ServiceImpl::~ServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -25,7 +25,14 @@
 
 // static
 std::unique_ptr<service_manager::Service> ServiceImpl::Create() {
-  return std::make_unique<ServiceImpl>();
+#if defined(OS_ANDROID)
+  // On Android, we do not use automatic service shutdown, because when shutting
+  // down the service, we lose caching of the supported formats, and re-querying
+  // these can take several seconds on certain Android devices.
+  return std::make_unique<ServiceImpl>(base::Optional<base::TimeDelta>());
+#else
+  return std::make_unique<ServiceImpl>(base::TimeDelta::FromSeconds(5));
+#endif
 }
 
 void ServiceImpl::SetDestructionObserver(base::OnceClosure observer_cb) {
@@ -66,8 +73,7 @@
   // SetServiceContextRefProviderForTesting().
   if (!ref_factory_) {
     ref_factory_ = std::make_unique<service_manager::ServiceKeepalive>(
-        context(), base::TimeDelta::FromSecondsD(shutdown_delay_in_seconds_),
-        this);
+        context(), shutdown_delay_, this);
   }
 
   registry_.AddInterface<mojom::DeviceFactoryProvider>(
diff --git a/services/video_capture/service_impl.h b/services/video_capture/service_impl.h
index 5fabb9a..e5b93d9 100644
--- a/services/video_capture/service_impl.h
+++ b/services/video_capture/service_impl.h
@@ -25,7 +25,9 @@
 class ServiceImpl : public service_manager::Service,
                     public service_manager::ServiceKeepalive::TimeoutObserver {
  public:
-  ServiceImpl(float shutdown_delay_in_seconds = 5.0f);
+  // If |shutdown_delay| is provided, the service will shut itself down as soon
+  // as no client was connect for the corresponding duration.
+  explicit ServiceImpl(base::Optional<base::TimeDelta> shutdown_delay);
   ~ServiceImpl() override;
 
   static std::unique_ptr<service_manager::Service> Create();
@@ -58,7 +60,7 @@
   void LazyInitializeDeviceFactoryProvider();
   void OnProviderClientDisconnected();
 
-  const float shutdown_delay_in_seconds_;
+  const base::Optional<base::TimeDelta> shutdown_delay_;
 #if defined(OS_WIN)
   // COM must be initialized in order to access the video capture devices.
   base::win::ScopedCOMInitializer com_initializer_;
diff --git a/services/video_capture/service_main.cc b/services/video_capture/service_main.cc
index 52ae77d..52093729 100644
--- a/services/video_capture/service_main.cc
+++ b/services/video_capture/service_main.cc
@@ -7,6 +7,7 @@
 #include "services/video_capture/service_impl.h"
 
 MojoResult ServiceMain(MojoHandle service_request_handle) {
-  return service_manager::ServiceRunner(new video_capture::ServiceImpl())
+  return service_manager::ServiceRunner(
+             video_capture::ServiceImpl::Create().release())
       .Run(service_request_handle);
 }
diff --git a/services/video_capture/test/device_factory_provider_connectortest.cc b/services/video_capture/test/device_factory_provider_connectortest.cc
index 30e2165..af211e1 100644
--- a/services/video_capture/test/device_factory_provider_connectortest.cc
+++ b/services/video_capture/test/device_factory_provider_connectortest.cc
@@ -24,6 +24,7 @@
 // Test fixture that creates a video_capture::ServiceImpl and sets up a
 // local service_manager::Connector through which client code can connect to
 // it.
+template <class DeviceFactoryProviderConnectorTestTraits>
 class DeviceFactoryProviderConnectorTest : public ::testing::Test {
  public:
   DeviceFactoryProviderConnectorTest() {}
@@ -32,13 +33,8 @@
   void SetUp() override {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseFakeDeviceForMediaStream);
-    // We need to set the shutdown delay to at least some epsilon > 0 in order
-    // to avoid the service shutting down synchronously which would prevent
-    // test case ServiceIncreasesRefCountOnNewConnectionAfterDisconnect from
-    // being able to reconnect before the timer expires.
-    static const float kShutdownDelayInSeconds = 0.0001f;
-    std::unique_ptr<ServiceImpl> service_impl =
-        std::make_unique<ServiceImpl>(kShutdownDelayInSeconds);
+    std::unique_ptr<ServiceImpl> service_impl = std::make_unique<ServiceImpl>(
+        DeviceFactoryProviderConnectorTestTraits::shutdown_delay());
     service_impl->SetDestructionObserver(base::BindOnce(
         [](base::RunLoop* service_destroyed_wait_loop) {
           service_destroyed_wait_loop->Quit();
@@ -59,7 +55,9 @@
   }
 
   void TearDown() override {
-    if (factory_provider_.is_bound()) {
+    if (factory_provider_.is_bound() &&
+        DeviceFactoryProviderConnectorTestTraits::shutdown_delay()
+            .has_value()) {
       factory_provider_.reset();
       service_destroyed_wait_loop_.Run();
     }
@@ -78,9 +76,22 @@
   std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
 };
 
+// We need to set the shutdown delay to at least some epsilon > 0 in order
+// to avoid the service shutting down synchronously which would prevent
+// test case ServiceIncreasesRefCountOnNewConnectionAfterDisconnect from
+// being able to reconnect before the timer expires.
+struct ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits {
+  static base::Optional<base::TimeDelta> shutdown_delay() {
+    return base::TimeDelta::FromMicroseconds(100);
+  }
+};
+using ShortShutdownDelayDeviceFactoryProviderConnectorTest =
+    DeviceFactoryProviderConnectorTest<
+        ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits>;
+
 // Tests that the service does not quit when a client connects
 // while a second client stays connected.
-TEST_F(DeviceFactoryProviderConnectorTest,
+TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
        ServiceDoesNotQuitWhenOneOfTwoClientsDisconnects) {
   // Establish second connection
   mojom::DeviceFactoryProviderPtr second_connection;
@@ -109,7 +120,7 @@
 // Tests that the service quits when the only client disconnects after not
 // having done anything other than obtaining a connection to the fake device
 // factory.
-TEST_F(DeviceFactoryProviderConnectorTest,
+TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
        ServiceQuitsWhenSingleClientDisconnected) {
   mojom::DeviceFactoryPtr factory;
   factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
@@ -120,7 +131,7 @@
 }
 
 // Tests that the service quits when both of two clients disconnect.
-TEST_F(DeviceFactoryProviderConnectorTest,
+TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
        ServiceQuitsWhenAllClientsDisconnected) {
   // Bind another client to the DeviceFactoryProvider interface.
   mojom::DeviceFactoryProviderPtr second_connection;
@@ -137,7 +148,7 @@
 
 // Tests that the service increase the context ref count when a new connection
 // comes in after all previous connections have been released.
-TEST_F(DeviceFactoryProviderConnectorTest,
+TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
        ServiceIncreasesRefCountOnNewConnectionAfterDisconnect) {
   base::RunLoop wait_loop;
   service_impl_->SetShutdownTimeoutCancelledObserver(base::BindRepeating(
@@ -152,7 +163,7 @@
 
 // Tests that the service quits when the last client disconnects while using a
 // device.
-TEST_F(DeviceFactoryProviderConnectorTest,
+TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
        ServiceQuitsWhenClientDisconnectsWhileUsingDevice) {
   mojom::DeviceFactoryPtr factory;
   factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
@@ -201,4 +212,41 @@
   service_destroyed_wait_loop_.Run();
 }
 
+struct NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits {
+  static base::Optional<base::TimeDelta> shutdown_delay() {
+    return base::Optional<base::TimeDelta>();
+  }
+};
+using NoAutomaticShutdownDeviceFactoryProviderConnectorTest =
+    DeviceFactoryProviderConnectorTest<
+        NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits>;
+
+// Tests that the service does not shut down after disconnecting.
+TEST_F(NoAutomaticShutdownDeviceFactoryProviderConnectorTest,
+       ServiceDoesNotShutDownOnDisconnect) {
+  {
+    base::RunLoop wait_loop;
+    service_impl_->SetFactoryProviderClientDisconnectedObserver(
+        wait_loop.QuitClosure());
+    factory_provider_.reset();
+    wait_loop.Run();
+  }
+
+  base::MockCallback<base::OnceClosure> service_impl_destructor_cb;
+  service_impl_->SetDestructionObserver(service_impl_destructor_cb.Get());
+  EXPECT_CALL(service_impl_destructor_cb, Run()).Times(0);
+
+  // Wait for an arbitrary short extra time after which we are convinced that
+  // there is enough evidence that service is not going to shut down.
+  {
+    base::RunLoop wait_loop;
+    base::OneShotTimer wait_timer;
+    wait_timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+                     wait_loop.QuitClosure());
+    wait_loop.Run();
+  }
+
+  service_impl_->SetDestructionObserver(base::DoNothing());
+}
+
 }  // namespace video_capture
diff --git a/docs/es6_chromium.md b/styleguide/web/es6.md
similarity index 100%
rename from docs/es6_chromium.md
rename to styleguide/web/es6.md
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 73e9992..9514f6d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4133,7 +4133,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled2",
                     "enable_features": [
                         "ServiceWorkerServicification"
                     ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 54ca084c2..47d8edb7 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -59,7 +59,6 @@
 crbug.com/728378 compositing/culling/tile-occlusion-boundaries.html [ Failure ]
 crbug.com/591099 compositing/iframes/floating-self-painting-frame.html [ Failure ]
 crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ]
-crbug.com/714962 css3/masking/clip-path-reference-box-inline.html [ Failure ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/float-nowrap-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/floats-line-wrap-shifted-001.html [ Pass ]
@@ -193,8 +192,6 @@
 crbug.com/591099 external/wpt/css/selectors/selector-placeholder-shown-type-change-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/selectors/selector-read-write-type-change-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/selectors/selector-required-type-change-002.html [ Pass ]
-crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-002.html [ Pass ]
-crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-006.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html [ Pass ]
@@ -280,7 +277,7 @@
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-001.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-002.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/path/bearing/zero.svg [ Failure ]
-crbug.com/591099 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
+crbug.com/591099 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Failure Pass ]
 crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html [ Pass ]
 crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_partially_move_down.html [ Failure ]
 crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_tracks.html [ Failure ]
@@ -328,7 +325,7 @@
 crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_outline_shorthand.html [ Failure ]
 crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_white-space_normal_wrapped.html [ Failure ]
 crbug.com/591099 external/wpt/workers/Worker_ErrorEvent_error.htm [ Pass ]
-crbug.com/591099 external/wpt/workers/Worker_terminate_event_queue.htm [ Crash Timeout ]
+crbug.com/591099 external/wpt/workers/Worker_terminate_event_queue.htm [ Timeout ]
 crbug.com/591099 external/wpt/workers/baseurl/alpha/worker-in-worker.html [ Pass ]
 crbug.com/591099 external/wpt/workers/constructors/Worker/same-origin.html [ Timeout ]
 crbug.com/591099 external/wpt/workers/semantics/interface-objects/004.html [ Failure ]
@@ -389,7 +386,6 @@
 crbug.com/591099 fast/writing-mode/fieldsets.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
 crbug.com/591099 fast/writing-mode/table-percent-width-quirk.html [ Pass ]
-crbug.com/591099 hittesting/inline-with-clip-path.html [ Failure ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-center.html [ Failure ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-left.html [ Failure ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-right.html [ Failure ]
@@ -404,7 +400,7 @@
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-effects.html [ Failure ]
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-occlusion.html [ Failure ]
 crbug.com/591099 http/tests/local/fileapi/select-dragged-file-input.html [ Skip ]
-crbug.com/591099 http/tests/misc/object-embedding-svg-delayed-size-negotiation.xhtml [ Failure ]
+crbug.com/591099 http/tests/misc/object-embedding-svg-delayed-size-negotiation.xhtml [ Failure Pass ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ]
 crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
@@ -462,7 +458,7 @@
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
 crbug.com/591099 svg/hixie/error/013.xml [ Failure ]
 crbug.com/591099 svg/transforms/svg-css-transforms.xhtml [ Failure ]
-crbug.com/591099 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure ]
+crbug.com/591099 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure Pass ]
 crbug.com/591099 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ Failure ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Failure ]
 crbug.com/591099 svg/zoom/page/zoom-svg-through-object-with-absolute-size-2.xhtml [ Failure ]
@@ -475,7 +471,7 @@
 crbug.com/591099 tables/mozilla/bugs/bug50695-2.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug55527.html [ Failure ]
 crbug.com/591099 transforms/svg-vs-css.xhtml [ Failure ]
-crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure ]
+crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure Pass ]
 crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
@@ -519,5 +515,5 @@
 crbug.com/591099 virtual/threaded/ [ Skip ]
 crbug.com/591099 virtual/user-activation-v2/fast/events/mouse-cursor.html [ Failure ]
 crbug.com/591099 virtual/user-activation-v2/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
-crbug.com/591099 virtual/video-surface-layer/media/video-layer-crash.html [ Crash Failure Pass ]
-crbug.com/591099 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Failure Pass ]
+crbug.com/591099 virtual/video-surface-layer/media/video-layer-crash.html [ Crash Pass ]
+crbug.com/591099 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
index 2ea7da2..0d4ba7161 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -60,7 +60,6 @@
 Bug(none) vibration/ [ Skip ]
 Bug(none) virtual/cors-rfc1918/ [ Skip ]
 Bug(none) virtual/custom-user-timing/ [ Skip ]
-Bug(none) virtual/enable_asmjs/ [ Skip ]
 Bug(none) virtual/enable_wasm/ [ Skip ]
 Bug(none) virtual/exotic-color-space/ [ Skip ]
 Bug(none) virtual/feature-policy-permissions/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index b95ae80..60f6ebf 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -1,8 +1,6 @@
 # These tests currently fail when run with --enable-features=NetworkSerivce
 # See https://crbug.com/729849
 
-crbug.com/829417 external/wpt/html/browsers/offline/appcache/workers/appcache-worker.https.html [ Timeout ]
-
 Bug(none) http/tests/security/cors-rfc1918 [ Crash Timeout ]
 Bug(none) http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html [ Pass Timeout ]
 Bug(none) virtual/outofblink-cors/http/tests/security/cors-rfc1918 [ Crash Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 4766be0..6f821ef 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -68,7 +68,6 @@
 
 Bug(none) virtual/android/ [ Skip ]
 Bug(none) virtual/blink-gen-property-trees/ [ Skip ]
-Bug(none) virtual/enable_asmjs/ [ Skip ]
 Bug(none) virtual/enable_wasm/ [ Skip ]
 Bug(none) virtual/exotic-color-space/ [ Skip ]
 Bug(none) virtual/gpu/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 3135bd4..09430a9 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -15,6 +15,12 @@
 crbug.com/410974 virtual/scroll_customization/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html [ Leak ]
 crbug.com/410974 virtual/scroll_customization/fast/scroll-behavior/scroll-customization/scrollstate-distribute-to-scroll-chain-descendant.html [ Leak ]
 
+# FIXME: rootscroller/set-root-scroller.html on WebKit Linux Trusty
+crbug.com/891575 rootscroller/set-root-scroller.html [ Leak ]
+crbug.com/891575 virtual/android/rootscroller/remove-rootscroller-crash.html [ Leak Pass ]
+crbug.com/891575 virtual/android/rootscroller/set-root-scroller.html [ Leak ]
+crbug.com/891575 virtual/android/fullscreen/video-overlay-scroll.html [ Leak Pass ]
+
 # -----------------------------------------------------------------
 # Untriaged but known leaks of PausableObject (fast).
 # -----------------------------------------------------------------
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 52ca328..bc5d0c1 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -337,11 +337,6 @@
     "args": ["--enable-features=WebAssembly"]
   },
   {
-    "prefix": "enable_asmjs",
-    "base": "http/tests/asmjs",
-    "args": ["--enable-features=AsmJsToWebAssembly"]
-  },
-  {
     "prefix": "layout_ng",
     "base": "fast/block/basic",
     "args": ["--enable-blink-features=LayoutNG"]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-001.html
new file mode 100644
index 0000000..3436464
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-001.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>clip-path on inline, horizontal-tb writing-mode</title>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-clip-path" title="5.1 Clipping Shape: the clip-path property">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta content="ahem" name="flags">
+<style>
+  .container {
+    writing-mode: horizontal-tb;
+    font: 100px/1 Ahem;
+    line-height: 100px;
+    color: red;
+  }
+  .container::first-letter {
+    color:green;
+  }
+  .container > span {
+    clip-path: polygon(0% 0%, 50% 0%, 50% 100%, 0% 100%);
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="container">
+  <span>
+    XX<br>
+    XXX
+  </span>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-002.html
new file mode 100644
index 0000000..d56117e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-002.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>clip-path on inline, vertical-rl writing-mode</title>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-clip-path" title="5.1 Clipping Shape: the clip-path property">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta content="ahem" name="flags">
+<style>
+  .container {
+    writing-mode: vertical-rl;
+    margin-left: -100px;
+    font: 100px/1 Ahem;
+    line-height: 100px;
+    color: red;
+  }
+  .container::first-letter {
+    color:green;
+  }
+  .container > span {
+    clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 0% 50%);
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="container">
+  <span>
+    XX<br>
+    XXX
+  </span>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-003.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-003.html
new file mode 100644
index 0000000..4c907a46
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-masking/clip-path/clip-path-inline-003.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>clip-path on inline, vertical-lr writing-mode</title>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-clip-path" title="5.1 Clipping Shape: the clip-path property">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<meta content="ahem" name="flags">
+<style>
+  .container {
+    writing-mode: vertical-lr;
+    font: 100px/1 Ahem;
+    line-height: 100px;
+    color: red;
+  }
+  .container::first-letter {
+    color:green;
+  }
+  .container > span {
+    clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 0% 50%);
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="container">
+  <span>
+    XX<br>
+    XXX
+  </span>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/appcache-iframe.https.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/appcache-iframe.https.html
new file mode 100644
index 0000000..8e72664
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/appcache-iframe.https.html
@@ -0,0 +1,37 @@
+<html manifest="resources/appcache-iframe.manifest">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const initPromise = new Promise(resolve => {
+  applicationCache.addEventListener('cached', resolve, false);
+  applicationCache.addEventListener('noupdate', resolve, false);
+});
+
+function with_iframe(url) {
+  return new Promise(resolve => {
+      let frame = document.createElement('iframe');
+      frame.src = url;
+      frame.onload = () => { resolve(frame); };
+      document.body.appendChild(frame);
+      add_result_callback(() => frame.remove());
+    });
+}
+
+promise_test(async t => {
+    await initPromise;
+    const frame =
+        await with_iframe('resources/appcache-iframe.py?type=cached');
+    const msgEvent = await new Promise(r => window.onmessage = r);
+    assert_equals(msgEvent.data, 'Done: cached');
+  }, 'iframe should be loaded from application caches.');
+
+promise_test(async t => {
+    await initPromise;
+    const frame =
+        await with_iframe('resources/appcache-iframe.py?type=fallingback');
+    const msgEvent = await new Promise(r => window.onmessage = r);
+    assert_equals(msgEvent.data, 'Done: fallbacked');
+  }, 'iframe should be loaded from application caches for fallback.');
+
+</script>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-data.py b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-data.py
new file mode 100644
index 0000000..f92c511
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-data.py
@@ -0,0 +1,5 @@
+def main(request, response):
+    type = request.GET['type']
+    if request.GET['type'] == 'fallingback':
+        return 404, [('Content-Type', 'text/plain')], "Page not found"
+    return [('Content-Type', 'text/plain')], type
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.manifest b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.manifest
new file mode 100644
index 0000000..7221e90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.manifest
@@ -0,0 +1,8 @@
+CACHE MANIFEST
+
+appcache-iframe.py?type=cached
+appcache-data.py?type=cached
+
+FALLBACK:
+appcache-iframe.py?type=fallingback appcache-iframe.py?type=fallbacked
+appcache-data.py?type=fallingback appcache-data.py?type=fallbacked
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.py b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.py
new file mode 100644
index 0000000..bc82788
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/resources/appcache-iframe.py
@@ -0,0 +1,43 @@
+script = '''
+<script>
+function fetchCachedFileTest() {
+  return fetch('appcache-data.py?type=cached')
+    .then(res => res.text(),
+          _ => { throw new Error('Failed to fetch cached file'); })
+    .then(text => {
+      if (text != 'cached') {
+        throw new Error('cached file missmatch');
+      }
+    });
+}
+
+function fetchNotInCacheFileTest() {
+  return fetch('appcache-data.py?type=not-in-cache')
+    .then(_ => { throw new Error('Fetching not-in-cache file must fail'); },
+          _ => {});
+}
+
+function fetchFallbackFileTest() {
+  return fetch('appcache-data.py?type=fallingback')
+    .then(res => res.text(),
+          _ => { throw new Error('Failed to fetch fallingback file'); })
+    .then(text => {
+      if (text != 'fallbacked') {
+        throw new Error('fallbacked file miss match');
+      }
+    });
+}
+
+fetchCachedFileTest()
+  .then(fetchNotInCacheFileTest)
+  .then(fetchFallbackFileTest)
+  .then(_ => window.parent.postMessage('Done: %s'),
+        error => window.parent.postMessage(error.toString()));
+</script>
+'''
+
+def main(request, response):
+    type = request.GET['type']
+    if request.GET['type'] == 'fallingback':
+        return 404, [('Content-Type', 'text/plain')], "Page not found"
+    return [('Content-Type', 'text/html')], script % type
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/workers/resources/appcache-worker.py b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/workers/resources/appcache-worker.py
index 00281a2b..10643219 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/workers/resources/appcache-worker.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/offline/appcache/workers/resources/appcache-worker.py
@@ -91,6 +91,7 @@
   .then(importFallbackScriptTest)
   .then(fetchCachedFileTest)
   .then(fetchNotInCacheFileTest)
+  .then(fetchFallbackFileTest)
   .then(_ => postMessage('Done: %s'),
         error => postMessage(error.toString()));
 '''
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicStream.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicStream.https.html
index 33025451..68c88e27 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicStream.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicStream.https.html
@@ -13,13 +13,14 @@
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
 //   makeStandaloneQuicTransport
+//   makeTwoConnectedQuicTransports
 
 promise_test(async t => {
-  const quicTransport = await makeStandaloneQuicTransport(t);
+  const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
   const quicStream = quicTransport.createStream();
   assert_equals(quicStream.transport, quicTransport,
       'Expect transport to be set to the creating RTCQuicTransport.');
-  assert_equals(quicStream.state, 'new', `Expect state to be 'new'.`);
+  assert_equals(quicStream.state, 'open', `Expect state to be 'open'.`);
   assert_equals(quicStream.readBufferedAmount, 0,
       'Expect read buffered amount to be 0.');
   assert_equals(quicStream.writeBufferedAmount, 0,
@@ -28,17 +29,130 @@
 
 promise_test(async t => {
   const quicTransport = await makeStandaloneQuicTransport(t);
+  assert_throws('InvalidStateError', () => quicTransport.createStream());
+}, 'createStream() throws if the transport is not connected.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_throws('InvalidStateError', () => quicTransport.createStream());
 }, 'createStream() throws if the transport is closed.');
 
 promise_test(async t => {
-  const quicTransport = await makeStandaloneQuicTransport(t);
+  const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
   const firstQuicStream = quicTransport.createStream();
   const secondQuicStream = quicTransport.createStream();
   quicTransport.stop();
   assert_equals(firstQuicStream.state, 'closed');
   assert_equals(secondQuicStream.state, 'closed');
-}, 'RTCQuicTransport.stop() closes all streams.');
+}, 'RTCQuicTransport.stop() closes all local streams.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const firstLocalStream = localQuicTransport.createStream();
+  firstLocalStream.finish();
+  const secondLocalStream = localQuicTransport.createStream();
+  secondLocalStream.finish();
+  const remoteWatcher =
+      new EventWatcher(t, remoteQuicTransport, [ 'quicstream', 'statechange' ]);
+  const { stream: firstRemoteStream } =
+      await remoteWatcher.wait_for('quicstream');
+  const { stream: secondRemoteStream } =
+      await remoteWatcher.wait_for('quicstream');
+  localQuicTransport.stop();
+  await remoteWatcher.wait_for('statechange');
+  assert_equals(firstRemoteStream.state, 'closed');
+  assert_equals(secondRemoteStream.state, 'closed');
+}, 'RTCQuicTransport.stop() closes all remote streams.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  assert_equals(localStream.state, 'closing');
+}, `finish() changes state to 'closing'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.finish();
+  assert_equals(localStream.state, 'closing');
+}, `finish() twice does not change state.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.reset();
+  assert_equals(localStream.state, 'closed');
+}, `reset() changes state to 'closed'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.reset();
+  assert_equals(localStream.state, 'closed');
+}, `reset() following finish() changes state to 'closed'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  assert_equals(remoteStream.state, 'open');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closing');
+}, 'createStream() followed by finish() fires a quicstream event followed by ' +
+    `a statechange event to 'closing' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.reset();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  assert_equals(remoteStream.state, 'open');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+}, 'createStream() followed by reset() fires a quicstream event followed ' +
+    `by a statechange event to 'closed' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  remoteQuicTransport.onquicstream = ({ stream }) => stream.reset();
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const localWatcher = new EventWatcher(t, localStream, 'statechange');
+  await localWatcher.wait_for('statechange');
+  assert_equals(localStream.state, 'closed');
+}, 'finish() on a remote stream that has already finished fires a ' +
+    `statechange event to 'closed' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.reset();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closing');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+}, 'finish() then reset() fires two statechange events on the remote side.');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport-helper.js b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport-helper.js
index 3ea19d7..7e28fea 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport-helper.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport-helper.js
@@ -79,4 +79,3 @@
   ]);
   return [ localQuicTransport, remoteQuicTransport ];
 }
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport.https.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport.https.html
index ec79bc22..3bcc93d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCQuicTransport.https.html
@@ -17,6 +17,7 @@
 //   makeAndGatherTwoIceTransports
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
+//   generateCertificate
 //   makeQuicTransport
 //   makeStandaloneQuicTransport
 //   makeAndStartTwoQuicTransports
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/no-hover-during-scroll.html b/third_party/WebKit/LayoutTests/fast/scrolling/no-hover-during-scroll.html
new file mode 100644
index 0000000..da0083a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/scrolling/no-hover-during-scroll.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../../resources/gesture-util.js'></script>
+
+<style>
+  body, html {
+    margin: 0;
+  }
+  div {
+    height: 50px;
+    width: 100%;
+  }
+
+  .hoverme {
+    background-color: rgb(0, 0, 255);
+  }
+
+  .hoverme:hover {
+    background-color: rgb(255, 255, 0);
+  }
+
+  .message {
+    width: 100%;
+    text-align: left;
+  }
+</style>
+
+<div class="message">
+  First move your mouse cursor to the page, you will see the text under the mouse cursor changed to "currently hovered". <br>
+  Scroll mouse wheel slowly, you should not see any text under the mouse changed to "currently hovered" while the scrolling is in process and finishes.
+</div>
+
+<script>
+  let array;
+  const numHoverElements = 30;
+  const elementHeight = 50;
+  const textWhenNotHovered = "hover over me";
+  const textWhenHovered = "currently hovered";
+  const textWhenWasHovered = "was hovered";
+  const mouse = GestureSourceType.MOUSE_INPUT;
+
+  function buildPage() {
+    for (let i = 0; i < numHoverElements; i++) {
+      let div = document.createElement('div');
+      div.className = "hoverme";
+      div.innerHTML = textWhenNotHovered;
+      document.body.appendChild(div);
+    }
+
+    array = document.getElementsByClassName('hoverme');
+
+    for (let element of array) {
+      element.addEventListener('mouseover', function (e) {
+        this.innerHTML = textWhenHovered;
+      });
+      element.addEventListener('mouseout', function (e) {
+        this.innerHTML = textWhenWasHovered;
+      });
+    }
+  }
+
+  window.onload = async () => {
+    if (window.internals) {
+      internals.settings.setScrollAnimatorEnabled(false);
+      internals.runtimeFlags.noHoverDuringScrollEnabled = true;
+    }
+
+    buildPage();
+    await waitForCompositorCommit();
+
+    promise_test(async () => {
+      let x = array[0].offsetLeft + 10;
+      let y = array[0].offsetTop + 10;
+      // Move cursor to 1st element.
+      await mouseMoveTo(x, y);
+      await waitFor( () => { return array[0].innerHTML == textWhenHovered;}, 'wait for moveto 1st element');
+      assert_equals(array[0].innerHTML, textWhenHovered);
+      assert_equals(array[1].innerHTML, textWhenNotHovered);
+      assert_equals(getComputedStyle(array[0]).backgroundColor, 'rgb(255, 255, 0)');
+
+      // Scroll end up at 4th element. Hover state does not update during scrolling
+      // so that 2nd, 3rd and 4th elements do not see the mouseover and mouseout events.
+      assert_equals(document.scrollingElement.scrollTop, 0);
+      await smoothScroll(3 * elementHeight, x, y, mouse, 'down', SPEED_INSTANT);
+      // Wait enough time to see if we fire a fake mouse move event to update the hover state. 
+      await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; }, 200, 100);
+      assert_approx_equals(document.scrollingElement.scrollTop, 3 * elementHeight, 10);
+      assert_equals(array[0].innerHTML, textWhenHovered);
+      assert_equals(array[1].innerHTML, textWhenNotHovered);
+      assert_equals(array[2].innerHTML, textWhenNotHovered);
+      assert_equals(array[3].innerHTML, textWhenNotHovered);
+      assert_equals(array[4].innerHTML, textWhenNotHovered);
+      assert_equals(getComputedStyle(array[0]).backgroundColor, 'rgb(255, 255, 0)');
+      assert_equals(getComputedStyle(array[1]).backgroundColor, 'rgb(0, 0, 255)');
+      assert_equals(getComputedStyle(array[2]).backgroundColor, 'rgb(0, 0, 255)');
+      assert_equals(getComputedStyle(array[3]).backgroundColor, 'rgb(0, 0, 255)');
+    }, 'Mouse wheel scroll on the page, no hover update during scrolling.');
+  }
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/resources/xmlhttprequest-no-file-access-real.html b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/resources/xmlhttprequest-no-file-access-real.html
index 3cd09ab..48a9d80 100644
--- a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/resources/xmlhttprequest-no-file-access-real.html
+++ b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/resources/xmlhttprequest-no-file-access-real.html
@@ -3,43 +3,37 @@
 <html>
 <head>
     <script>
-        function log(message)
-        {
-            var console = document.getElementById('console');
-            console.appendChild(document.createTextNode(message));
-            console.appendChild(document.createElement('br'));
+        function logFailure(message) {
+          window.top.postMessage(message, "*");
         }
-
+        function done() {
+          window.top.postMessage("DONE", "*");
+        }
         function testXHRDenied()
         {
-            log("Checking that same-origin iframes work.");
             var f = document.getElementById("f");
+            // Check that access to an empty iframe allowed.
             f.contentDocument.body.innerHTML = "Successful write into iframe";
-            log("Doing an XHR to an existing file.");
             xhr = new XMLHttpRequest();
 
-            try {
-                xhr.open("GET", "../xmlhttprequest-no-file-access-expected.txt", false);
-                xhr.send("");
-                log("Bad: XHR didn't throw exception");
-            } catch(e) {
-                log("Exception: " + e.message);
-                try {
-                    var results = window.top.document.getElementById('results');
-                    log("Bad: DOM access didn't throw exception");
-                } catch (e) {
-                    log("Exception: " + e.message);
-                    if (window.testRunner) {
-                        setTimeout("testRunner.notifyDone()", 0);
-                    }
-                }
-            }
+            xhr.open("GET", "../xmlhttprequest-no-file-access-expected.txt");
+            xhr.onload = () => {
+              logFailure("Bad: XHR didn't throw exception");
+              done();
+            };
+            xhr.onerror = (e) => {
+                 try {
+                    window.top.document.body;
+                    logFailure("Bad: DOM access didn't throw exception");
+                 } finally {
+                    done();
+                 }
+            };
+            xhr.send("");
         }
         </script>
     </head>
     <body onload="testXHRDenied()">
     <iframe id="f"></iframe>
-        <p> We're checking we can't read an arbitrary file when we set each file:// URI to have a unique domain. </p>
-        <div id="console"/>
     </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access-expected.txt b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access-expected.txt
deleted file mode 100644
index 8214650e..0000000
--- a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CONSOLE WARNING: line 22: 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/.
-CONSOLE ERROR: line 23: Access to XMLHttpRequest at 'xmlhttprequest-no-file-access-expected.txt' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, https.
-
-The child iframe cannot paste its textual results into this iframe because it is considered a different domain - that's the point of this test! Therefore, success is denoted by the child iframe calling notifyDone. The test will hang if something goes amiss with the access control checks.
diff --git a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access.html b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access.html
index 2ef75dd..40a44e8 100644
--- a/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access.html
+++ b/third_party/WebKit/LayoutTests/fast/xmlhttprequest/xmlhttprequest-no-file-access.html
@@ -1,20 +1,26 @@
 <html>
 <head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
 if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
     testRunner.setAllowUniversalAccessFromFileURLs(false);
     testRunner.setAllowFileAccessFromFileURLs(false);
 }
 </script>
 </head>
 <body>
+<script>
+async_test((test) => {
+  window.onmessage = (e) => {
+    if (e.data === 'DONE') {
+      test.done();
+      return;
+    }
+    assert_unreached(e.data);
+  };
+}, 'Test if file: origin is treate as (virtually) opaque.');
+</script>
 <iframe src="resources/xmlhttprequest-no-file-access-real.html"></iframe>
-<div id="results"></div>
-The child iframe cannot paste its textual results into this iframe because it
-is considered a different domain - that's the point of this test!
-Therefore, success is denoted by the child iframe calling notifyDone.
-The test will hang if something goes amiss with the access control checks.
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/asm-warnings-expected.txt b/third_party/WebKit/LayoutTests/http/tests/asmjs/asm-warnings-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/asm-warnings-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/asmjs/asm-warnings-expected.txt
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/asm-warnings.html b/third_party/WebKit/LayoutTests/http/tests/asmjs/asm-warnings.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/asm-warnings.html
rename to third_party/WebKit/LayoutTests/http/tests/asmjs/asm-warnings.html
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/worker.js b/third_party/WebKit/LayoutTests/http/tests/asmjs/worker.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/worker.js
rename to third_party/WebKit/LayoutTests/http/tests/asmjs/worker.js
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/README.txt b/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/README.txt
deleted file mode 100644
index ea39d27d..0000000
--- a/third_party/WebKit/LayoutTests/virtual/enable_asmjs/http/tests/asmjs/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests that depend on --enable-features=AsmJsToWebAssembly
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 f19508d..d2d04ae 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5569,16 +5569,25 @@
     attribute @@toStringTag
     getter candidate
     method constructor
-interface RTCQuicStream
+interface RTCQuicStream : EventTarget
     attribute @@toStringTag
+    getter onstatechange
     getter readBufferedAmount
     getter state
     getter transport
     getter writeBufferedAmount
     method constructor
+    method finish
+    method reset
+    setter onstatechange
+interface RTCQuicStreamEvent : Event
+    attribute @@toStringTag
+    getter stream
+    method constructor
 interface RTCQuicTransport : EventTarget
     attribute @@toStringTag
     getter onerror
+    getter onquicstream
     getter onstatechange
     getter state
     getter transport
@@ -5591,6 +5600,7 @@
     method start
     method stop
     setter onerror
+    setter onquicstream
     setter onstatechange
 interface RTCRtpContributingSource
     attribute @@toStringTag
diff --git a/third_party/WebKit/Tools/Scripts/run-webkit-tests b/third_party/WebKit/Tools/Scripts/run-webkit-tests
index 7da86061..112dc99 100755
--- a/third_party/WebKit/Tools/Scripts/run-webkit-tests
+++ b/third_party/WebKit/Tools/Scripts/run-webkit-tests
@@ -29,8 +29,7 @@
 
 """Wrapper around webkitpy/layout_tests/run_webkit_tests.py"""
 
-from webkitpy.common import add_blinkpy  # pylint: disable=unused-import
-from blinkpy.common import multiprocessing_bootstrap
+import sys
 
 print '\n    Please use third_party/blink/tools/run_web_tests.*.  This command will be removed.\n'
-multiprocessing_bootstrap.run('blinkpy', 'web_tests', 'run_webkit_tests.py')
+sys.exit(1)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/__init__.py b/third_party/WebKit/Tools/Scripts/webkitpy/__init__.py
deleted file mode 100644
index b376bf2b..0000000
--- a/third_party/WebKit/Tools/Scripts/webkitpy/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Required for Python to search this directory for module files
-
-# Keep this file free of any code or import statements that could
-# cause either an error to occur or a log message to be logged.
-# This ensures that calling code can import initialization code from
-# webkitpy before any errors or log messages due to code in this file.
-# Initialization code can include things like version-checking code and
-# logging configuration code.
-#
-# We do not execute any version-checking code or logging configuration
-# code in this file so that callers can opt-in as they want.  This also
-# allows different callers to choose different initialization code,
-# as necessary.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/__init__.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/__init__.py
deleted file mode 100644
index ef65bee5..0000000
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/add_blinkpy.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/add_blinkpy.py
deleted file mode 100644
index f6615881..0000000
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/add_blinkpy.py
+++ /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.
-
-"""Add the path of blinkpy to sys.path.
-
-You don't need to call a function to do it. You need just import this. e.g.
-    import webkitpy.common.add_blinkpy  # pylint: disable=unused-import
-    or
-    from webkitpy.common import add_blinkpy  # pylint: disable=unused-import
-
-This is a transitional solution to handle both of blinkpy and webkitpy. We'll
-remove this file when we finish moving webkitpy to blinkpy.
-"""
-
-import os
-import sys
-
-# Without abspath(), PathFinder can't find chromium_base correctly.
-sys.path.append(os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..',
-                 'blink', 'tools')))
diff --git a/third_party/blink/public/platform/web_localized_string.h b/third_party/blink/public/platform/web_localized_string.h
index bd19f6f..8be42ff 100644
--- a/third_party/blink/public/platform/web_localized_string.h
+++ b/third_party/blink/public/platform/web_localized_string.h
@@ -47,43 +47,30 @@
     kAXMediaAudioElementHelp,
     kAXMediaAudioSliderHelp,
     kAXMediaCastOffButton,
-    kAXMediaCastOffButtonHelp,
     kAXMediaCastOnButton,
-    kAXMediaCastOnButtonHelp,
     kAXMediaCurrentTimeDisplay,
     kAXMediaCurrentTimeDisplayHelp,
     kAXMediaDefault,
     kAXMediaDownloadButton,
     kAXMediaEnterFullscreenButton,
-    kAXMediaEnterFullscreenButtonHelp,
     kAXMediaExitFullscreenButton,
-    kAXMediaExitFullscreenButtonHelp,
     kAXMediaHideClosedCaptionsButton,
-    kAXMediaHideClosedCaptionsButtonHelp,
     kAXMediaMuteButton,
-    kAXMediaMuteButtonHelp,
     kAXMediaDisplayCutoutFullscreenButton,
-    kAXMediaDisplayCutoutFullscreenButtonHelp,
     kAXMediaOverflowButton,
     kAXMediaOverflowButtonHelp,
     kAXMediaPauseButton,
-    kAXMediaPauseButtonHelp,
     kAXMediaPlayButton,
-    kAXMediaPlayButtonHelp,
     kAXMediaShowClosedCaptionsButton,
-    kAXMediaShowClosedCaptionsButtonHelp,
     kAXMediaTimeRemainingDisplay,
     kAXMediaTimeRemainingDisplayHelp,
     kAXMediaUnMuteButton,
-    kAXMediaUnMuteButtonHelp,
     kAXMediaVideoElement,
     kAXMediaVideoElementHelp,
     kAXMediaVideoSliderHelp,
     kAXMediaVolumeSliderHelp,
     kAXMediaEnterPictureInPictureButton,
-    kAXMediaEnterPictureInPictureButtonHelp,
     kAXMediaExitPictureInPictureButton,
-    kAXMediaExitPictureInPictureButtonHelp,
     kAXMillisecondFieldText,
     kAXMinuteFieldText,
     kAXMonthFieldText,
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index c8499d1..ad7c5f2 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -113,6 +113,7 @@
   BLINK_PLATFORM_EXPORT static void EnableNetInfoDownlinkMax(bool);
   BLINK_PLATFORM_EXPORT static void EnableNetworkService(bool);
   BLINK_PLATFORM_EXPORT static void EnableNoHoverAfterLayoutChange(bool);
+  BLINK_PLATFORM_EXPORT static void EnableNoHoverDuringScroll(bool);
   BLINK_PLATFORM_EXPORT static void EnableNotificationConstructor(bool);
   BLINK_PLATFORM_EXPORT static void EnableNotificationContentImage(bool);
   BLINK_PLATFORM_EXPORT static void EnableNotifications(bool);
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 9d29ab05..79271999 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -234,8 +234,7 @@
   // TODO(dgozman): rename to CommitHTMLStringNavigation.
   virtual void LoadHTMLString(const WebData& html,
                               const WebURL& base_url,
-                              const WebURL& unreachable_url = WebURL(),
-                              bool replace = false) = 0;
+                              const WebURL& unreachable_url = WebURL()) = 0;
 
   // Navigates to the given |data| with specified |mime_type| and optional
   // |text_encoding|.
@@ -254,7 +253,6 @@
       const WebString& mime_type,
       const WebString& text_encoding,
       const WebURL& unreachable_url,
-      bool replace,
       WebFrameLoadType,
       const WebHistoryItem&,
       bool is_client_redirect,
diff --git a/third_party/blink/public/web/web_serialized_script_value_version.h b/third_party/blink/public/web/web_serialized_script_value_version.h
index 08b2bd0..176ecef7 100644
--- a/third_party/blink/public/web/web_serialized_script_value_version.h
+++ b/third_party/blink/public/web/web_serialized_script_value_version.h
@@ -38,7 +38,7 @@
 // Embedders may serialize this as out-of-band metadata along with
 // collections of serialized data so that version skew can be detected
 // before deserializing individual values.
-const unsigned kSerializedScriptValueVersion = 18;
+const unsigned kSerializedScriptValueVersion = 19;
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
index adb208c..33d2b21f 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
@@ -93,7 +93,15 @@
   //                     namedCurve:uint32_t
   kRTCCertificateTag = 'k',  // length:uint32_t, pemPrivateKey:WebCoreString,
                              // pemCertificate:WebCoreString
-  kVersionTag = 0xFF  // version:uint32_t -> Uses this as the file version.
+  kDetectedBarcodeTag =
+      'B',  // raw_value:WebCoreString, bounding_box:DOMRectReadOnly,
+            // corner_points:Point2D[length] -> DetectedBarcode (ref)
+  kDetectedFaceTag =
+      'F',  // raw_value:WebCoreString, bounding_box:DOMRectReadOnly,
+            // corner_points:Point2D[length] -> DetectedText (ref)
+  kDetectedTextTag = 't',  // bounding_box:DOMRectReadOnly,
+                           // landmarks:Landmark[length] -> DetectedFace (ref)
+  kVersionTag = 0xFF       // version:uint32_t -> Uses this as the file version.
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
index 5dc7bbf..68ebafe5 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
@@ -88,6 +88,7 @@
   // Version 17: Remove unnecessary byte swapping.
   // Version 18: Add a list of key-value pairs for ImageBitmap and ImageData to
   //             support color space information, compression, etc.
+  // Version 19: Add DetectedBarcode, DetectedFace, and DetectedText support.
   //
   // The following versions cannot be used, in order to be able to
   // deserialize version 0 SSVs. The class implementation has details.
@@ -100,7 +101,7 @@
   //
   // Recent changes are routinely reverted in preparation for branch, and this
   // has been the cause of at least one bug in the past.
-  static constexpr uint32_t kWireFormatVersion = 18;
+  static constexpr uint32_t kWireFormatVersion = 19;
 
   // This enumeration specifies whether we're serializing a value for storage;
   // e.g. when writing to IndexedDB. This corresponds to the forStorage flag of
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index a7f18c5..298cc58e 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -419,11 +419,7 @@
       return DOMRect::Create(x, y, width, height);
     }
     case kDOMRectReadOnlyTag: {
-      double x = 0, y = 0, width = 0, height = 0;
-      if (!ReadDouble(&x) || !ReadDouble(&y) || !ReadDouble(&width) ||
-          !ReadDouble(&height))
-        return nullptr;
-      return DOMRectReadOnly::Create(x, y, width, height);
+      return ReadDOMRectReadOnly();
     }
     case kDOMQuadTag: {
       DOMPointInit pointInits[4];
@@ -560,6 +556,14 @@
                                               blob_handle);
 }
 
+DOMRectReadOnly* V8ScriptValueDeserializer::ReadDOMRectReadOnly() {
+  double x = 0, y = 0, width = 0, height = 0;
+  if (!ReadDouble(&x) || !ReadDouble(&y) || !ReadDouble(&width) ||
+      !ReadDouble(&height))
+    return nullptr;
+  return DOMRectReadOnly::Create(x, y, width, height);
+}
+
 scoped_refptr<BlobDataHandle>
 V8ScriptValueDeserializer::GetOrCreateBlobDataHandle(const String& uuid,
                                                      const String& type,
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
index 3582fcb..c020791 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
@@ -17,6 +17,7 @@
 
 namespace blink {
 
+class DOMRectReadOnly;
 class File;
 class UnpackedSerializedScriptValue;
 
@@ -65,6 +66,7 @@
     return deserializer_.ReadRawBytes(size, data);
   }
   bool ReadUTF8String(String* string_out);
+  DOMRectReadOnly* ReadDOMRectReadOnly();
 
   template <typename E>
   bool ReadUint32Enum(E* value) {
diff --git a/third_party/blink/renderer/bindings/modules/BUILD.gn b/third_party/blink/renderer/bindings/modules/BUILD.gn
index b242546..e35eaba33 100644
--- a/third_party/blink/renderer/bindings/modules/BUILD.gn
+++ b/third_party/blink/renderer/bindings/modules/BUILD.gn
@@ -41,6 +41,7 @@
     "//third_party/blink/renderer/modules/peerconnection/rtc_data_channel_event.idl",
     "//third_party/blink/renderer/modules/peerconnection/rtc_dtmf_tone_change_event.idl",
     "//third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event.idl",
+    "//third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.idl",
     "//third_party/blink/renderer/modules/picture_in_picture/enter_picture_in_picture_event.idl",
     "//third_party/blink/renderer/modules/presentation/presentation_connection_available_event.idl",
     "//third_party/blink/renderer/modules/presentation/presentation_connection_close_event.idl",
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index 10e3e7af..1609823 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -14,6 +14,9 @@
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
+#include "third_party/blink/renderer/modules/shapedetection/detected_barcode.h"
+#include "third_party/blink/renderer/modules/shapedetection/detected_face.h"
+#include "third_party/blink/renderer/modules/shapedetection/detected_text.h"
 
 namespace blink {
 
@@ -57,6 +60,60 @@
         return nullptr;
       return new RTCCertificate(std::move(certificate));
     }
+    case kDetectedBarcodeTag: {
+      String raw_value;
+      if (!ReadUTF8String(&raw_value))
+        return nullptr;
+      DOMRectReadOnly* bounding_box = ReadDOMRectReadOnly();
+      if (!bounding_box)
+        return nullptr;
+      uint32_t corner_points_length;
+      if (!ReadUint32(&corner_points_length))
+        return nullptr;
+      HeapVector<Point2D> corner_points;
+      for (uint32_t i = 0; i < corner_points_length; i++) {
+        Point2D point;
+        if (!ReadPoint2D(&point))
+          return nullptr;
+        corner_points.push_back(point);
+      }
+      return DetectedBarcode::Create(raw_value, bounding_box, corner_points);
+    }
+    case kDetectedFaceTag: {
+      DOMRectReadOnly* bounding_box = ReadDOMRectReadOnly();
+      if (!bounding_box)
+        return nullptr;
+      uint32_t landmarks_length;
+      if (!ReadUint32(&landmarks_length))
+        return nullptr;
+      HeapVector<Landmark> landmarks;
+      for (uint32_t i = 0; i < landmarks_length; i++) {
+        Landmark landmark;
+        if (!ReadLandmark(&landmark))
+          return nullptr;
+        landmarks.push_back(landmark);
+      }
+      return DetectedFace::Create(bounding_box, landmarks);
+    }
+    case kDetectedTextTag: {
+      String raw_value;
+      if (!ReadUTF8String(&raw_value))
+        return nullptr;
+      DOMRectReadOnly* bounding_box = ReadDOMRectReadOnly();
+      if (!bounding_box)
+        return nullptr;
+      uint32_t corner_points_length;
+      if (!ReadUint32(&corner_points_length))
+        return nullptr;
+      HeapVector<Point2D> corner_points;
+      for (uint32_t i = 0; i < corner_points_length; i++) {
+        Point2D point;
+        if (!ReadPoint2D(&point))
+          return nullptr;
+        corner_points.push_back(point);
+      }
+      return DetectedText::Create(raw_value, bounding_box, corner_points);
+    }
     default:
       break;
   }
@@ -291,4 +348,32 @@
   return CryptoKey::Create(key);
 }
 
+bool V8ScriptValueDeserializerForModules::ReadLandmark(Landmark* landmark) {
+  String type;
+  if (!ReadUTF8String(&type))
+    return false;
+  uint32_t locations_length;
+  if (!ReadUint32(&locations_length))
+    return false;
+  HeapVector<Point2D> locations;
+  for (uint32_t i = 0; i < locations_length; i++) {
+    Point2D location;
+    if (!ReadPoint2D(&location))
+      return false;
+    locations.push_back(location);
+  }
+  landmark->setType(type);
+  landmark->setLocations(locations);
+  return true;
+}
+
+bool V8ScriptValueDeserializerForModules::ReadPoint2D(Point2D* point) {
+  double x = 0, y = 0;
+  if (!ReadDouble(&x) || !ReadDouble(&y))
+    return false;
+  point->setX(x);
+  point->setY(y);
+  return true;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
index a120c08..eab1e1f 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
@@ -11,6 +11,8 @@
 namespace blink {
 
 class CryptoKey;
+class Landmark;
+class Point2D;
 
 // Extends V8ScriptValueSerializer with support for modules/ types.
 class MODULES_EXPORT V8ScriptValueDeserializerForModules final
@@ -42,6 +44,8 @@
     return true;
   }
   CryptoKey* ReadCryptoKey();
+  bool ReadLandmark(Landmark* landmark);
+  bool ReadPoint2D(Point2D* point);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index 49bf4c3..00efd23 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -8,8 +8,12 @@
 #include "third_party/blink/public/platform/web_crypto.h"
 #include "third_party/blink/public/platform/web_crypto_key.h"
 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h"
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_crypto_key.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_barcode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_face.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_text.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -54,6 +58,61 @@
     WriteUTF8String(pem.certificate().c_str());
     return true;
   }
+  if (wrapper_type_info == &V8DetectedBarcode::wrapperTypeInfo) {
+    DetectedBarcode* detected_barcode = wrappable->ToImpl<DetectedBarcode>();
+    WriteTag(kDetectedBarcodeTag);
+    WriteUTF8String(detected_barcode->rawValue());
+    DOMRectReadOnly* bounding_box = detected_barcode->boundingBox();
+    WriteDouble(bounding_box->x());
+    WriteDouble(bounding_box->y());
+    WriteDouble(bounding_box->width());
+    WriteDouble(bounding_box->height());
+    const HeapVector<Point2D>& corner_points = detected_barcode->cornerPoints();
+    WriteUint32(static_cast<uint32_t>(corner_points.size()));
+    for (const auto& corner_point : corner_points) {
+      WriteDouble(corner_point.x());
+      WriteDouble(corner_point.y());
+    }
+    return true;
+  }
+  if (wrapper_type_info == &V8DetectedFace::wrapperTypeInfo) {
+    DetectedFace* detected_face = wrappable->ToImpl<DetectedFace>();
+    WriteTag(kDetectedFaceTag);
+    DOMRectReadOnly* bounding_box = detected_face->boundingBox();
+    WriteDouble(bounding_box->x());
+    WriteDouble(bounding_box->y());
+    WriteDouble(bounding_box->width());
+    WriteDouble(bounding_box->height());
+    const HeapVector<Landmark>& landmarks = detected_face->landmarks();
+    WriteUint32(static_cast<uint32_t>(landmarks.size()));
+    for (const auto& landmark : landmarks) {
+      WriteUTF8String(landmark.type());
+      const HeapVector<Point2D>& locations = landmark.locations();
+      WriteUint32(static_cast<uint32_t>(locations.size()));
+      for (const auto& location : locations) {
+        WriteDouble(location.x());
+        WriteDouble(location.y());
+      }
+    }
+    return true;
+  }
+  if (wrapper_type_info == &V8DetectedText::wrapperTypeInfo) {
+    DetectedText* detected_text = wrappable->ToImpl<DetectedText>();
+    WriteTag(kDetectedTextTag);
+    WriteUTF8String(detected_text->rawValue());
+    DOMRectReadOnly* bounding_box = detected_text->boundingBox();
+    WriteDouble(bounding_box->x());
+    WriteDouble(bounding_box->y());
+    WriteDouble(bounding_box->width());
+    WriteDouble(bounding_box->height());
+    const HeapVector<Point2D>& corner_points = detected_text->cornerPoints();
+    WriteUint32(static_cast<uint32_t>(corner_points.size()));
+    for (const auto& corner_point : corner_points) {
+      WriteDouble(corner_point.x());
+      WriteDouble(corner_point.y());
+    }
+    return true;
+  }
   return false;
 }
 
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 6fd7f32..8f5ffd39 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -17,8 +17,12 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h"
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_crypto_key.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_barcode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_face.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_detected_text.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
@@ -972,5 +976,83 @@
           ->IsNull());
 }
 
+TEST(V8ScriptValueSerializerForModulesTest, DecodeDetectedBarcode) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  scoped_refptr<SerializedScriptValue> input = SerializedValue(
+      {0xff, 0x13, 0xff, 0x0d, 0x5c, 'B',  0x04, 0x74, 0x65, 0x78, 0x74, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40});
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializerForModules(script_state, input).Deserialize();
+  ASSERT_TRUE(V8DetectedBarcode::hasInstance(result, scope.GetIsolate()));
+  DetectedBarcode* detected_barcode =
+      V8DetectedBarcode::ToImpl(result.As<v8::Object>());
+  EXPECT_EQ("text", detected_barcode->rawValue());
+  DOMRectReadOnly* bounding_box = detected_barcode->boundingBox();
+  EXPECT_EQ(1, bounding_box->x());
+  EXPECT_EQ(2, bounding_box->y());
+  EXPECT_EQ(3, bounding_box->width());
+  EXPECT_EQ(4, bounding_box->height());
+  const HeapVector<Point2D>& corner_points = detected_barcode->cornerPoints();
+  EXPECT_EQ(1u, corner_points.size());
+  EXPECT_EQ(1, corner_points[0].x());
+  EXPECT_EQ(2, corner_points[0].y());
+}
+
+TEST(V8ScriptValueSerializerForModulesTest, DecodeDetectedFace) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  scoped_refptr<SerializedScriptValue> input = SerializedValue(
+      {0xff, 0x13, 0xff, 0x0d, 0x5c, 'F',  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x10, 0x40, 0x01, 0x03, 0x65, 0x79, 0x65, 0x01, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40});
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializerForModules(script_state, input).Deserialize();
+  ASSERT_TRUE(V8DetectedFace::hasInstance(result, scope.GetIsolate()));
+  DetectedFace* detected_face = V8DetectedFace::ToImpl(result.As<v8::Object>());
+  DOMRectReadOnly* bounding_box = detected_face->boundingBox();
+  EXPECT_EQ(1, bounding_box->x());
+  EXPECT_EQ(2, bounding_box->y());
+  EXPECT_EQ(3, bounding_box->width());
+  EXPECT_EQ(4, bounding_box->height());
+  const HeapVector<Landmark>& landmarks = detected_face->landmarks();
+  EXPECT_EQ(1u, landmarks.size());
+  EXPECT_EQ("eye", landmarks[0].type());
+  const HeapVector<Point2D>& locations = landmarks[0].locations();
+  EXPECT_EQ(1u, locations.size());
+  EXPECT_EQ(1, locations[0].x());
+  EXPECT_EQ(2, locations[0].y());
+}
+
+TEST(V8ScriptValueSerializerForModulesTest, DecodeDetectedText) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  scoped_refptr<SerializedScriptValue> input = SerializedValue(
+      {0xff, 0x13, 0xff, 0x0d, 0x5c, 't',  0x04, 0x74, 0x65, 0x78, 0x74, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40});
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializerForModules(script_state, input).Deserialize();
+  ASSERT_TRUE(V8DetectedText::hasInstance(result, scope.GetIsolate()));
+  DetectedText* detected_text = V8DetectedText::ToImpl(result.As<v8::Object>());
+  EXPECT_EQ("text", detected_text->rawValue());
+  DOMRectReadOnly* bounding_box = detected_text->boundingBox();
+  EXPECT_EQ(1, bounding_box->x());
+  EXPECT_EQ(2, bounding_box->y());
+  EXPECT_EQ(3, bounding_box->width());
+  EXPECT_EQ(4, bounding_box->height());
+  const HeapVector<Point2D>& corner_points = detected_text->cornerPoints();
+  EXPECT_EQ(1u, corner_points.size());
+  EXPECT_EQ(1, corner_points[0].x());
+  EXPECT_EQ(2, corner_points[0].y());
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_style_declaration.idl b/third_party/blink/renderer/core/css/css_style_declaration.idl
index 7f5b13d..03e8b07 100644
--- a/third_party/blink/renderer/core/css/css_style_declaration.idl
+++ b/third_party/blink/renderer/core/css/css_style_declaration.idl
@@ -39,7 +39,5 @@
     // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-camel-cased-attribute
     // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute
     [Affects=Nothing] getter DOMString (DOMString name);
-    // TODO(crbug.com/831544): [TreatNullAs=EmptyString] should be used instead
-    // of [TreatNullAs=NullString].
-    [CEReactions, CallWith=ScriptState] setter void (DOMString property, [TreatNullAs=NullString] DOMString propertyValue);
+    [CEReactions, CallWith=ScriptState] setter void (DOMString property, [TreatNullAs=EmptyString] DOMString propertyValue);
 };
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 7325011..1a63d54 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -172,7 +172,7 @@
 ColorSpaceGamut MediaValues::CalculateColorGamut(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetPage());
-  return ColorSpaceUtilities::GetColorSpaceGamut(
+  return color_space_utilities::GetColorSpaceGamut(
       frame->GetPage()->GetChromeClient().GetScreenInfo());
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 978a1ff..574fb14 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7578,7 +7578,8 @@
     if (child->IsLocalFrame()) {
       if (ToLocalFrame(child)->IsAdSubframe()) {
         ToLocalFrame(child)->Navigate(
-            FrameLoadRequest(this, ResourceRequest(BlankURL())));
+            FrameLoadRequest(this, ResourceRequest(BlankURL())),
+            WebFrameLoadType::kStandard);
       }
     }
     // TODO(yuzus): Once AdsTracker for remote frames is implemented and OOPIF
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index d922745..4e520a8c 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -216,6 +216,7 @@
     "progress",
     "processorerror",
     "push",
+    "quicstream",
     "ratechange",
     "reading",
     "readystatechange",
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index 368ce90..d1d9b39 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -34,7 +34,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
@@ -55,6 +54,7 @@
 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
+#include "third_party/blink/renderer/core/inspector/devtools_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspected_frames.h"
 #include "third_party/blink/renderer/core/inspector/inspector_animation_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspector_application_cache_agent.h"
@@ -185,295 +185,113 @@
 
 ClientMessageLoopAdapter* ClientMessageLoopAdapter::instance_ = nullptr;
 
-// --------- WebDevToolsAgentImpl::Session -------------
-
-class WebDevToolsAgentImpl::Session : public GarbageCollectedFinalized<Session>,
-                                      public mojom::blink::DevToolsSession,
-                                      public InspectorSession::Client {
- public:
-  Session(WebDevToolsAgentImpl*,
-          mojom::blink::DevToolsSessionHostAssociatedPtrInfo host_ptr_info,
-          mojom::blink::DevToolsSessionAssociatedRequest main_request,
-          mojom::blink::DevToolsSessionRequest io_request,
-          mojom::blink::DevToolsSessionStatePtr reattach_session_state);
-  ~Session() override;
-
-  virtual void Trace(blink::Visitor*);
-  void Detach();
-
-  InspectorSession* inspector_session() { return inspector_session_.Get(); }
-  InspectorNetworkAgent* network_agent() { return network_agent_.Get(); }
-  InspectorPageAgent* page_agent() { return page_agent_.Get(); }
-  InspectorOverlayAgent* overlay_agent() { return overlay_agent_.Get(); }
-
- private:
-  class IOSession;
-
-  // mojom::blink::DevToolsSession implementation.
-  void DispatchProtocolCommand(int call_id,
-                               const String& method,
-                               const String& message) override;
-
-  // InspectorSession::Client implementation.
-  void SendProtocolResponse(
-      int session_id,
-      int call_id,
-      const String& response,
-      mojom::blink::DevToolsSessionStatePtr updates) override;
-  void SendProtocolNotification(
-      int session_id,
-      const String& message,
-      mojom::blink::DevToolsSessionStatePtr updates) override;
-
-  void DispatchProtocolCommandInternal(int call_id,
-                                       const String& method,
-                                       const String& message);
-  void InitializeInspectorSession(
-      mojom::blink::DevToolsSessionStatePtr reattach_session_state);
-
-  Member<WebDevToolsAgentImpl> agent_;
-  Member<WebLocalFrameImpl> frame_;
-  mojo::AssociatedBinding<mojom::blink::DevToolsSession> binding_;
-  mojom::blink::DevToolsSessionHostAssociatedPtr host_ptr_;
-  IOSession* io_session_;
-  Member<InspectorSession> inspector_session_;
-  Member<InspectorNetworkAgent> network_agent_;
-  Member<InspectorPageAgent> page_agent_;
-  Member<InspectorOverlayAgent> overlay_agent_;
-  bool detached_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(Session);
-};
-
-// Created and stored in unique_ptr on UI.
-// Binds request, receives messages and destroys on IO.
-class WebDevToolsAgentImpl::Session::IOSession
-    : public mojom::blink::DevToolsSession {
- public:
-  IOSession(scoped_refptr<base::SingleThreadTaskRunner> session_task_runner,
-            scoped_refptr<InspectorTaskRunner> inspector_task_runner,
-            CrossThreadWeakPersistent<WebDevToolsAgentImpl::Session> session,
-            mojom::blink::DevToolsSessionRequest request)
-      : session_task_runner_(session_task_runner),
-        inspector_task_runner_(inspector_task_runner),
-        session_(std::move(session)),
-        binding_(this) {
-    session_task_runner->PostTask(
-        FROM_HERE, ConvertToBaseCallback(CrossThreadBind(
-                       &IOSession::BindInterface, CrossThreadUnretained(this),
-                       WTF::Passed(std::move(request)))));
-  }
-
-  ~IOSession() override {}
-
-  void BindInterface(mojom::blink::DevToolsSessionRequest request) {
-    binding_.Bind(std::move(request));
-  }
-
-  void DeleteSoon() { session_task_runner_->DeleteSoon(FROM_HERE, this); }
-
-  // mojom::blink::DevToolsSession implementation.
-  void DispatchProtocolCommand(int call_id,
-                               const String& method,
-                               const String& message) override {
-    DCHECK(InspectorSession::ShouldInterruptForMethod(method));
-    // Crash renderer.
-    if (method == "Page.crash")
-      CHECK(false);
-    inspector_task_runner_->AppendTask(
-        CrossThreadBind(&WebDevToolsAgentImpl::Session::DispatchProtocolCommand,
-                        session_, call_id, method, message));
-  }
-
- private:
-  scoped_refptr<base::SingleThreadTaskRunner> session_task_runner_;
-  scoped_refptr<InspectorTaskRunner> inspector_task_runner_;
-  CrossThreadWeakPersistent<WebDevToolsAgentImpl::Session> session_;
-  mojo::Binding<mojom::blink::DevToolsSession> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(IOSession);
-};
-
-WebDevToolsAgentImpl::Session::Session(
-    WebDevToolsAgentImpl* agent,
-    mojom::blink::DevToolsSessionHostAssociatedPtrInfo host_ptr_info,
-    mojom::blink::DevToolsSessionAssociatedRequest request,
-    mojom::blink::DevToolsSessionRequest io_request,
-    mojom::blink::DevToolsSessionStatePtr reattach_session_state)
-    : agent_(agent),
-      frame_(agent->web_local_frame_impl_),
-      binding_(this, std::move(request)) {
-  io_session_ =
-      new IOSession(Platform::Current()->GetIOTaskRunner(),
-                    frame_->GetFrame()->GetInspectorTaskRunner(),
-                    WrapCrossThreadWeakPersistent(this), std::move(io_request));
-
-  host_ptr_.Bind(std::move(host_ptr_info));
-  host_ptr_.set_connection_error_handler(WTF::Bind(
-      &WebDevToolsAgentImpl::Session::Detach, WrapWeakPersistent(this)));
-
-  InitializeInspectorSession(std::move(reattach_session_state));
-}
-
-WebDevToolsAgentImpl::Session::~Session() {
-  DCHECK(detached_);
-}
-
-void WebDevToolsAgentImpl::Session::Trace(blink::Visitor* visitor) {
-  visitor->Trace(agent_);
-  visitor->Trace(frame_);
-  visitor->Trace(inspector_session_);
-  visitor->Trace(network_agent_);
-  visitor->Trace(page_agent_);
-  visitor->Trace(overlay_agent_);
-}
-
-void WebDevToolsAgentImpl::Session::Detach() {
-  DCHECK(!detached_);
-  detached_ = true;
-  agent_->DetachSession(this);
-  binding_.Close();
-  host_ptr_.reset();
-  io_session_->DeleteSoon();
-  io_session_ = nullptr;
-  inspector_session_->Dispose();
-}
-
-void WebDevToolsAgentImpl::Session::SendProtocolResponse(
-    int session_id,
-    int call_id,
-    const String& response,
-    mojom::blink::DevToolsSessionStatePtr updates) {
-  if (detached_)
-    return;
-
-  // Make tests more predictable by flushing all sessions before sending
-  // protocol response in any of them.
-  if (LayoutTestSupport::IsRunningLayoutTest())
-    agent_->FlushProtocolNotifications();
-  host_ptr_->DispatchProtocolResponse(response, call_id, std::move(updates));
-}
-
-void WebDevToolsAgentImpl::Session::SendProtocolNotification(
-    int session_id,
-    const String& message,
-    mojom::blink::DevToolsSessionStatePtr updates) {
-  host_ptr_->DispatchProtocolNotification(message, std::move(updates));
-}
-
-void WebDevToolsAgentImpl::Session::DispatchProtocolCommand(
-    int call_id,
-    const String& method,
-    const String& message) {
-  // IOSession does not provide ordering guarantees relative to
-  // Session, so a command may come to IOSession after Session is detached,
-  // and get posted to main thread to this method.
-  //
-  // At the same time, Session may not be garbage collected yet
-  // (even though already detached), and CrossThreadWeakPersistent<Session>
-  // will still be valid.
-  //
-  // Both these factors combined may lead to this method being called after
-  // detach, so we have to check a flag here.
-  if (detached_)
-    return;
-  inspector_session_->DispatchProtocolMessage(call_id, method, message);
-}
-
-void WebDevToolsAgentImpl::Session::InitializeInspectorSession(
+InspectorSession* WebDevToolsAgentImpl::AttachSession(
+    InspectorSession::Client* session_client,
     mojom::blink::DevToolsSessionStatePtr reattach_session_state) {
+  if (!sessions_.size())
+    Platform::Current()->CurrentThread()->AddTaskObserver(this);
+
   ClientMessageLoopAdapter::EnsureMainThreadDebuggerCreated();
   MainThreadDebugger* main_thread_debugger = MainThreadDebugger::Instance();
   v8::Isolate* isolate = V8PerIsolateData::MainThreadIsolate();
-  InspectedFrames* inspected_frames = agent_->inspected_frames_.Get();
+  InspectedFrames* inspected_frames = inspected_frames_.Get();
 
   bool should_reattach = !reattach_session_state.is_null();
 
-  inspector_session_ = new InspectorSession(
-      this, agent_->probe_sink_.Get(), 0,
+  InspectorSession* inspector_session = new InspectorSession(
+      session_client, probe_sink_.Get(), 0,
       main_thread_debugger->GetV8Inspector(),
       main_thread_debugger->ContextGroupId(inspected_frames->Root()),
       std::move(reattach_session_state));
 
   InspectorDOMAgent* dom_agent = new InspectorDOMAgent(
-      isolate, inspected_frames, inspector_session_->V8Session());
-  inspector_session_->Append(dom_agent);
+      isolate, inspected_frames, inspector_session->V8Session());
+  inspector_session->Append(dom_agent);
 
   InspectorLayerTreeAgent* layer_tree_agent =
-      InspectorLayerTreeAgent::Create(inspected_frames, agent_);
-  inspector_session_->Append(layer_tree_agent);
+      InspectorLayerTreeAgent::Create(inspected_frames, this);
+  inspector_session->Append(layer_tree_agent);
 
-  network_agent_ = new InspectorNetworkAgent(inspected_frames, nullptr,
-                                             inspector_session_->V8Session());
-  inspector_session_->Append(network_agent_);
+  InspectorNetworkAgent* network_agent = new InspectorNetworkAgent(
+      inspected_frames, nullptr, inspector_session->V8Session());
+  inspector_session->Append(network_agent);
 
-  InspectorCSSAgent* css_agent =
-      InspectorCSSAgent::Create(dom_agent, inspected_frames, network_agent_,
-                                agent_->resource_content_loader_.Get(),
-                                agent_->resource_container_.Get());
-  inspector_session_->Append(css_agent);
+  InspectorCSSAgent* css_agent = InspectorCSSAgent::Create(
+      dom_agent, inspected_frames, network_agent,
+      resource_content_loader_.Get(), resource_container_.Get());
+  inspector_session->Append(css_agent);
 
   InspectorDOMDebuggerAgent* dom_debugger_agent = new InspectorDOMDebuggerAgent(
-      isolate, dom_agent, inspector_session_->V8Session());
-  inspector_session_->Append(dom_debugger_agent);
+      isolate, dom_agent, inspector_session->V8Session());
+  inspector_session->Append(dom_debugger_agent);
 
-  inspector_session_->Append(
+  inspector_session->Append(
       InspectorDOMSnapshotAgent::Create(inspected_frames, dom_debugger_agent));
 
-  inspector_session_->Append(new InspectorAnimationAgent(
-      inspected_frames, css_agent, inspector_session_->V8Session()));
+  inspector_session->Append(new InspectorAnimationAgent(
+      inspected_frames, css_agent, inspector_session->V8Session()));
 
-  inspector_session_->Append(InspectorMemoryAgent::Create(inspected_frames));
+  inspector_session->Append(InspectorMemoryAgent::Create(inspected_frames));
 
-  inspector_session_->Append(
+  inspector_session->Append(
       InspectorPerformanceAgent::Create(inspected_frames));
 
-  inspector_session_->Append(
+  inspector_session->Append(
       InspectorApplicationCacheAgent::Create(inspected_frames));
 
-  inspector_session_->Append(
+  inspector_session->Append(
       new InspectorWorkerAgent(inspected_frames, nullptr));
 
-  page_agent_ = InspectorPageAgent::Create(
-      inspected_frames, agent_, agent_->resource_content_loader_.Get(),
-      inspector_session_->V8Session());
-  inspector_session_->Append(page_agent_);
+  InspectorPageAgent* page_agent = InspectorPageAgent::Create(
+      inspected_frames, this, resource_content_loader_.Get(),
+      inspector_session->V8Session());
+  inspector_session->Append(page_agent);
 
-  inspector_session_->Append(new InspectorLogAgent(
+  inspector_session->Append(new InspectorLogAgent(
       &inspected_frames->Root()->GetPage()->GetConsoleMessageStorage(),
       inspected_frames->Root()->GetPerformanceMonitor(),
-      inspector_session_->V8Session()));
+      inspector_session->V8Session()));
 
-  overlay_agent_ =
-      new InspectorOverlayAgent(frame_.Get(), inspected_frames,
-                                inspector_session_->V8Session(), dom_agent);
-  inspector_session_->Append(overlay_agent_);
+  InspectorOverlayAgent* overlay_agent =
+      new InspectorOverlayAgent(web_local_frame_impl_.Get(), inspected_frames,
+                                inspector_session->V8Session(), dom_agent);
+  inspector_session->Append(overlay_agent);
 
-  inspector_session_->Append(
-      new InspectorIOAgent(isolate, inspector_session_->V8Session()));
+  inspector_session->Append(
+      new InspectorIOAgent(isolate, inspector_session->V8Session()));
 
-  inspector_session_->Append(new InspectorAuditsAgent(network_agent_));
+  inspector_session->Append(new InspectorAuditsAgent(network_agent));
 
   // TODO(dgozman): we should actually pass the view instead of frame, but
   // during remote->local transition we cannot access mainFrameImpl() yet, so
   // we have to store the frame which will become the main frame later.
-  inspector_session_->Append(new InspectorEmulationAgent(frame_.Get()));
+  inspector_session->Append(
+      new InspectorEmulationAgent(web_local_frame_impl_.Get()));
 
-  inspector_session_->Append(new InspectorTestingAgent(inspected_frames));
+  inspector_session->Append(new InspectorTestingAgent(inspected_frames));
 
   // Call session init callbacks registered from higher layers
   CoreInitializer::GetInstance().InitInspectorAgentSession(
-      inspector_session_, agent_->include_view_agents_, dom_agent,
-      inspected_frames, frame_->ViewImpl()->GetPage());
+      inspector_session, include_view_agents_, dom_agent, inspected_frames,
+      web_local_frame_impl_->ViewImpl()->GetPage());
 
   if (should_reattach) {
-    inspector_session_->Restore();
-    if (agent_->worker_client_)
-      agent_->worker_client_->ResumeStartup();
+    inspector_session->Restore();
+    if (worker_client_)
+      worker_client_->ResumeStartup();
   }
-}
 
-// --------- WebDevToolsAgentImpl -------------
+  if (node_to_inspect_) {
+    overlay_agent->Inspect(node_to_inspect_);
+    node_to_inspect_ = nullptr;
+  }
+
+  sessions_.insert(inspector_session);
+  network_agents_.insert(inspector_session, network_agent);
+  page_agents_.insert(inspector_session, page_agent);
+  overlay_agents_.insert(inspector_session, overlay_agent);
+  return inspector_session;
+}
 
 // static
 WebDevToolsAgentImpl* WebDevToolsAgentImpl::CreateForFrame(
@@ -492,8 +310,7 @@
     WebLocalFrameImpl* web_local_frame_impl,
     bool include_view_agents,
     WorkerClient* worker_client)
-    : binding_(this),
-      worker_client_(worker_client),
+    : worker_client_(worker_client),
       web_local_frame_impl_(web_local_frame_impl),
       probe_sink_(web_local_frame_impl_->GetFrame()->GetProbeSink()),
       resource_content_loader_(InspectorResourceContentLoader::Create(
@@ -502,7 +319,9 @@
       resource_container_(new InspectorResourceContainer(inspected_frames_)),
       include_view_agents_(include_view_agents) {
   DCHECK(IsMainThread());
-  DCHECK(web_local_frame_impl_->GetFrame());
+  agent_ = new DevToolsAgent(
+      this, web_local_frame_impl_->GetFrame()->GetInspectorTaskRunner(),
+      Platform::Current()->GetIOTaskRunner());
 }
 
 WebDevToolsAgentImpl::~WebDevToolsAgentImpl() {
@@ -510,7 +329,11 @@
 }
 
 void WebDevToolsAgentImpl::Trace(blink::Visitor* visitor) {
+  visitor->Trace(agent_);
   visitor->Trace(sessions_);
+  visitor->Trace(network_agents_);
+  visitor->Trace(page_agents_);
+  visitor->Trace(overlay_agents_);
   visitor->Trace(web_local_frame_impl_);
   visitor->Trace(probe_sink_);
   visitor->Trace(resource_content_loader_);
@@ -522,36 +345,23 @@
 void WebDevToolsAgentImpl::WillBeDestroyed() {
   DCHECK(web_local_frame_impl_->GetFrame());
   DCHECK(inspected_frames_->Root()->View());
-
-  HeapHashSet<Member<Session>> copy(sessions_);
-  for (auto& session : copy)
-    session->Detach();
-
+  agent_->WillBeDestroyed();
   resource_content_loader_->Dispose();
   worker_client_ = nullptr;
-  binding_.Close();
 }
 
 void WebDevToolsAgentImpl::BindRequest(
     mojom::blink::DevToolsAgentAssociatedRequest request) {
-  binding_.Bind(std::move(request));
+  agent_->BindRequest(std::move(request));
 }
 
-void WebDevToolsAgentImpl::AttachDevToolsSession(
-    mojom::blink::DevToolsSessionHostAssociatedPtrInfo host,
-    mojom::blink::DevToolsSessionAssociatedRequest session_request,
-    mojom::blink::DevToolsSessionRequest io_session_request,
-    mojom::blink::DevToolsSessionStatePtr reattach_session_state) {
+void WebDevToolsAgentImpl::DetachSession(InspectorSession* session) {
+  network_agents_.erase(session);
+  page_agents_.erase(session);
+  overlay_agents_.erase(session);
+  sessions_.erase(session);
   if (!sessions_.size())
-    Platform::Current()->CurrentThread()->AddTaskObserver(this);
-  Session* session = new Session(
-      this, std::move(host), std::move(session_request),
-      std::move(io_session_request), std::move(reattach_session_state));
-  sessions_.insert(session);
-  if (node_to_inspect_) {
-    session->overlay_agent()->Inspect(node_to_inspect_);
-    node_to_inspect_ = nullptr;
-  }
+    Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
 }
 
 void WebDevToolsAgentImpl::InspectElement(const WebPoint& point_in_local_root) {
@@ -589,50 +399,44 @@
   if (!node && web_local_frame_impl_->GetFrame()->GetDocument())
     node = web_local_frame_impl_->GetFrame()->GetDocument()->documentElement();
 
-  if (!sessions_.IsEmpty()) {
-    for (auto& session : sessions_)
-      session->overlay_agent()->Inspect(node);
+  if (!overlay_agents_.IsEmpty()) {
+    for (auto& it : overlay_agents_)
+      it.value->Inspect(node);
   } else {
     node_to_inspect_ = node;
   }
 }
 
-void WebDevToolsAgentImpl::DetachSession(Session* session) {
-  sessions_.erase(session);
-  if (!sessions_.size())
-    Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
-}
-
 void WebDevToolsAgentImpl::DidCommitLoadForLocalFrame(LocalFrame* frame) {
   resource_container_->DidCommitLoadForLocalFrame(frame);
   resource_content_loader_->DidCommitLoadForLocalFrame(frame);
   for (auto& session : sessions_)
-    session->inspector_session()->DidCommitLoadForLocalFrame(frame);
+    session->DidCommitLoadForLocalFrame(frame);
 }
 
 void WebDevToolsAgentImpl::DidStartProvisionalLoad(LocalFrame* frame) {
   if (inspected_frames_->Root() == frame) {
     for (auto& session : sessions_)
-      session->inspector_session()->V8Session()->resume();
+      session->V8Session()->resume();
   }
 }
 
 bool WebDevToolsAgentImpl::ScreencastEnabled() {
-  for (auto& session : sessions_) {
-    if (session->page_agent()->ScreencastEnabled())
+  for (auto& it : page_agents_) {
+    if (it.value->ScreencastEnabled())
       return true;
   }
   return false;
 }
 
 void WebDevToolsAgentImpl::PageLayoutInvalidated(bool resized) {
-  for (auto& session : sessions_)
-    session->overlay_agent()->PageLayoutInvalidated(resized);
+  for (auto& it : overlay_agents_)
+    it.value->PageLayoutInvalidated(resized);
 }
 
 bool WebDevToolsAgentImpl::IsInspectorLayer(GraphicsLayer* layer) {
-  for (auto& session : sessions_) {
-    if (session->overlay_agent()->IsInspectorLayer(layer))
+  for (auto& it : overlay_agents_) {
+    if (it.value->IsInspectorLayer(layer))
       return true;
   }
   return false;
@@ -640,32 +444,32 @@
 
 String WebDevToolsAgentImpl::EvaluateInOverlayForTesting(const String& script) {
   String result;
-  for (auto& session : sessions_)
-    result = session->overlay_agent()->EvaluateInOverlayForTest(script);
+  for (auto& it : overlay_agents_)
+    result = it.value->EvaluateInOverlayForTest(script);
   return result;
 }
 
 void WebDevToolsAgentImpl::UpdateOverlays() {
-  for (auto& session : sessions_)
-    session->overlay_agent()->UpdateAllOverlayLifecyclePhases();
+  for (auto& it : overlay_agents_)
+    it.value->UpdateAllOverlayLifecyclePhases();
 }
 
 void WebDevToolsAgentImpl::DispatchBufferedTouchEvents() {
-  for (auto& session : sessions_)
-    session->overlay_agent()->DispatchBufferedTouchEvents();
+  for (auto& it : overlay_agents_)
+    it.value->DispatchBufferedTouchEvents();
 }
 
 bool WebDevToolsAgentImpl::HandleInputEvent(const WebInputEvent& event) {
-  for (auto& session : sessions_) {
-    if (session->overlay_agent()->HandleInputEvent(event))
+  for (auto& it : overlay_agents_) {
+    if (it.value->HandleInputEvent(event))
       return true;
   }
   return false;
 }
 
 String WebDevToolsAgentImpl::NavigationInitiatorInfo(LocalFrame* frame) {
-  for (auto& session : sessions_) {
-    String initiator = session->network_agent()->NavigationInitiatorInfo(frame);
+  for (auto& it : network_agents_) {
+    String initiator = it.value->NavigationInitiatorInfo(frame);
     if (!initiator.IsNull())
       return initiator;
   }
@@ -673,8 +477,7 @@
 }
 
 void WebDevToolsAgentImpl::FlushProtocolNotifications() {
-  for (auto& session : sessions_)
-    session->inspector_session()->flushProtocolNotifications();
+  agent_->FlushProtocolNotifications();
 }
 
 void WebDevToolsAgentImpl::WillProcessTask() {
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
index 61d383f0..acc14e3 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
@@ -33,11 +33,11 @@
 
 #include <memory>
 
-#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_thread.h"
 #include "third_party/blink/public/web/devtools_agent.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/inspector/devtools_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspector_page_agent.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -48,6 +48,8 @@
 class ClientMessageLoopAdapter;
 class GraphicsLayer;
 class InspectedFrames;
+class InspectorNetworkAgent;
+class InspectorOverlayAgent;
 class InspectorResourceContainer;
 class InspectorResourceContentLoader;
 class LocalFrame;
@@ -55,7 +57,7 @@
 
 class CORE_EXPORT WebDevToolsAgentImpl final
     : public GarbageCollectedFinalized<WebDevToolsAgentImpl>,
-      public mojom::blink::DevToolsAgent,
+      public DevToolsAgent::Client,
       public InspectorPageAgent::Client,
       public InspectorLayerTreeAgent::Client,
       private WebThread::TaskObserver {
@@ -88,18 +90,16 @@
 
  private:
   friend class ClientMessageLoopAdapter;
-  class Session;
 
   WebDevToolsAgentImpl(WebLocalFrameImpl*,
                        bool include_view_agents,
                        WorkerClient*);
 
-  // mojom::blink::DevToolsAgent implementation.
-  void AttachDevToolsSession(
-      mojom::blink::DevToolsSessionHostAssociatedPtrInfo,
-      mojom::blink::DevToolsSessionAssociatedRequest main_session,
-      mojom::blink::DevToolsSessionRequest io_session,
+  // DevToolsAgent::Client implementation.
+  InspectorSession* AttachSession(
+      InspectorSession::Client*,
       mojom::blink::DevToolsSessionStatePtr reattach_session_state) override;
+  void DetachSession(InspectorSession*) override;
   void InspectElement(const WebPoint& point_in_local_root) override;
 
   // InspectorPageAgent::Client implementation.
@@ -112,10 +112,14 @@
   void WillProcessTask() override;
   void DidProcessTask() override;
 
-  void DetachSession(Session*);
-
-  mojo::AssociatedBinding<mojom::blink::DevToolsAgent> binding_;
-  HeapHashSet<Member<Session>> sessions_;
+  Member<DevToolsAgent> agent_;
+  HeapHashSet<Member<InspectorSession>> sessions_;
+  HeapHashMap<Member<InspectorSession>, Member<InspectorNetworkAgent>>
+      network_agents_;
+  HeapHashMap<Member<InspectorSession>, Member<InspectorPageAgent>>
+      page_agents_;
+  HeapHashMap<Member<InspectorSession>, Member<InspectorOverlayAgent>>
+      overlay_agents_;
   WorkerClient* worker_client_;
   Member<WebLocalFrameImpl> web_local_frame_impl_;
   Member<CoreProbeSink> probe_sink_;
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index ab84e7e..cfaa334 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -6550,9 +6550,12 @@
   // FrameTestHelpers::TestWebFrameClient:
   void DidFailProvisionalLoad(const WebURLError& error,
                               WebHistoryCommitType) override {
-    Frame()->LoadHTMLString("This should appear",
-                            ToKURL("chrome-error://chromewebdata/"),
-                            error.url(), true);
+    Frame()->CommitDataNavigation(
+        WebURLRequest(ToKURL("chrome-error://chromewebdata/")),
+        WebData("This should appear"), WebString::FromUTF8("text/html"),
+        WebString::FromUTF8("UTF-8"), error.url(),
+        WebFrameLoadType::kReplaceCurrentItem, WebHistoryItem(),
+        false /* is_client_redirect */, nullptr, nullptr);
   }
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 036e509..5845feb 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -99,10 +99,10 @@
   // eventually fixed.
   virtual void ScheduleNavigation(Document& origin_document,
                                   const KURL&,
-                                  bool replace_current_item,
+                                  WebFrameLoadType,
                                   UserGestureStatus) = 0;
   // Synchronously begins a navigation.
-  virtual void Navigate(const FrameLoadRequest&) = 0;
+  virtual void Navigate(const FrameLoadRequest&, WebFrameLoadType) = 0;
 
   void Detach(FrameDetachType);
   void DisconnectOwnerElement();
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 43bc62d2..1996ac95 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1602,7 +1602,8 @@
     if (url_string.IsEmpty())
       return target_frame->DomWindow();
 
-    target_frame->ScheduleNavigation(*active_document, completed_url, false,
+    target_frame->ScheduleNavigation(*active_document, completed_url,
+                                     WebFrameLoadType::kStandard,
                                      UserGestureStatus::kNone);
     return target_frame->DomWindow();
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 3ca32dc..6e83b3a 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -272,14 +272,15 @@
 
 void LocalFrame::ScheduleNavigation(Document& origin_document,
                                     const KURL& url,
-                                    bool replace_current_item,
+                                    WebFrameLoadType frame_load_type,
                                     UserGestureStatus user_gesture_status) {
   navigation_scheduler_->ScheduleFrameNavigation(&origin_document, url,
-                                                 replace_current_item);
+                                                 frame_load_type);
 }
 
-void LocalFrame::Navigate(const FrameLoadRequest& request) {
-  loader_.StartNavigation(request);
+void LocalFrame::Navigate(const FrameLoadRequest& request,
+                          WebFrameLoadType frame_load_type) {
+  loader_.StartNavigation(request, frame_load_type);
 }
 
 void LocalFrame::DetachImpl(FrameDetachType type) {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 4b52f9e..b0a066e 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -127,9 +127,9 @@
   void Trace(blink::Visitor*) override;
   void ScheduleNavigation(Document& origin_document,
                           const KURL&,
-                          bool replace_current_item,
+                          WebFrameLoadType,
                           UserGestureStatus) override;
-  void Navigate(const FrameLoadRequest&) override;
+  void Navigate(const FrameLoadRequest&, WebFrameLoadType) override;
   bool ShouldClose() override;
   SecurityContext* GetSecurityContext() const override;
   void PrintNavigationErrorMessage(const Frame&, const char* reason);
diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc
index 23d4b30..3306af7 100644
--- a/third_party/blink/renderer/core/frame/location.cc
+++ b/third_party/blink/renderer/core/frame/location.cc
@@ -304,10 +304,12 @@
     argv.push_back(completed_url);
     activity_logger->LogEvent("blinkSetAttribute", argv.size(), argv.data());
   }
-  dom_window_->GetFrame()->ScheduleNavigation(
-      *current_window->document(), completed_url,
-      set_location_policy == SetLocationPolicy::kReplaceThisFrame,
-      UserGestureStatus::kNone);
+  WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
+  if (set_location_policy == SetLocationPolicy::kReplaceThisFrame)
+    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
+  dom_window_->GetFrame()->ScheduleNavigation(*current_window->document(),
+                                              completed_url, frame_load_type,
+                                              UserGestureStatus::kNone);
 }
 
 Document* Location::GetDocument() const {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index bc54e83..e1358d8 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -58,19 +58,19 @@
 
 void RemoteFrame::ScheduleNavigation(Document& origin_document,
                                      const KURL& url,
-                                     bool replace_current_item,
+                                     WebFrameLoadType frame_load_type,
                                      UserGestureStatus user_gesture_status) {
   FrameLoadRequest frame_request(&origin_document, ResourceRequest(url));
-  frame_request.SetReplacesCurrentItem(replace_current_item);
   frame_request.GetResourceRequest().SetHasUserGesture(
       user_gesture_status == UserGestureStatus::kActive);
   frame_request.GetResourceRequest().SetFrameType(
       IsMainFrame() ? network::mojom::RequestContextFrameType::kTopLevel
                     : network::mojom::RequestContextFrameType::kNested);
-  Navigate(frame_request);
+  Navigate(frame_request, frame_load_type);
 }
 
-void RemoteFrame::Navigate(const FrameLoadRequest& passed_request) {
+void RemoteFrame::Navigate(const FrameLoadRequest& passed_request,
+                           WebFrameLoadType frame_load_type) {
   FrameLoadRequest frame_request(passed_request);
 
   // The process where this frame actually lives won't have sufficient
@@ -81,7 +81,7 @@
                                       frame_request.OriginDocument());
 
   Client()->Navigate(frame_request.GetResourceRequest(),
-                     frame_request.ReplacesCurrentItem(),
+                     frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
                      frame_request.GetBlobURLToken());
 }
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index afc7119..84173c0 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -31,9 +31,9 @@
   void Trace(blink::Visitor*) override;
   void ScheduleNavigation(Document& origin_document,
                           const KURL&,
-                          bool replace_current_item,
+                          WebFrameLoadType,
                           UserGestureStatus) override;
-  void Navigate(const FrameLoadRequest& passed_request) override;
+  void Navigate(const FrameLoadRequest&, WebFrameLoadType) override;
   RemoteSecurityContext* GetSecurityContext() const override;
   bool PrepareForCommit() override;
   void CheckCompleted() override;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 76b761b0..7632df4 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -949,12 +949,11 @@
 
 void WebLocalFrameImpl::LoadHTMLString(const WebData& data,
                                        const WebURL& base_url,
-                                       const WebURL& unreachable_url,
-                                       bool replace) {
+                                       const WebURL& unreachable_url) {
   DCHECK(GetFrame());
   CommitDataNavigation(
       WebURLRequest(base_url), data, WebString::FromUTF8("text/html"),
-      WebString::FromUTF8("UTF-8"), unreachable_url, replace,
+      WebString::FromUTF8("UTF-8"), unreachable_url,
       WebFrameLoadType::kStandard, WebHistoryItem(), false, nullptr, nullptr);
 }
 
@@ -2133,7 +2132,6 @@
     const WebString& mime_type,
     const WebString& text_encoding,
     const WebURL& unreachable_url,
-    bool replace,
     WebFrameLoadType web_frame_load_type,
     const WebHistoryItem& history_item,
     bool is_client_redirect,
@@ -2143,7 +2141,6 @@
       nullptr, request.ToResourceRequest(),
       SubstituteData(data, mime_type, text_encoding, unreachable_url));
   DCHECK(frame_request.GetSubstituteData().IsValid());
-  frame_request.SetReplacesCurrentItem(replace);
   if (is_client_redirect)
     frame_request.SetClientRedirect(ClientRedirectPolicy::kClientRedirect);
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 383fd8c..48918c3c 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -156,8 +156,7 @@
   void CheckCompleted() override;
   void LoadHTMLString(const WebData& html,
                       const WebURL& base_url,
-                      const WebURL& unreachable_url,
-                      bool replace) override;
+                      const WebURL& unreachable_url) override;
   void StopLoading() override;
   WebDocumentLoader* GetProvisionalDocumentLoader() const override;
   WebDocumentLoader* GetDocumentLoader() const override;
@@ -282,7 +281,6 @@
       const WebString& mime_type,
       const WebString& text_encoding,
       const WebURL& unreachable_url,
-      bool replace,
       WebFrameLoadType,
       const WebHistoryItem&,
       bool is_client_redirect,
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 06846322..3f10176 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -490,7 +490,8 @@
         LocalFrame::HasTransientUserActivation(GetDocument().GetFrame()));
     // TODO(dgozman): we lose information about triggering event and desired
     // navigation policy here.
-    ToRemoteFrame(target_frame)->Navigate(frame_load_request);
+    ToRemoteFrame(target_frame)
+        ->Navigate(frame_load_request, WebFrameLoadType::kStandard);
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 320afef..da2aaf4 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -364,7 +364,10 @@
 
   if (ContentFrame()) {
     // TODO(sclittle): Support lazily loading frame navigations.
-    ContentFrame()->ScheduleNavigation(GetDocument(), url, replace_current_item,
+    WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
+    if (replace_current_item)
+      frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
+    ContentFrame()->ScheduleNavigation(GetDocument(), url, frame_load_type,
                                        UserGestureStatus::kNone);
     return true;
   }
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index 953ba5f..1077580 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -620,8 +620,10 @@
 
   if (update_hover_reason ==
           MouseEventManager::UpdateHoverReason::kScrollOffsetChanged &&
-      mouse_pressed_)
+      (RuntimeEnabledFeatures::NoHoverDuringScrollEnabled() ||
+       mouse_pressed_)) {
     return;
+  }
 
   // TODO(lanwei): When the mouse position is unknown, we do not send the fake
   // mousemove event for now, so we cannot update the hover states and mouse
diff --git a/third_party/blink/renderer/core/inspector/BUILD.gn b/third_party/blink/renderer/core/inspector/BUILD.gn
index 0c995cd..9d1daad 100644
--- a/third_party/blink/renderer/core/inspector/BUILD.gn
+++ b/third_party/blink/renderer/core/inspector/BUILD.gn
@@ -23,6 +23,8 @@
     "dev_tools_emulator.h",
     "dev_tools_host.cc",
     "dev_tools_host.h",
+    "devtools_agent.cc",
+    "devtools_agent.h",
     "dom_editor.cc",
     "dom_editor.h",
     "dom_patch_support.cc",
diff --git a/third_party/blink/renderer/core/inspector/devtools_agent.cc b/third_party/blink/renderer/core/inspector/devtools_agent.cc
new file mode 100644
index 0000000..4400f14
--- /dev/null
+++ b/third_party/blink/renderer/core/inspector/devtools_agent.cc
@@ -0,0 +1,246 @@
+// 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/inspector/devtools_agent.h"
+
+#include <v8-inspector.h>
+#include <memory>
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/renderer/core/inspector/inspector_session.h"
+#include "third_party/blink/renderer/core/inspector/inspector_task_runner.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// --------- DevToolsAgent::Session -------------
+
+class DevToolsAgent::Session : public GarbageCollectedFinalized<Session>,
+                               public mojom::blink::DevToolsSession,
+                               public InspectorSession::Client {
+ public:
+  Session(DevToolsAgent*,
+          mojom::blink::DevToolsSessionHostAssociatedPtrInfo host_ptr_info,
+          mojom::blink::DevToolsSessionAssociatedRequest main_request,
+          mojom::blink::DevToolsSessionRequest io_request,
+          mojom::blink::DevToolsSessionStatePtr reattach_session_state);
+  ~Session() override;
+
+  virtual void Trace(blink::Visitor*);
+  void Detach();
+
+  InspectorSession* inspector_session() { return inspector_session_.Get(); }
+
+ private:
+  class IOSession;
+
+  // mojom::blink::DevToolsSession implementation.
+  void DispatchProtocolCommand(int call_id,
+                               const String& method,
+                               const String& message) override;
+
+  // InspectorSession::Client implementation.
+  void SendProtocolResponse(
+      int session_id,
+      int call_id,
+      const String& response,
+      mojom::blink::DevToolsSessionStatePtr updates) override;
+  void SendProtocolNotification(
+      int session_id,
+      const String& message,
+      mojom::blink::DevToolsSessionStatePtr updates) override;
+
+  void DispatchProtocolCommandInternal(int call_id,
+                                       const String& method,
+                                       const String& message);
+
+  Member<DevToolsAgent> agent_;
+  mojo::AssociatedBinding<mojom::blink::DevToolsSession> binding_;
+  mojom::blink::DevToolsSessionHostAssociatedPtr host_ptr_;
+  IOSession* io_session_;
+  Member<InspectorSession> inspector_session_;
+
+  DISALLOW_COPY_AND_ASSIGN(Session);
+};
+
+// Created and stored in unique_ptr on UI.
+// Binds request, receives messages and destroys on IO.
+class DevToolsAgent::Session::IOSession : public mojom::blink::DevToolsSession {
+ public:
+  IOSession(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+            scoped_refptr<InspectorTaskRunner> inspector_task_runner,
+            CrossThreadWeakPersistent<DevToolsAgent::Session> session,
+            mojom::blink::DevToolsSessionRequest request)
+      : io_task_runner_(io_task_runner),
+        inspector_task_runner_(inspector_task_runner),
+        session_(std::move(session)),
+        binding_(this) {
+    io_task_runner->PostTask(
+        FROM_HERE, ConvertToBaseCallback(CrossThreadBind(
+                       &IOSession::BindInterface, CrossThreadUnretained(this),
+                       WTF::Passed(std::move(request)))));
+  }
+
+  ~IOSession() override {}
+
+  void BindInterface(mojom::blink::DevToolsSessionRequest request) {
+    binding_.Bind(std::move(request));
+  }
+
+  void DeleteSoon() { io_task_runner_->DeleteSoon(FROM_HERE, this); }
+
+  // mojom::blink::DevToolsSession implementation.
+  void DispatchProtocolCommand(int call_id,
+                               const String& method,
+                               const String& message) override {
+    DCHECK(InspectorSession::ShouldInterruptForMethod(method));
+    // Crash renderer.
+    if (method == "Page.crash")
+      CHECK(false);
+    inspector_task_runner_->AppendTask(
+        CrossThreadBind(&DevToolsAgent::Session::DispatchProtocolCommand,
+                        session_, call_id, method, message));
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  scoped_refptr<InspectorTaskRunner> inspector_task_runner_;
+  CrossThreadWeakPersistent<DevToolsAgent::Session> session_;
+  mojo::Binding<mojom::blink::DevToolsSession> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOSession);
+};
+
+DevToolsAgent::Session::Session(
+    DevToolsAgent* agent,
+    mojom::blink::DevToolsSessionHostAssociatedPtrInfo host_ptr_info,
+    mojom::blink::DevToolsSessionAssociatedRequest request,
+    mojom::blink::DevToolsSessionRequest io_request,
+    mojom::blink::DevToolsSessionStatePtr reattach_session_state)
+    : agent_(agent), binding_(this, std::move(request)) {
+  io_session_ =
+      new IOSession(agent_->io_task_runner_, agent_->inspector_task_runner_,
+                    WrapCrossThreadWeakPersistent(this), std::move(io_request));
+
+  host_ptr_.Bind(std::move(host_ptr_info));
+  host_ptr_.set_connection_error_handler(
+      WTF::Bind(&DevToolsAgent::Session::Detach, WrapWeakPersistent(this)));
+  inspector_session_ =
+      agent_->client_->AttachSession(this, std::move(reattach_session_state));
+}
+
+DevToolsAgent::Session::~Session() {
+  DCHECK(!host_ptr_.is_bound());
+}
+
+void DevToolsAgent::Session::Trace(blink::Visitor* visitor) {
+  visitor->Trace(agent_);
+  visitor->Trace(inspector_session_);
+}
+
+void DevToolsAgent::Session::Detach() {
+  agent_->client_->DetachSession(inspector_session_.Get());
+  agent_->sessions_.erase(this);
+  binding_.Close();
+  host_ptr_.reset();
+  io_session_->DeleteSoon();
+  io_session_ = nullptr;
+  inspector_session_->Dispose();
+}
+
+void DevToolsAgent::Session::SendProtocolResponse(
+    int session_id,
+    int call_id,
+    const String& response,
+    mojom::blink::DevToolsSessionStatePtr updates) {
+  if (!host_ptr_.is_bound())
+    return;
+  // Make tests more predictable by flushing all sessions before sending
+  // protocol response in any of them.
+  if (LayoutTestSupport::IsRunningLayoutTest())
+    agent_->FlushProtocolNotifications();
+  host_ptr_->DispatchProtocolResponse(response, call_id, std::move(updates));
+}
+
+void DevToolsAgent::Session::SendProtocolNotification(
+    int session_id,
+    const String& message,
+    mojom::blink::DevToolsSessionStatePtr updates) {
+  if (!host_ptr_.is_bound())
+    return;
+  host_ptr_->DispatchProtocolNotification(message, std::move(updates));
+}
+
+void DevToolsAgent::Session::DispatchProtocolCommand(int call_id,
+                                                     const String& method,
+                                                     const String& message) {
+  // IOSession does not provide ordering guarantees relative to
+  // Session, so a command may come to IOSession after Session is detached,
+  // and get posted to main thread to this method.
+  //
+  // At the same time, Session may not be garbage collected yet
+  // (even though already detached), and CrossThreadWeakPersistent<Session>
+  // will still be valid.
+  //
+  // Both these factors combined may lead to this method being called after
+  // detach, so we have to check it here.
+  if (!host_ptr_.is_bound())
+    return;
+  inspector_session_->DispatchProtocolMessage(call_id, method, message);
+}
+
+// --------- DevToolsAgent -------------
+
+DevToolsAgent::DevToolsAgent(
+    Client* client,
+    scoped_refptr<InspectorTaskRunner> inspector_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    : client_(client),
+      binding_(this),
+      inspector_task_runner_(std::move(inspector_task_runner)),
+      io_task_runner_(std::move(io_task_runner)) {}
+
+DevToolsAgent::~DevToolsAgent() {}
+
+void DevToolsAgent::Trace(blink::Visitor* visitor) {
+  visitor->Trace(sessions_);
+}
+
+void DevToolsAgent::WillBeDestroyed() {
+  HeapHashSet<Member<Session>> copy(sessions_);
+  for (auto& session : copy)
+    session->Detach();
+  binding_.Close();
+}
+
+void DevToolsAgent::BindRequest(
+    mojom::blink::DevToolsAgentAssociatedRequest request) {
+  binding_.Bind(std::move(request));
+}
+
+void DevToolsAgent::AttachDevToolsSession(
+    mojom::blink::DevToolsSessionHostAssociatedPtrInfo host,
+    mojom::blink::DevToolsSessionAssociatedRequest session_request,
+    mojom::blink::DevToolsSessionRequest io_session_request,
+    mojom::blink::DevToolsSessionStatePtr reattach_session_state) {
+  Session* session = new Session(
+      this, std::move(host), std::move(session_request),
+      std::move(io_session_request), std::move(reattach_session_state));
+  sessions_.insert(session);
+}
+
+void DevToolsAgent::InspectElement(const WebPoint& point) {
+  client_->InspectElement(point);
+}
+
+void DevToolsAgent::FlushProtocolNotifications() {
+  for (auto& session : sessions_)
+    session->inspector_session()->flushProtocolNotifications();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/devtools_agent.h b/third_party/blink/renderer/core/inspector/devtools_agent.h
new file mode 100644
index 0000000..2ce2cf3
--- /dev/null
+++ b/third_party/blink/renderer/core/inspector/devtools_agent.h
@@ -0,0 +1,67 @@
+// 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_INSPECTOR_DEVTOOLS_AGENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_DEVTOOLS_AGENT_H_
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/blink/public/web/devtools_agent.mojom-blink.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/inspector/inspector_session.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class InspectorTaskRunner;
+
+class CORE_EXPORT DevToolsAgent
+    : public GarbageCollectedFinalized<DevToolsAgent>,
+      public mojom::blink::DevToolsAgent {
+ public:
+  class Client {
+   public:
+    virtual ~Client() {}
+    virtual InspectorSession* AttachSession(
+        InspectorSession::Client*,
+        mojom::blink::DevToolsSessionStatePtr reattach_session_state) = 0;
+    virtual void DetachSession(InspectorSession*) = 0;
+    virtual void InspectElement(const WebPoint&) = 0;
+  };
+
+  DevToolsAgent(Client*,
+                scoped_refptr<InspectorTaskRunner> inspector_task_runner,
+                scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+  ~DevToolsAgent() override;
+
+  void WillBeDestroyed();
+  void FlushProtocolNotifications();
+  void BindRequest(mojom::blink::DevToolsAgentAssociatedRequest);
+  virtual void Trace(blink::Visitor*);
+
+ private:
+  class Session;
+
+  // mojom::blink::DevToolsAgent implementation.
+  void AttachDevToolsSession(
+      mojom::blink::DevToolsSessionHostAssociatedPtrInfo,
+      mojom::blink::DevToolsSessionAssociatedRequest main_session,
+      mojom::blink::DevToolsSessionRequest io_session,
+      mojom::blink::DevToolsSessionStatePtr reattach_session_state) override;
+  void InspectElement(const WebPoint& point) override;
+
+  Client* client_;
+  mojo::AssociatedBinding<mojom::blink::DevToolsAgent> binding_;
+  HeapHashSet<Member<Session>> sessions_;
+  scoped_refptr<InspectorTaskRunner> inspector_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_DEVTOOLS_AGENT_H_
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index ef0d9ce..aa7e9178 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -1312,6 +1312,23 @@
   return overflow_rect;
 }
 
+LayoutRect LayoutInline::ReferenceBoxForClipPath() const {
+  // The spec just says to use the border box as clip-path reference box. It
+  // doesn't say what to do if there are multiple lines. Gecko uses the first
+  // fragment in that case. We'll do the same here (but correctly with respect
+  // to writing-mode - Gecko has some issues there).
+  // See crbug.com/641907
+  LayoutRect bounding_box;
+  if (const NGPaintFragment* fragment = FirstInlineFragment()) {
+    bounding_box.SetLocation(fragment->Offset().ToLayoutPoint());
+    bounding_box.SetSize(fragment->Size().ToLayoutSize());
+  } else if (const InlineFlowBox* flow_box = FirstLineBox()) {
+    bounding_box = flow_box->FrameRect();
+  }
+  ContainingBlock()->FlipForWritingMode(bounding_box);
+  return bounding_box;
+}
+
 bool LayoutInline::MapToVisualRectInAncestorSpaceInternal(
     const LayoutBoxModelObject* ancestor,
     TransformState& transform_state,
diff --git a/third_party/blink/renderer/core/layout/layout_inline.h b/third_party/blink/renderer/core/layout/layout_inline.h
index 14c8dcd..b55cc5eb 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.h
+++ b/third_party/blink/renderer/core/layout/layout_inline.h
@@ -158,6 +158,7 @@
 
   LayoutRect LinesBoundingBox() const;
   LayoutRect VisualOverflowRect() const final;
+  LayoutRect ReferenceBoxForClipPath() const;
 
   InlineFlowBox* CreateAndAppendInlineFlowBox();
 
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
index 4510299e..ec240a0 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
@@ -165,7 +165,8 @@
     // navigation algorithm. The navigation will not result in the same resource
     // being loaded, because "foreign" entries are never picked during
     // navigation. see ApplicationCacheGroup::selectCache()
-    frame->ScheduleNavigation(*document, document->Url(), true,
+    frame->ScheduleNavigation(*document, document->Url(),
+                              WebFrameLoadType::kReplaceCurrentItem,
                               UserGestureStatus::kNone);
   }
 }
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.cc b/third_party/blink/renderer/core/loader/frame_load_request.cc
index 0b30b8da..19de358 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.cc
+++ b/third_party/blink/renderer/core/loader/frame_load_request.cc
@@ -76,7 +76,6 @@
       resource_request_(resource_request),
       frame_name_(frame_name),
       substitute_data_(substitute_data),
-      replaces_current_item_(false),
       client_redirect_(ClientRedirectPolicy::kNotClientRedirect),
       should_send_referrer_(kMaybeSendReferrer),
       should_set_opener_(kMaybeSetOpener),
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index 50379f81..13502501 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -76,11 +76,6 @@
 
   const SubstituteData& GetSubstituteData() const { return substitute_data_; }
 
-  bool ReplacesCurrentItem() const { return replaces_current_item_; }
-  void SetReplacesCurrentItem(bool replaces_current_item) {
-    replaces_current_item_ = replaces_current_item;
-  }
-
   ClientRedirectPolicy ClientRedirect() const { return client_redirect_; }
   void SetClientRedirect(ClientRedirectPolicy client_redirect) {
     client_redirect_ = client_redirect;
@@ -167,7 +162,6 @@
   AtomicString frame_name_;
   AtomicString href_translate_;
   SubstituteData substitute_data_;
-  bool replaces_current_item_;
   ClientRedirectPolicy client_redirect_;
   WebTriggeringEventInfo triggering_event_info_ =
       WebTriggeringEventInfo::kNotFromEvent;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index d055dba..bfc32b4 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -629,15 +629,27 @@
 }
 
 WebFrameLoadType FrameLoader::DetermineFrameLoadType(
-    const FrameLoadRequest& request) {
-  if (frame_->Tree().Parent() &&
-      !state_machine_.CommittedFirstRealDocumentLoad())
-    return WebFrameLoadType::kReplaceCurrentItem;
-  if (!frame_->Tree().Parent() && !Client()->BackForwardLength()) {
-    if (Opener() && request.GetResourceRequest().Url().IsEmpty())
+    const FrameLoadRequest& request,
+    WebFrameLoadType frame_load_type) {
+  // TODO(dgozman): this method is rewriting the load type, which makes it hard
+  // to reason about various navigations and their desired load type. We should
+  // untangle it and detect the load type at the proper place. See, for example,
+  // location.assign() block below.
+  // Achieving that is complicated due to similar conditions in many places
+  // both in the renderer and in the browser.
+  if (frame_load_type == WebFrameLoadType::kStandard ||
+      frame_load_type == WebFrameLoadType::kReplaceCurrentItem) {
+    if (frame_->Tree().Parent() &&
+        !state_machine_.CommittedFirstRealDocumentLoad())
       return WebFrameLoadType::kReplaceCurrentItem;
-    return WebFrameLoadType::kStandard;
+    if (!frame_->Tree().Parent() && !Client()->BackForwardLength()) {
+      if (Opener() && request.GetResourceRequest().Url().IsEmpty())
+        return WebFrameLoadType::kReplaceCurrentItem;
+      return WebFrameLoadType::kStandard;
+    }
   }
+  if (frame_load_type != WebFrameLoadType::kStandard)
+    return frame_load_type;
   CHECK_NE(mojom::FetchCacheMode::kValidateCache,
            request.GetResourceRequest().GetCacheMode());
   CHECK_NE(mojom::FetchCacheMode::kBypassCache,
@@ -646,8 +658,7 @@
   // "If the browsing context's session history contains only one Document,
   // and that was the about:blank Document created when the browsing context
   // was created, then the navigation must be done with replacement enabled."
-  if (request.ReplacesCurrentItem() ||
-      (!state_machine_.CommittedMultipleRealLoads() &&
+  if ((!state_machine_.CommittedMultipleRealLoads() &&
        DeprecatedEqualIgnoringCase(frame_->GetDocument()->Url(), BlankURL())))
     return WebFrameLoadType::kReplaceCurrentItem;
 
@@ -806,7 +817,7 @@
     bool was_in_same_page = target_frame->GetPage() == frame_->GetPage();
 
     request.SetFrameName("_self");
-    target_frame->Navigate(request);
+    target_frame->Navigate(request, frame_load_type);
     Page* page = target_frame->GetPage();
     if (!was_in_same_page && page)
       page->GetChromeClient().Focus(frame_);
@@ -835,8 +846,7 @@
     return;
   }
 
-  if (frame_load_type == WebFrameLoadType::kStandard)
-    frame_load_type = DetermineFrameLoadType(request);
+  frame_load_type = DetermineFrameLoadType(request, frame_load_type);
 
   bool same_document_navigation =
       policy == kNavigationPolicyCurrentTab &&
@@ -934,7 +944,6 @@
         nullptr, resource_request, AtomicString(),
         request.ShouldCheckMainWorldContentSecurityPolicy(),
         request.GetDevToolsNavigationToken());
-    new_request.SetReplacesCurrentItem(request.ReplacesCurrentItem());
     new_request.SetClientRedirect(request.ClientRedirect());
     CommitNavigation(new_request, frame_load_type, nullptr, nullptr, nullptr);
     return;
@@ -1000,8 +1009,7 @@
   resource_request.SetHasUserGesture(
       LocalFrame::HasTransientUserActivation(frame_));
 
-  if (frame_load_type == WebFrameLoadType::kStandard)
-    frame_load_type = DetermineFrameLoadType(request);
+  frame_load_type = DetermineFrameLoadType(request, frame_load_type);
 
   // Note: we might actually classify this navigation as same document
   // right here in the following circumstances:
@@ -1760,6 +1768,9 @@
   bool replace_current_item =
       load_type == WebFrameLoadType::kReplaceCurrentItem &&
       (!Opener() || !request.Url().IsEmpty());
+  // TODO(dgozman): we should get rid of this boolean field, and make client
+  // responsible for it's own view of "replaces current item", based on the
+  // frame load type.
   loader->SetReplacesCurrentHistoryItem(replace_current_item);
 
   probe::lifecycleEvent(frame_, loader, "init", CurrentTimeTicksInSeconds());
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 11c3de10..2f113d6a 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -229,7 +229,8 @@
 
  private:
   bool PrepareRequestForThisFrame(FrameLoadRequest&);
-  WebFrameLoadType DetermineFrameLoadType(const FrameLoadRequest&);
+  WebFrameLoadType DetermineFrameLoadType(const FrameLoadRequest&,
+                                          WebFrameLoadType);
 
   SubstituteData DefaultSubstituteDataForURL(const KURL&);
 
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.cc b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
index ba1dd26..7d5aba6 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.cc
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
@@ -70,18 +70,15 @@
                          double delay,
                          Document* origin_document,
                          const KURL& url,
-                         bool replaces_current_item,
+                         WebFrameLoadType frame_load_type,
                          bool is_location_change,
                          base::TimeTicks input_timestamp)
-      : ScheduledNavigation(reason,
-                            delay,
-                            origin_document,
-                            replaces_current_item,
-                            is_location_change),
+      : ScheduledNavigation(reason, delay, origin_document, is_location_change),
         url_(url),
         should_check_main_world_content_security_policy_(
             kCheckContentSecurityPolicy),
-        input_timestamp_(input_timestamp) {
+        input_timestamp_(input_timestamp),
+        frame_load_type_(frame_load_type) {
     if (ContentSecurityPolicy::ShouldBypassMainWorld(origin_document)) {
       should_check_main_world_content_security_policy_ =
           kDoNotCheckContentSecurityPolicy;
@@ -99,7 +96,6 @@
         CreateUserGestureIndicator();
     FrameLoadRequest request(OriginDocument(), ResourceRequest(url_), "_self",
                              should_check_main_world_content_security_policy_);
-    request.SetReplacesCurrentItem(ReplacesCurrentItem());
     request.SetClientRedirect(ClientRedirectPolicy::kClientRedirect);
     if (!input_timestamp_.is_null()) {
       request.SetInputStartTime(input_timestamp_);
@@ -111,17 +107,20 @@
       request.SetBlobURLToken(std::move(token_clone));
     }
 
-    frame->Loader().StartNavigation(request);
+    frame->Loader().StartNavigation(request, frame_load_type_);
   }
 
   KURL Url() const override { return url_; }
 
+  WebFrameLoadType LoadType() const { return frame_load_type_; }
+
  private:
   KURL url_;
   mojom::blink::BlobURLTokenPtr blob_url_token_;
   ContentSecurityPolicyDisposition
       should_check_main_world_content_security_policy_;
   base::TimeTicks input_timestamp_;
+  WebFrameLoadType frame_load_type_;
 };
 
 class ScheduledRedirect final : public ScheduledURLNavigation {
@@ -130,9 +129,9 @@
                                    Document* origin_document,
                                    const KURL& url,
                                    Document::HttpRefreshType http_refresh_type,
-                                   bool replaces_current_item) {
+                                   WebFrameLoadType frame_load_type) {
     return new ScheduledRedirect(delay, origin_document, url, http_refresh_type,
-                                 replaces_current_item);
+                                 frame_load_type);
   }
 
   bool ShouldStartTimer(LocalFrame* frame) override {
@@ -143,8 +142,7 @@
     std::unique_ptr<UserGestureIndicator> gesture_indicator =
         CreateUserGestureIndicator();
     FrameLoadRequest request(OriginDocument(), ResourceRequest(Url()), "_self");
-    WebFrameLoadType load_type = WebFrameLoadType::kStandard;
-    request.SetReplacesCurrentItem(ReplacesCurrentItem());
+    WebFrameLoadType load_type = LoadType();
     if (EqualIgnoringFragmentIdentifier(frame->GetDocument()->Url(),
                                         request.GetResourceRequest().Url())) {
       request.GetResourceRequest().SetCacheMode(
@@ -173,12 +171,12 @@
                     Document* origin_document,
                     const KURL& url,
                     Document::HttpRefreshType http_refresh_type,
-                    bool replaces_current_item)
+                    WebFrameLoadType frame_load_type)
       : ScheduledURLNavigation(ToReason(http_refresh_type),
                                delay,
                                origin_document,
                                url,
-                               replaces_current_item,
+                               frame_load_type,
                                false,
                                base::TimeTicks()) {
     ClearUserGesture();
@@ -189,22 +187,22 @@
  public:
   static ScheduledFrameNavigation* Create(Document* origin_document,
                                           const KURL& url,
-                                          bool replaces_current_item,
+                                          WebFrameLoadType frame_load_type,
                                           base::TimeTicks input_timestamp) {
-    return new ScheduledFrameNavigation(origin_document, url,
-                                        replaces_current_item, input_timestamp);
+    return new ScheduledFrameNavigation(origin_document, url, frame_load_type,
+                                        input_timestamp);
   }
 
  private:
   ScheduledFrameNavigation(Document* origin_document,
                            const KURL& url,
-                           bool replaces_current_item,
+                           WebFrameLoadType frame_load_type,
                            base::TimeTicks input_timestamp)
       : ScheduledURLNavigation(Reason::kFrameNavigation,
                                0.0,
                                origin_document,
                                url,
-                               replaces_current_item,
+                               frame_load_type,
                                !url.ProtocolIsJavaScript(),
                                input_timestamp) {}
 };
@@ -239,7 +237,6 @@
       : ScheduledNavigation(Reason::kReload,
                             0.0,
                             nullptr /*origin_document */,
-                            true,
                             true),
         frame_(frame) {
     DCHECK(frame->GetDocument());
@@ -265,7 +262,6 @@
       : ScheduledNavigation(Reason::kPageBlock,
                             0.0,
                             origin_document,
-                            true,
                             true),
         reason_(reason) {}
 
@@ -276,9 +272,8 @@
  public:
   static ScheduledFormSubmission* Create(Document* document,
                                          FormSubmission* submission,
-                                         bool replaces_current_item) {
-    return new ScheduledFormSubmission(document, submission,
-                                       replaces_current_item);
+                                         WebFrameLoadType frame_load_type) {
+    return new ScheduledFormSubmission(document, submission, frame_load_type);
   }
 
   void Fire(LocalFrame* frame) override {
@@ -286,8 +281,7 @@
         CreateUserGestureIndicator();
     FrameLoadRequest frame_request =
         submission_->CreateFrameLoadRequest(OriginDocument());
-    frame_request.SetReplacesCurrentItem(ReplacesCurrentItem());
-    frame->Loader().StartNavigation(frame_request, WebFrameLoadType::kStandard,
+    frame->Loader().StartNavigation(frame_request, frame_load_type_,
                                     submission_->GetNavigationPolicy());
   }
 
@@ -301,20 +295,21 @@
  private:
   ScheduledFormSubmission(Document* document,
                           FormSubmission* submission,
-                          bool replaces_current_item)
+                          WebFrameLoadType frame_load_type)
       : ScheduledNavigation(submission->Method() == FormSubmission::kGetMethod
                                 ? Reason::kFormSubmissionGet
                                 : Reason::kFormSubmissionPost,
                             0,
                             document,
-                            replaces_current_item,
                             true),
-        submission_(submission) {
+        submission_(submission),
+        frame_load_type_(frame_load_type) {
     DCHECK_NE(submission->Method(), FormSubmission::kDialogMethod);
     DCHECK(submission_->Form());
   }
 
   Member<FormSubmission> submission_;
+  WebFrameLoadType frame_load_type_;
 };
 
 NavigationScheduler::NavigationScheduler(LocalFrame* frame) : frame_(frame) {}
@@ -373,8 +368,11 @@
 
   // We want a new back/forward list item if the refresh timeout is > 1 second.
   if (!redirect_ || delay <= redirect_->Delay()) {
+    WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
+    if (delay <= 1)
+      frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
     Schedule(ScheduledRedirect::Create(delay, frame_->GetDocument(), url,
-                                       http_refresh_type, delay <= 1));
+                                       http_refresh_type, frame_load_type));
   }
 }
 
@@ -396,9 +394,10 @@
          !ToLocalFrame(parent_frame)->Loader().AllAncestorsAreComplete();
 }
 
-void NavigationScheduler::ScheduleFrameNavigation(Document* origin_document,
-                                                  const KURL& url,
-                                                  bool replaces_current_item) {
+void NavigationScheduler::ScheduleFrameNavigation(
+    Document* origin_document,
+    const KURL& url,
+    WebFrameLoadType frame_load_type) {
   if (!ShouldScheduleNavigation(url))
     return;
 
@@ -407,8 +406,8 @@
     input_timestamp = input_event->TimeStamp();
   }
 
-  replaces_current_item =
-      replaces_current_item || MustReplaceCurrentItem(frame_);
+  if (MustReplaceCurrentItem(frame_))
+    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
 
   // If the URL we're going to navigate to is the same as the current one,
   // except for the fragment part, we don't need to schedule the location
@@ -419,17 +418,16 @@
     if (url.HasFragmentIdentifier() &&
         EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url)) {
       FrameLoadRequest request(origin_document, ResourceRequest(url), "_self");
-      request.SetReplacesCurrentItem(replaces_current_item);
       request.SetInputStartTime(input_timestamp);
-      if (replaces_current_item)
+      if (frame_load_type == WebFrameLoadType::kReplaceCurrentItem)
         request.SetClientRedirect(ClientRedirectPolicy::kClientRedirect);
-      frame_->Loader().StartNavigation(request);
+      frame_->Loader().StartNavigation(request, frame_load_type);
       return;
     }
   }
 
-  Schedule(ScheduledFrameNavigation::Create(
-      origin_document, url, replaces_current_item, input_timestamp));
+  Schedule(ScheduledFrameNavigation::Create(origin_document, url,
+                                            frame_load_type, input_timestamp));
 }
 
 void NavigationScheduler::SchedulePageBlock(Document* origin_document,
@@ -441,8 +439,11 @@
 void NavigationScheduler::ScheduleFormSubmission(Document* document,
                                                  FormSubmission* submission) {
   DCHECK(frame_->GetPage());
-  Schedule(ScheduledFormSubmission::Create(document, submission,
-                                           MustReplaceCurrentItem(frame_)));
+  WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
+  if (MustReplaceCurrentItem(frame_))
+    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
+  Schedule(
+      ScheduledFormSubmission::Create(document, submission, frame_load_type));
 }
 
 void NavigationScheduler::ScheduleReload() {
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.h b/third_party/blink/renderer/core/loader/navigation_scheduler.h
index 67cd23e4a..c270de2 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.h
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.h
@@ -37,6 +37,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
+#include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -65,9 +66,7 @@
   bool IsNavigationScheduledWithin(double interval_in_seconds) const;
 
   void ScheduleRedirect(double delay, const KURL&, Document::HttpRefreshType);
-  void ScheduleFrameNavigation(Document*,
-                               const KURL&,
-                               bool replaces_current_item = true);
+  void ScheduleFrameNavigation(Document*, const KURL&, WebFrameLoadType);
   void SchedulePageBlock(Document*, int reason);
   void ScheduleFormSubmission(Document*, FormSubmission*);
   void ScheduleReload();
diff --git a/third_party/blink/renderer/core/loader/scheduled_navigation.cc b/third_party/blink/renderer/core/loader/scheduled_navigation.cc
index 6b2b9e5..fc8768da 100644
--- a/third_party/blink/renderer/core/loader/scheduled_navigation.cc
+++ b/third_party/blink/renderer/core/loader/scheduled_navigation.cc
@@ -15,12 +15,10 @@
 ScheduledNavigation::ScheduledNavigation(Reason reason,
                                          double delay,
                                          Document* origin_document,
-                                         bool replaces_current_item,
                                          bool is_location_change)
     : reason_(reason),
       delay_(delay),
       origin_document_(origin_document),
-      replaces_current_item_(replaces_current_item),
       is_location_change_(is_location_change) {
   if (LocalFrame::HasTransientUserActivation(
           origin_document ? origin_document->GetFrame() : nullptr))
diff --git a/third_party/blink/renderer/core/loader/scheduled_navigation.h b/third_party/blink/renderer/core/loader/scheduled_navigation.h
index 82942e2..5d48eed 100644
--- a/third_party/blink/renderer/core/loader/scheduled_navigation.h
+++ b/third_party/blink/renderer/core/loader/scheduled_navigation.h
@@ -31,7 +31,6 @@
   ScheduledNavigation(Reason,
                       double delay,
                       Document* origin_document,
-                      bool replaces_current_item,
                       bool is_location_change);
   virtual ~ScheduledNavigation();
 
@@ -44,7 +43,6 @@
   Reason GetReason() const { return reason_; }
   double Delay() const { return delay_; }
   Document* OriginDocument() const { return origin_document_.Get(); }
-  bool ReplacesCurrentItem() const { return replaces_current_item_; }
   bool IsLocationChange() const { return is_location_change_; }
   std::unique_ptr<UserGestureIndicator> CreateUserGestureIndicator();
 
@@ -59,7 +57,6 @@
   Reason reason_;
   double delay_;
   Member<Document> origin_document_;
-  bool replaces_current_item_;
   bool is_location_change_;
   scoped_refptr<UserGestureToken> user_gesture_token_;
 
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index f3ab620..3d3f5683 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -451,10 +451,10 @@
     if (const WebInputEvent* input_event = CurrentInputEvent::Get()) {
       request.SetInputStartTime(input_event->TimeStamp());
     }
-    new_frame->Navigate(request);
+    new_frame->Navigate(request, WebFrameLoadType::kStandard);
   } else if (!url_string.IsEmpty()) {
     new_frame->ScheduleNavigation(*calling_window.document(), completed_url,
-                                  false,
+                                  WebFrameLoadType::kStandard,
                                   has_user_gesture ? UserGestureStatus::kActive
                                                    : UserGestureStatus::kNone);
   }
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index a7c06afc..7b0d5eb 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -299,7 +299,8 @@
           SecurityOrigin::Create(KURL(drag_data->AsURL())));
       resource_request.SetHasUserGesture(LocalFrame::HasTransientUserActivation(
           document_under_mouse_ ? document_under_mouse_->GetFrame() : nullptr));
-      page_->MainFrame()->Navigate(FrameLoadRequest(nullptr, resource_request));
+      page_->MainFrame()->Navigate(FrameLoadRequest(nullptr, resource_request),
+                                   WebFrameLoadType::kStandard);
     }
 
     // TODO(bokan): This case happens when we end a URL drag inside a guest
diff --git a/third_party/blink/renderer/core/paint/clip_path_clipper.cc b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
index 54e5ce3..6816f59 100644
--- a/third_party/blink/renderer/core/paint/clip_path_clipper.cc
+++ b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
@@ -71,14 +71,7 @@
     return FloatRect(ToLayoutBox(object).BorderBoxRect());
 
   SECURITY_DCHECK(object.IsLayoutInline());
-  const LayoutInline& layout_inline = ToLayoutInline(object);
-  // This somewhat convoluted computation matches what Gecko does.
-  // See crbug.com/641907.
-  LayoutRect inline_b_box = layout_inline.LinesBoundingBox();
-  const InlineFlowBox* flow_box = layout_inline.FirstLineBox();
-  inline_b_box.SetHeight(flow_box ? flow_box->FrameRect().Height()
-                                  : LayoutUnit(0));
-  return FloatRect(inline_b_box);
+  return FloatRect(ToLayoutInline(object).ReferenceBoxForClipPath());
 }
 
 base::Optional<FloatRect> ClipPathClipper::LocalClipPathBoundingBox(
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index 5d6f2c2..ad3a5a1c 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -11,14 +11,14 @@
 #include "third_party/blink/public/platform/dedicated_worker_factory.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_layer_tree_view.h"
-#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/post_message_helper.h"
 #include "third_party/blink/renderer/core/core_initializer.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
-#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
 #include "third_party/blink/renderer/core/messaging/post_message_options.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
@@ -257,9 +257,8 @@
 
   std::unique_ptr<WebContentSettingsClient> client;
   if (GetExecutionContext()->IsDocument()) {
-    WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(
-        ToDocument(GetExecutionContext())->GetFrame());
-    client = web_frame->Client()->CreateWorkerContentSettingsClient();
+    LocalFrame* frame = ToDocument(GetExecutionContext())->GetFrame();
+    client = frame->Client()->CreateWorkerContentSettingsClient();
   } else if (GetExecutionContext()->IsWorkerGlobalScope()) {
     WebContentSettingsClient* web_worker_content_settings_client =
         WorkerContentSettingsClient::From(*GetExecutionContext())
diff --git a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
index 4dc16a5..920d9cf 100644
--- a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
+++ b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
@@ -7,11 +7,11 @@
 #include "base/synchronization/waitable_event.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
-#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
-#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
@@ -65,15 +65,13 @@
 
   std::unique_ptr<WebWorkerFetchContext> web_worker_fetch_context;
   if (execution_context_->IsDocument()) {
-    // |web_frame| is null in some unit tests.
-    if (WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(
-            ToDocument(GetExecutionContext())->GetFrame())) {
-      web_worker_fetch_context =
-          web_frame->Client()->CreateWorkerFetchContext();
-      DCHECK(web_worker_fetch_context);
+    LocalFrame* frame = ToDocument(GetExecutionContext())->GetFrame();
+    web_worker_fetch_context = frame->Client()->CreateWorkerFetchContext();
+    // |web_worker_fetch_context| is null in some unit tests.
+    if (web_worker_fetch_context) {
       web_worker_fetch_context->SetApplicationCacheHostID(
           GetExecutionContext()->Fetcher()->Context().ApplicationCacheHostID());
-      web_worker_fetch_context->SetIsOnSubframe(web_frame != web_frame->Top());
+      web_worker_fetch_context->SetIsOnSubframe(!frame->IsMainFrame());
     }
   } else if (execution_context_->IsWorkerGlobalScope()) {
     web_worker_fetch_context =
diff --git a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
index 55888d9e..36b0565 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
@@ -2107,7 +2107,7 @@
       case 'ArrowDown':
       case 'PageUp':
       case 'PageDown':
-        if (this._handleNameOrValueUpDown(event)) {
+        if (!this.isSuggestBoxVisible() && this._handleNameOrValueUpDown(event)) {
           event.preventDefault();
           return;
         }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TextPrompt.js b/third_party/blink/renderer/devtools/front_end/ui/TextPrompt.js
index 30f5e79a..f3305be 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/TextPrompt.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/TextPrompt.js
@@ -274,7 +274,7 @@
    */
   onKeyDown(event) {
     let handled = false;
-    if (this._isSuggestBoxVisible() && this._suggestBox.keyPressed(event)) {
+    if (this.isSuggestBoxVisible() && this._suggestBox.keyPressed(event)) {
       event.consume(true);
       return;
     }
@@ -299,7 +299,7 @@
           this.clearAutocomplete();
         break;
       case 'Escape':
-        if (this._isSuggestBoxVisible()) {
+        if (this.isSuggestBoxVisible()) {
           this.clearAutocomplete();
           handled = true;
         }
@@ -338,7 +338,7 @@
    */
   acceptAutoComplete() {
     let result = false;
-    if (this._isSuggestBoxVisible())
+    if (this.isSuggestBoxVisible())
       result = this._suggestBox.acceptSuggestion();
     if (!result)
       result = this._acceptSuggestionInternal();
@@ -349,7 +349,7 @@
   clearAutocomplete() {
     const beforeText = this.textWithCurrentSuggestion();
 
-    if (this._isSuggestBoxVisible())
+    if (this.isSuggestBoxVisible())
       this._suggestBox.hide();
     this._clearAutocompleteTimeout();
     this._queryRange = null;
@@ -382,7 +382,7 @@
    * @param {boolean=} force
    */
   autoCompleteSoon(force) {
-    const immediately = this._isSuggestBoxVisible() || force;
+    const immediately = this.isSuggestBoxVisible() || force;
     if (!this._completeTimeout) {
       this._completeTimeout =
           setTimeout(this.complete.bind(this, force), immediately ? 0 : this._autocompletionTimeout);
@@ -401,7 +401,7 @@
 
     let shouldExit;
 
-    if (!force && !this._isCaretAtEndOfPrompt() && !this._isSuggestBoxVisible())
+    if (!force && !this._isCaretAtEndOfPrompt() && !this.isSuggestBoxVisible())
       shouldExit = true;
     else if (!selection.isCollapsed)
       shouldExit = true;
@@ -565,9 +565,10 @@
   }
 
   /**
+   * @protected
    * @return {boolean}
    */
-  _isSuggestBoxVisible() {
+  isSuggestBoxVisible() {
     return this._suggestBox && this._suggestBox.visible();
   }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
index 664ad27..4bc852f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
@@ -195,45 +195,31 @@
     ax::mojom::DescriptionFrom& description_from,
     AXObjectVector* description_objects) const {
   switch (ControlType()) {
-    case kMediaEnterFullscreenButton:
-      return QueryString(WebLocalizedString::kAXMediaEnterFullscreenButtonHelp);
-    case kMediaExitFullscreenButton:
-      return QueryString(WebLocalizedString::kAXMediaExitFullscreenButtonHelp);
-    case kMediaMuteButton:
-      return QueryString(WebLocalizedString::kAXMediaMuteButtonHelp);
-    case kMediaPlayButton:
-      return QueryString(WebLocalizedString::kAXMediaPlayButtonHelp);
-    case kMediaUnMuteButton:
-      return QueryString(WebLocalizedString::kAXMediaUnMuteButtonHelp);
-    case kMediaPauseButton:
-      return QueryString(WebLocalizedString::kAXMediaPauseButtonHelp);
     case kMediaCurrentTimeDisplay:
       return QueryString(WebLocalizedString::kAXMediaCurrentTimeDisplayHelp);
     case kMediaTimeRemainingDisplay:
       return QueryString(WebLocalizedString::kAXMediaTimeRemainingDisplayHelp);
-    case kMediaShowClosedCaptionsButton:
-      return QueryString(
-          WebLocalizedString::kAXMediaShowClosedCaptionsButtonHelp);
-    case kMediaHideClosedCaptionsButton:
-      return QueryString(
-          WebLocalizedString::kAXMediaHideClosedCaptionsButtonHelp);
-    case kMediaCastOffButton:
-    case kMediaOverlayCastOffButton:
-      return QueryString(WebLocalizedString::kAXMediaCastOffButtonHelp);
-    case kMediaCastOnButton:
-    case kMediaOverlayCastOnButton:
-      return QueryString(WebLocalizedString::kAXMediaCastOnButtonHelp);
     case kMediaOverflowButton:
       return QueryString(WebLocalizedString::kAXMediaOverflowButtonHelp);
-    case kMediaEnterPictureInPictureButton:
-      return QueryString(
-          WebLocalizedString::kAXMediaEnterPictureInPictureButtonHelp);
-    case kMediaExitPictureInPictureButton:
-      return QueryString(
-          WebLocalizedString::kAXMediaExitPictureInPictureButtonHelp);
+    // The following descriptions are repeats of their respective titles. When
+    // read by accessibility, we get the same thing said twice, with no value
+    // added. So instead, we just return an empty string.
+    case kMediaEnterFullscreenButton:
+    case kMediaExitFullscreenButton:
     case kMediaDisplayCutoutFullscreenButton:
-      return QueryString(
-          WebLocalizedString::kAXMediaDisplayCutoutFullscreenButtonHelp);
+    case kMediaMuteButton:
+    case kMediaUnMuteButton:
+    case kMediaPlayButton:
+    case kMediaPauseButton:
+    case kMediaShowClosedCaptionsButton:
+    case kMediaHideClosedCaptionsButton:
+    case kMediaCastOffButton:
+    case kMediaOverlayCastOffButton:
+    case kMediaCastOnButton:
+    case kMediaOverlayCastOnButton:
+    case kMediaEnterPictureInPictureButton:
+    case kMediaExitPictureInPictureButton:
+      return "";
     case kMediaSliderThumb:
     case kMediaTextTrackList:
     case kMediaTimelineContainer:
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index b1b6e85..dc4b6094 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -31,6 +31,7 @@
     "modules/notifications/Notification",
     "modules/payments/PaymentRequest",
     "modules/peerconnection/RTCIceTransport",
+    "modules/peerconnection/RTCQuicStream",
     "modules/peerconnection/RTCQuicTransport",
     "modules/permissions/PermissionStatus",
     "modules/picture_in_picture/HTMLVideoElementPictureInPicture",
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index eb7effe..763c8494 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -228,6 +228,7 @@
           "peerconnection/rtc_peer_connection.idl",
           "peerconnection/rtc_peer_connection_ice_event.idl",
           "peerconnection/rtc_quic_stream.idl",
+          "peerconnection/rtc_quic_stream_event.idl",
           "peerconnection/rtc_quic_transport.idl",
           "peerconnection/rtc_rtp_contributing_source.idl",
           "peerconnection/rtc_rtp_receiver.idl",
@@ -614,6 +615,7 @@
           "peerconnection/rtc_offer_options.idl",
           "peerconnection/rtc_peer_connection_ice_event_init.idl",
           "peerconnection/rtc_quic_parameters.idl",
+          "peerconnection/rtc_quic_stream_event_init.idl",
           "peerconnection/rtc_rtcp_parameters.idl",
           "peerconnection/rtc_rtp_capabilities.idl",
           "peerconnection/rtc_rtp_codec_capability.idl",
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 25b1e5f..cbdac9c6 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -1222,7 +1222,8 @@
 
   if (retry_resolver_) {
     DCHECK(payment_response_);
-    payment_response_->Update(std::move(response), shipping_address_.Get());
+    payment_response_->Update(retry_resolver_->GetScriptState(),
+                              std::move(response), shipping_address_.Get());
     retry_resolver_->Resolve();
 
     // Do not close the mojo connection here. The merchant website should call
@@ -1230,9 +1231,9 @@
     // connection to display a success or failure message to the user.
     retry_resolver_.Clear();
   } else if (accept_resolver_) {
-    payment_response_ =
-        new PaymentResponse(GetExecutionContext(), std::move(response),
-                            shipping_address_.Get(), this, id_);
+    payment_response_ = new PaymentResponse(accept_resolver_->GetScriptState(),
+                                            std::move(response),
+                                            shipping_address_.Get(), this, id_);
     accept_resolver_->Resolve(payment_response_);
 
     // Do not close the mojo connection here. The merchant website should call
diff --git a/third_party/blink/renderer/modules/payments/payment_response.cc b/third_party/blink/renderer/modules/payments/payment_response.cc
index 31811e8a..68f5ae9 100644
--- a/third_party/blink/renderer/modules/payments/payment_response.cc
+++ b/third_party/blink/renderer/modules/payments/payment_response.cc
@@ -10,20 +10,20 @@
 #include "third_party/blink/renderer/modules/payments/payment_state_resolver.h"
 #include "third_party/blink/renderer/modules/payments/payment_validation_errors.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
 namespace blink {
 
 PaymentResponse::PaymentResponse(
-    ExecutionContext* execution_context,
+    ScriptState* script_state,
     payments::mojom::blink::PaymentResponsePtr response,
     PaymentAddress* shipping_address,
     PaymentStateResolver* payment_state_resolver,
-    const String& requestId)
-    : ContextLifecycleObserver(execution_context),
-      requestId_(requestId),
+    const String& request_id)
+    : ContextLifecycleObserver(ExecutionContext::From(script_state)),
+      request_id_(request_id),
       method_name_(response->method_name),
-      stringified_details_(response->stringified_details),
       shipping_address_(shipping_address),
       shipping_option_(response->shipping_option),
       payer_name_(response->payer->name),
@@ -31,22 +31,24 @@
       payer_phone_(response->payer->phone),
       payment_state_resolver_(payment_state_resolver) {
   DCHECK(payment_state_resolver_);
+  UpdateDetailsFromJSON(script_state, response->stringified_details);
 }
 
 PaymentResponse::~PaymentResponse() = default;
 
 void PaymentResponse::Update(
+    ScriptState* script_state,
     payments::mojom::blink::PaymentResponsePtr response,
     PaymentAddress* shipping_address) {
   DCHECK(response);
   DCHECK(response->payer);
   method_name_ = response->method_name;
-  stringified_details_ = response->stringified_details;
   shipping_address_ = shipping_address;
   shipping_option_ = response->shipping_option;
   payer_name_ = response->payer->name;
   payer_email_ = response->payer->email;
   payer_phone_ = response->payer->phone;
+  UpdateDetailsFromJSON(script_state, response->stringified_details);
 }
 
 void PaymentResponse::UpdatePayerDetail(
@@ -57,11 +59,33 @@
   payer_phone_ = detail->phone;
 }
 
+void PaymentResponse::UpdateDetailsFromJSON(ScriptState* script_state,
+                                            const String& json) {
+  ScriptState::Scope scope(script_state);
+  if (json.IsEmpty()) {
+    details_ = V8ObjectBuilder(script_state).GetScriptValue();
+    return;
+  }
+
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kConstructionContext,
+                                 "PaymentResponse");
+  v8::Local<v8::Value> parsed_value =
+      FromJSONString(script_state->GetIsolate(), script_state->GetContext(),
+                     json, exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    details_ = V8ObjectBuilder(script_state).GetScriptValue();
+    return;
+  }
+  details_ = ScriptValue(script_state, parsed_value);
+}
+
 ScriptValue PaymentResponse::toJSONForBinding(ScriptState* script_state) const {
   V8ObjectBuilder result(script_state);
   result.AddString("requestId", requestId());
   result.AddString("methodName", methodName());
-  result.Add("details", details(script_state, ASSERT_NO_EXCEPTION));
+  result.Add("details", details(script_state));
 
   if (shippingAddress())
     result.Add("shippingAddress",
@@ -77,12 +101,8 @@
   return result.GetScriptValue();
 }
 
-ScriptValue PaymentResponse::details(ScriptState* script_state,
-                                     ExceptionState& exception_state) const {
-  return ScriptValue(
-      script_state,
-      FromJSONString(script_state->GetIsolate(), script_state->GetContext(),
-                     stringified_details_, exception_state));
+ScriptValue PaymentResponse::details(ScriptState* script_state) const {
+  return ScriptValue(script_state, details_.V8ValueFor(script_state));
 }
 
 ScriptPromise PaymentResponse::complete(ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/payments/payment_response.h b/third_party/blink/renderer/modules/payments/payment_response.h
index 91fd235..f20071d 100644
--- a/third_party/blink/renderer/modules/payments/payment_response.h
+++ b/third_party/blink/renderer/modules/payments/payment_response.h
@@ -20,7 +20,6 @@
 
 namespace blink {
 
-class ExceptionState;
 class PaymentAddress;
 class PaymentStateResolver;
 class PaymentValidationErrors;
@@ -35,21 +34,24 @@
   WTF_MAKE_NONCOPYABLE(PaymentResponse);
 
  public:
-  PaymentResponse(ExecutionContext*,
-                  payments::mojom::blink::PaymentResponsePtr,
-                  PaymentAddress* shipping_address_,
-                  PaymentStateResolver*,
-                  const String& requestId);
+  PaymentResponse(ScriptState* script_state,
+                  payments::mojom::blink::PaymentResponsePtr response,
+                  PaymentAddress* shipping_address,
+                  PaymentStateResolver* payment_state_resolver,
+                  const String& request_id);
   ~PaymentResponse() override;
 
-  void Update(payments::mojom::blink::PaymentResponsePtr, PaymentAddress*);
+  void Update(ScriptState* script_state,
+              payments::mojom::blink::PaymentResponsePtr response,
+              PaymentAddress* shipping_address);
   void UpdatePayerDetail(payments::mojom::blink::PayerDetailPtr);
+  void UpdateDetailsFromJSON(ScriptState* script_state, const String& json);
 
   ScriptValue toJSONForBinding(ScriptState*) const;
 
-  const String& requestId() const { return requestId_; }
+  const String& requestId() const { return request_id_; }
   const String& methodName() const { return method_name_; }
-  ScriptValue details(ScriptState*, ExceptionState&) const;
+  ScriptValue details(ScriptState* script_state) const;
   PaymentAddress* shippingAddress() const { return shipping_address_.Get(); }
   const String& shippingOption() const { return shipping_option_; }
   const String& payerName() const { return payer_name_; }
@@ -69,9 +71,9 @@
   void Trace(blink::Visitor*) override;
 
  private:
-  String requestId_;
+  String request_id_;
   String method_name_;
-  String stringified_details_;
+  ScriptValue details_;
   Member<PaymentAddress> shipping_address_;
   String shipping_option_;
   String payer_name_;
diff --git a/third_party/blink/renderer/modules/payments/payment_response.idl b/third_party/blink/renderer/modules/payments/payment_response.idl
index 39ecfc0..31096ecec 100644
--- a/third_party/blink/renderer/modules/payments/payment_response.idl
+++ b/third_party/blink/renderer/modules/payments/payment_response.idl
@@ -22,7 +22,7 @@
 
     readonly attribute DOMString requestId;
     readonly attribute DOMString methodName;
-    [CallWith=ScriptState, RaisesException] readonly attribute object details;
+    [CallWith=ScriptState] readonly attribute object details;
     readonly attribute PaymentAddress? shippingAddress;
     readonly attribute DOMString? shippingOption;
     readonly attribute DOMString? payerName;
diff --git a/third_party/blink/renderer/modules/payments/payment_response_test.cc b/third_party/blink/renderer/modules/payments/payment_response_test.cc
index 95c2a276..5f1b4d995 100644
--- a/third_party/blink/renderer/modules/payments/payment_response_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_response_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/modules/payments/payment_address.h"
 #include "third_party/blink/renderer/modules/payments/payment_state_resolver.h"
 #include "third_party/blink/renderer/modules/payments/payment_test_helper.h"
@@ -59,8 +60,8 @@
   MockPaymentStateResolver* complete_callback = new MockPaymentStateResolver;
 
   PaymentResponse* output =
-      new PaymentResponse(scope.GetExecutionContext(), std::move(input),
-                          nullptr, complete_callback, "id");
+      new PaymentResponse(scope.GetScriptState(), std::move(input), nullptr,
+                          complete_callback, "id");
 
   EXPECT_EQ("foo", output->methodName());
   EXPECT_EQ("standardShippingOption", output->shippingOption());
@@ -69,8 +70,7 @@
   EXPECT_EQ("0123", output->payerPhone());
   EXPECT_EQ("id", output->requestId());
 
-  ScriptValue details =
-      output->details(scope.GetScriptState(), scope.GetExceptionState());
+  ScriptValue details = output->details(scope.GetScriptState());
 
   ASSERT_FALSE(scope.GetExceptionState().HadException());
   ASSERT_TRUE(details.V8Value()->IsObject());
@@ -84,20 +84,41 @@
   EXPECT_EQ(123, transaction_id.V8Value().As<v8::Number>()->Value());
 }
 
-TEST(PaymentResponseTest, PaymentResponseDetailsJSONObject) {
+TEST(PaymentResponseTest,
+     PaymentResponseDetailsWithUnexpectedJSONFormatString) {
   V8TestingScope scope;
   payments::mojom::blink::PaymentResponsePtr input =
       BuildPaymentResponseForTest();
   input->stringified_details = "transactionId";
   MockPaymentStateResolver* complete_callback = new MockPaymentStateResolver;
   PaymentResponse* output =
-      new PaymentResponse(scope.GetExecutionContext(), std::move(input),
-                          nullptr, complete_callback, "id");
+      new PaymentResponse(scope.GetScriptState(), std::move(input), nullptr,
+                          complete_callback, "id");
 
-  ScriptValue details =
-      output->details(scope.GetScriptState(), scope.GetExceptionState());
+  ScriptValue details = output->details(scope.GetScriptState());
+  ASSERT_TRUE(details.V8Value()->IsObject());
 
-  ASSERT_TRUE(scope.GetExceptionState().HadException());
+  String stringified_details = ToBlinkString<String>(
+      v8::JSON::Stringify(scope.GetContext(),
+                          details.V8Value().As<v8::Object>())
+          .ToLocalChecked(),
+      kDoNotExternalize);
+
+  EXPECT_EQ("{}", stringified_details);
+}
+
+TEST(PaymentResponseTest, PaymentResponseDetailsRetrunsTheSameObject) {
+  V8TestingScope scope;
+  payments::mojom::blink::PaymentResponsePtr input =
+      BuildPaymentResponseForTest();
+  input->method_name = "foo";
+  input->stringified_details = "{\"transactionId\": 123}";
+  MockPaymentStateResolver* complete_callback = new MockPaymentStateResolver;
+  PaymentResponse* output =
+      new PaymentResponse(scope.GetScriptState(), std::move(input), nullptr,
+                          complete_callback, "id");
+  EXPECT_EQ(output->details(scope.GetScriptState()),
+            output->details(scope.GetScriptState()));
 }
 
 TEST(PaymentResponseTest, CompleteCalledWithSuccess) {
@@ -108,8 +129,8 @@
   input->stringified_details = "{\"transactionId\": 123}";
   MockPaymentStateResolver* complete_callback = new MockPaymentStateResolver;
   PaymentResponse* output =
-      new PaymentResponse(scope.GetExecutionContext(), std::move(input),
-                          nullptr, complete_callback, "id");
+      new PaymentResponse(scope.GetScriptState(), std::move(input), nullptr,
+                          complete_callback, "id");
 
   EXPECT_CALL(*complete_callback,
               Complete(scope.GetScriptState(), PaymentStateResolver::kSuccess));
@@ -125,8 +146,8 @@
   input->stringified_details = "{\"transactionId\": 123}";
   MockPaymentStateResolver* complete_callback = new MockPaymentStateResolver;
   PaymentResponse* output =
-      new PaymentResponse(scope.GetExecutionContext(), std::move(input),
-                          nullptr, complete_callback, "id");
+      new PaymentResponse(scope.GetScriptState(), std::move(input), nullptr,
+                          complete_callback, "id");
 
   EXPECT_CALL(*complete_callback,
               Complete(scope.GetScriptState(), PaymentStateResolver::kFail));
@@ -155,8 +176,8 @@
       new PaymentAddress(std::move(input->shipping_address));
 
   PaymentResponse* output =
-      new PaymentResponse(scope.GetExecutionContext(), std::move(input),
-                          address, new MockPaymentStateResolver, "id");
+      new PaymentResponse(scope.GetScriptState(), std::move(input), address,
+                          new MockPaymentStateResolver, "id");
   ScriptValue json_object = output->toJSONForBinding(scope.GetScriptState());
   EXPECT_TRUE(json_object.IsObject());
 
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 4f543a3..0592dce0 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -24,6 +24,10 @@
     "adapters/p2p_quic_transport_factory_impl.h",
     "adapters/p2p_quic_transport_impl.cc",
     "adapters/p2p_quic_transport_impl.h",
+    "adapters/quic_stream_host.cc",
+    "adapters/quic_stream_host.h",
+    "adapters/quic_stream_proxy.cc",
+    "adapters/quic_stream_proxy.h",
     "adapters/quic_transport_host.cc",
     "adapters/quic_transport_host.h",
     "adapters/quic_transport_proxy.cc",
@@ -54,6 +58,8 @@
     "rtc_peer_connection_ice_event.h",
     "rtc_quic_stream.cc",
     "rtc_quic_stream.h",
+    "rtc_quic_stream_event.cc",
+    "rtc_quic_stream_event.h",
     "rtc_quic_transport.cc",
     "rtc_quic_transport.h",
     "rtc_rtp_contributing_source.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
index 5db8163..495e9605 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.cc
@@ -14,10 +14,14 @@
 
 IceTransportHost::IceTransportHost(
     scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
     base::WeakPtr<IceTransportProxy> proxy)
-    : proxy_thread_(std::move(proxy_thread)), proxy_(std::move(proxy)) {
+    : proxy_thread_(std::move(proxy_thread)),
+      host_thread_(std::move(host_thread)),
+      proxy_(std::move(proxy)) {
   DETACH_FROM_THREAD(thread_checker_);
   DCHECK(proxy_thread_);
+  DCHECK(host_thread_);
   DCHECK(proxy_);
 }
 
@@ -33,6 +37,18 @@
   transport_ = adapter_factory->ConstructOnWorkerThread(this);
 }
 
+scoped_refptr<base::SingleThreadTaskRunner> IceTransportHost::proxy_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return proxy_thread_;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> IceTransportHost::host_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return host_thread_;
+}
+
 void IceTransportHost::StartGathering(
     const cricket::IceParameters& local_parameters,
     const cricket::ServerAddresses& stun_servers,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
index 08ac5458..63fe3195 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h
@@ -42,12 +42,16 @@
 class IceTransportHost final : public IceTransportAdapter::Delegate {
  public:
   IceTransportHost(scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
+                   scoped_refptr<base::SingleThreadTaskRunner> host_thread,
                    base::WeakPtr<IceTransportProxy> proxy);
   ~IceTransportHost() override;
 
   void Initialize(
       std::unique_ptr<IceTransportAdapterCrossThreadFactory> adapter_factory);
 
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
+  scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
+
   void StartGathering(
       const cricket::IceParameters& local_parameters,
       const cricket::ServerAddresses& stun_servers,
@@ -75,6 +79,7 @@
   void OnStateChanged(cricket::IceTransportState new_state) override;
 
   const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
+  const scoped_refptr<base::SingleThreadTaskRunner> host_thread_;
   std::unique_ptr<IceTransportAdapter> transport_;
   base::WeakPtr<IceTransportProxy> proxy_;
   QuicTransportHost* consumer_host_ = nullptr;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
index 3ff6fd7..9a575b79 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.cc
@@ -33,8 +33,8 @@
   // The IceTransportHost is constructed on the proxy thread but should only be
   // interacted with via PostTask to the host thread. The OnTaskRunnerDeleter
   // (configured above) will ensure it gets deleted on the host thread.
-  host_.reset(
-      new IceTransportHost(proxy_thread_, weak_ptr_factory_.GetWeakPtr()));
+  host_.reset(new IceTransportHost(proxy_thread_, host_thread_,
+                                   weak_ptr_factory_.GetWeakPtr()));
   PostCrossThreadTask(*host_thread_, FROM_HERE,
                       CrossThreadBind(&IceTransportHost::Initialize,
                                       CrossThreadUnretained(host_.get()),
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
new file mode 100644
index 0000000..71545c64
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
@@ -0,0 +1,88 @@
+// 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/modules/peerconnection/adapters/quic_stream_host.h"
+
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+QuicStreamHost::QuicStreamHost() {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+QuicStreamHost::~QuicStreamHost() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void QuicStreamHost::set_proxy(base::WeakPtr<QuicStreamProxy> stream_proxy) {
+  DETACH_FROM_THREAD(thread_checker_);
+  stream_proxy_ = stream_proxy;
+}
+
+void QuicStreamHost::Initialize(QuicTransportHost* transport_host,
+                                P2PQuicStream* p2p_stream) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(transport_host);
+  DCHECK(p2p_stream);
+  transport_host_ = transport_host;
+  p2p_stream_ = p2p_stream;
+  p2p_stream_->SetDelegate(this);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> QuicStreamHost::proxy_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(transport_host_);
+  return transport_host_->proxy_thread();
+}
+
+void QuicStreamHost::Reset() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(p2p_stream_);
+  p2p_stream_->Reset();
+  Delete();
+}
+
+void QuicStreamHost::Finish() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(p2p_stream_);
+  p2p_stream_->Finish();
+  writeable_ = false;
+  if (!readable_ && !writeable_) {
+    Delete();
+  }
+}
+
+void QuicStreamHost::OnRemoteReset() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  PostCrossThreadTask(
+      *proxy_thread(), FROM_HERE,
+      CrossThreadBind(&QuicStreamProxy::OnRemoteReset, stream_proxy_));
+  Delete();
+}
+
+void QuicStreamHost::OnRemoteFinish() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  PostCrossThreadTask(
+      *proxy_thread(), FROM_HERE,
+      CrossThreadBind(&QuicStreamProxy::OnRemoteFinish, stream_proxy_));
+  readable_ = false;
+  if (!readable_ && !writeable_) {
+    Delete();
+  }
+}
+
+void QuicStreamHost::Delete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(transport_host_);
+  // OnRemoveStream will delete |this|.
+  transport_host_->OnRemoveStream(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h
new file mode 100644
index 0000000..baab847
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h
@@ -0,0 +1,86 @@
+// 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_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_HOST_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h"
+
+namespace blink {
+
+class QuicStreamProxy;
+class QuicTransportHost;
+
+// This class is the host side correspondent to the QuicStreamProxy. See the
+// QuicStreamProxy documentation for background. This class lives on the host
+// thread and proxies calls between the QuicStreamProxy and the P2PQuicStream
+// (which is single-threaded).
+//
+// The QuicStreamHost is owned by the QuicTransportHost and constructed when
+// either a new local QUIC stream is created or when a remote QUIC stream has
+// been created. The stream host will be deleted in the following circumstances:
+// 1) Reset() is called.
+// 2) OnRemoteReset() is indicated.
+// 3) Finish() and OnRemoteFinish() have been called.
+// The QuicStreamHost will instruct the QuicTransportHost to delete it when any
+// condition has been met.
+//
+// Since the QuicStreamHost can be constructed from either the proxy or host
+// thread, initialization happens in three steps:
+// 1) QuicStreamHost is constructed.
+// 2) set_proxy is called when a WeakPtr to the corresponding proxy-thread
+//    object.
+// 3) Initialize is called on the host thread.
+class QuicStreamHost final : public base::SupportsWeakPtr<QuicStreamHost>,
+                             public P2PQuicStream::Delegate {
+ public:
+  QuicStreamHost();
+  ~QuicStreamHost() override;
+
+  // Sets a WeakPtr to the corresponding QuicStreamProxy. This is valid on
+  // either the proxy or host thread. Should happen right after construction.
+  void set_proxy(base::WeakPtr<QuicStreamProxy> stream_proxy);
+
+  // Initializes the QuicStreamHost. Must be called on the host thread.
+  // |transport_host| must outlive this object.
+  void Initialize(QuicTransportHost* transport_host, P2PQuicStream* p2p_stream);
+
+  // The remaining methods can only be called from the host thread and must be
+  // preceded by Initialize().
+
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
+
+  void Reset();
+  void Finish();
+
+ private:
+  // Instruct the QuicTransportHost to remove and delete this stream host.
+  void Delete();
+
+  // P2PQuicStream::Delegate overrides.
+  void OnRemoteReset() override;
+  void OnRemoteFinish() override;
+
+  // Up reference. Owned by QuicTransportProxy.
+  QuicTransportHost* transport_host_ = nullptr;
+  // Forward reference. Owned by P2PQuicTransport.
+  P2PQuicStream* p2p_stream_ = nullptr;
+  // Back reference. Owned by QuicTransportProxy.
+  base::WeakPtr<QuicStreamProxy> stream_proxy_;
+
+  // |readable_| transitions to false when OnRemoteFinish() is called.
+  bool readable_ = true;
+  // |writeable_| transitions to false when Finish() is called.
+  bool writeable_ = true;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_HOST_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.cc
new file mode 100644
index 0000000..e50ebf09
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.cc
@@ -0,0 +1,91 @@
+// 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/modules/peerconnection/adapters/quic_stream_proxy.h"
+
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+QuicStreamProxy::QuicStreamProxy() {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+QuicStreamProxy::~QuicStreamProxy() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void QuicStreamProxy::set_host(base::WeakPtr<QuicStreamHost> stream_host) {
+  DETACH_FROM_THREAD(thread_checker_);
+  stream_host_ = stream_host;
+}
+
+void QuicStreamProxy::Initialize(QuicTransportProxy* transport_proxy) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(transport_proxy);
+  transport_proxy_ = transport_proxy;
+}
+
+void QuicStreamProxy::set_delegate(Delegate* delegate) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegate);
+  delegate_ = delegate;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> QuicStreamProxy::host_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(transport_proxy_);
+  return transport_proxy_->host_thread();
+}
+
+void QuicStreamProxy::Reset() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  PostCrossThreadTask(*host_thread(), FROM_HERE,
+                      CrossThreadBind(&QuicStreamHost::Reset, stream_host_));
+  Delete();
+}
+
+void QuicStreamProxy::Finish() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  PostCrossThreadTask(*host_thread(), FROM_HERE,
+                      CrossThreadBind(&QuicStreamHost::Finish, stream_host_));
+  writeable_ = false;
+  if (!readable_ && !writeable_) {
+    Delete();
+  }
+}
+
+void QuicStreamProxy::OnRemoteReset() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegate_);
+  // Need to copy the |delegate_| member since Delete() will destroy |this|.
+  Delegate* delegate_copy = delegate_;
+  Delete();
+  delegate_copy->OnRemoteReset();
+}
+
+void QuicStreamProxy::OnRemoteFinish() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegate_);
+  // Need to copy the |delegate_| member since Delete() will destroy |this|.
+  Delegate* delegate_copy = delegate_;
+  readable_ = false;
+  if (!readable_ && !writeable_) {
+    Delete();
+  }
+  delegate_copy->OnRemoteFinish();
+}
+
+void QuicStreamProxy::Delete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  // OnRemoveStream will delete |this|.
+  transport_proxy_->OnRemoveStream(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h
new file mode 100644
index 0000000..0d059f6
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h
@@ -0,0 +1,98 @@
+// 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_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_PROXY_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_PROXY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+
+namespace blink {
+
+class QuicStreamHost;
+class QuicTransportProxy;
+
+// This class allows interactions with a QUIC stream that runs on a thread
+// different from which it is controlled. All interactions with the QUIC
+// implementation happen asynchronously.
+//
+// The QuicStreamProxy is owned by the QuicTransportProxy and constructed when
+// either a new local QUIC stream is created or when a remote QUIC stream has
+// been created. The stream proxy will be deleted in the following
+// circumstances:
+// 1) Reset() is called.
+// 2) OnRemoteReset() is indicated.
+// 3) Finish() and OnRemoteFinish() have been called.
+// The client is responsible for knowing when any of these conditions have been
+// met and clearing its reference accordingly.
+//
+// Since the QuicStreamProxy can be constructed from either the proxy or host
+// thread, initialization happens in four steps:
+// 1) QuicStreamProxy is constructed.
+// 2) set_host is called with a WeakPtr to the corresponding host-thread object.
+// 3) Initialize is called on the proxy thread.
+// 4) set_delegate is called on the proxy thread.
+class QuicStreamProxy final : public base::SupportsWeakPtr<QuicStreamProxy> {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when the remote side resets the stream.
+    virtual void OnRemoteReset() {}
+    // Called when the remote side finishes the stream.
+    virtual void OnRemoteFinish() {}
+  };
+
+  QuicStreamProxy();
+  ~QuicStreamProxy();
+
+  // Sets a WeakPtr to the corresponding QuicStreamHost. This is valid on either
+  // the proxy or host thread. Should happen right after construction.
+  void set_host(base::WeakPtr<QuicStreamHost> stream_host);
+
+  // Initializes the QuicStreamProxy. Must be called on the proxy thread.
+  // |transport_proxy| must outlive this object.
+  void Initialize(QuicTransportProxy* transport_proxy);
+
+  // Sets the delegate for receiving remote callbacks.
+  void set_delegate(Delegate* delegate);
+
+  // The remaining methods can only be called from the proxy thread and must
+  // be preceded by Initialize().
+
+  scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
+
+  void Reset();
+  void Finish();
+
+ private:
+  // Instruct the QuicTransportProxy to remove and delete this stream proxy.
+  void Delete();
+
+  // Callbacks from QuicStreamHost.
+  friend class QuicStreamHost;
+  void OnRemoteReset();
+  void OnRemoteFinish();
+
+  // Up reference. Owned by the QuicTransportProxy client.
+  QuicTransportProxy* transport_proxy_ = nullptr;
+  // Forward reference. Owned by the QuicTransportHost.
+  base::WeakPtr<QuicStreamHost> stream_host_;
+  // Back reference. Owned by the RTCQuicTransport.
+  Delegate* delegate_ = nullptr;
+
+  // |readable_| transitions to false when OnRemoteFinish() is called.
+  bool readable_ = true;
+  // |writeable_| transitions to false when Finish() is called.
+  bool writeable_ = true;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_STREAM_PROXY_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
index a875a85..3ba0c444 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
@@ -4,10 +4,14 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h"
 
+#include <utility>
+
 #include "net/quic/quic_chromium_alarm_factory.h"
 #include "net/third_party/quic/platform/impl/quic_chromium_clock.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
@@ -15,12 +19,9 @@
 
 namespace blink {
 
-QuicTransportHost::QuicTransportHost(
-    scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
-    base::WeakPtr<QuicTransportProxy> proxy)
-    : proxy_thread_(std::move(proxy_thread)), proxy_(std::move(proxy)) {
+QuicTransportHost::QuicTransportHost(base::WeakPtr<QuicTransportProxy> proxy)
+    : proxy_(std::move(proxy)) {
   DETACH_FROM_THREAD(thread_checker_);
-  DCHECK(proxy_thread_);
   DCHECK(proxy_);
 }
 
@@ -35,7 +36,6 @@
 
 void QuicTransportHost::Initialize(
     IceTransportHost* ice_transport_host,
-    scoped_refptr<base::SingleThreadTaskRunner> host_thread,
     quic::Perspective perspective,
     const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -43,8 +43,8 @@
   DCHECK(!ice_transport_host_);
   ice_transport_host_ = ice_transport_host;
   quic::QuicClock* clock = quic::QuicChromiumClock::GetInstance();
-  auto alarm_factory =
-      std::make_unique<net::QuicChromiumAlarmFactory>(host_thread.get(), clock);
+  auto alarm_factory = std::make_unique<net::QuicChromiumAlarmFactory>(
+      host_thread().get(), clock);
   quic_transport_factory_.reset(
       new P2PQuicTransportFactoryImpl(clock, std::move(alarm_factory)));
   P2PQuicTransportConfig config(
@@ -55,6 +55,18 @@
       quic_transport_factory_->CreateQuicTransport(std::move(config));
 }
 
+scoped_refptr<base::SingleThreadTaskRunner> QuicTransportHost::proxy_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return ice_transport_host_->proxy_thread();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> QuicTransportHost::host_thread()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return ice_transport_host_->host_thread();
+}
+
 void QuicTransportHost::Start(
     std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -66,17 +78,37 @@
   quic_transport_->Stop();
 }
 
+void QuicTransportHost::CreateStream(
+    std::unique_ptr<QuicStreamHost> stream_host) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  P2PQuicStream* p2p_stream = quic_transport_->CreateStream();
+  stream_host->Initialize(this, p2p_stream);
+  stream_hosts_.insert(
+      std::make_pair(stream_host.get(), std::move(stream_host)));
+}
+
+void QuicTransportHost::OnRemoveStream(QuicStreamHost* stream_host_to_remove) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  auto it = stream_hosts_.find(stream_host_to_remove);
+  DCHECK(it != stream_hosts_.end());
+  stream_hosts_.erase(it);
+}
+
 void QuicTransportHost::OnRemoteStopped() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  stream_hosts_.clear();
   PostCrossThreadTask(
-      *proxy_thread_, FROM_HERE,
+      *proxy_thread(), FROM_HERE,
       CrossThreadBind(&QuicTransportProxy::OnRemoteStopped, proxy_));
 }
 
 void QuicTransportHost::OnConnectionFailed(const std::string& error_details,
                                            bool from_remote) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  PostCrossThreadTask(*proxy_thread_, FROM_HERE,
+  stream_hosts_.clear();
+  PostCrossThreadTask(*proxy_thread(), FROM_HERE,
                       CrossThreadBind(&QuicTransportProxy::OnConnectionFailed,
                                       proxy_, error_details, from_remote));
 }
@@ -84,8 +116,27 @@
 void QuicTransportHost::OnConnected() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   PostCrossThreadTask(
-      *proxy_thread_, FROM_HERE,
+      *proxy_thread(), FROM_HERE,
       CrossThreadBind(&QuicTransportProxy::OnConnected, proxy_));
 }
 
+void QuicTransportHost::OnStream(P2PQuicStream* p2p_stream) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(p2p_stream);
+
+  auto stream_proxy = std::make_unique<QuicStreamProxy>();
+  auto stream_host = std::make_unique<QuicStreamHost>();
+  stream_proxy->set_host(stream_host->AsWeakPtr());
+  stream_host->set_proxy(stream_proxy->AsWeakPtr());
+
+  stream_host->Initialize(this, p2p_stream);
+
+  stream_hosts_.insert(
+      std::make_pair(stream_host.get(), std::move(stream_host)));
+
+  PostCrossThreadTask(*proxy_thread(), FROM_HERE,
+                      CrossThreadBind(&QuicTransportProxy::OnStream, proxy_,
+                                      WTF::Passed(std::move(stream_proxy))));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h
index 0558c95..98c4403 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_TRANSPORT_HOST_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_TRANSPORT_HOST_H_
 
+#include <unordered_map>
+
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
@@ -16,6 +18,7 @@
 
 class IceTransportHost;
 class P2PQuicTransportFactory;
+class QuicStreamHost;
 class QuicTransportProxy;
 
 // The host class is the host side correspondent to the QuicTransportProxy. See
@@ -42,32 +45,40 @@
 // must be called on the host thread.
 class QuicTransportHost final : public P2PQuicTransport::Delegate {
  public:
-  QuicTransportHost(scoped_refptr<base::SingleThreadTaskRunner> proxy_thread,
-                    base::WeakPtr<QuicTransportProxy> transport_proxy);
+  QuicTransportHost(base::WeakPtr<QuicTransportProxy> transport_proxy);
   ~QuicTransportHost() override;
 
   void Initialize(
       IceTransportHost* ice_transport_host,
-      scoped_refptr<base::SingleThreadTaskRunner> host_thread,
       quic::Perspective perspective,
       const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates);
 
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
+  scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
+
   void Start(
       std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints);
   void Stop();
 
+  void CreateStream(std::unique_ptr<QuicStreamHost> stream_host);
+
+  // QuicStreamHost callbacks.
+  void OnRemoveStream(QuicStreamHost* stream_host_to_remove);
+
  private:
   // P2PQuicTransport::Delegate overrides.
   void OnRemoteStopped() override;
   void OnConnectionFailed(const std::string& error_details,
                           bool from_remote) override;
   void OnConnected() override;
+  void OnStream(P2PQuicStream* stream) override;
 
-  const scoped_refptr<base::SingleThreadTaskRunner> proxy_thread_;
   std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory_;
   std::unique_ptr<P2PQuicTransport> quic_transport_;
   base::WeakPtr<QuicTransportProxy> proxy_;
   IceTransportHost* ice_transport_host_ = nullptr;
+  std::unordered_map<QuicStreamHost*, std::unique_ptr<QuicStreamHost>>
+      stream_hosts_;
 
   THREAD_CHECKER(thread_checker_);
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.cc
index 815129d..7299267 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_host.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
@@ -32,8 +34,7 @@
   // The QuicTransportHost is constructed on the proxy thread but should only be
   // interacted with via PostTask to the host thread. The OnTaskRunnerDeleter
   // (configured above) will ensure it gets deleted on the host thread.
-  host_.reset(
-      new QuicTransportHost(proxy_thread, weak_ptr_factory_.GetWeakPtr()));
+  host_.reset(new QuicTransportHost(weak_ptr_factory_.GetWeakPtr()));
   // Connect to the IceTransportProxy. This gives us a reference to the
   // underlying IceTransportHost that should be connected by the
   // QuicTransportHost on the host thread. It is safe to post it unretained
@@ -42,12 +43,11 @@
   // object.
   IceTransportHost* ice_transport_host =
       ice_transport_proxy->ConnectConsumer(this);
-  PostCrossThreadTask(
-      *host_thread(), FROM_HERE,
-      CrossThreadBind(&QuicTransportHost::Initialize,
-                      CrossThreadUnretained(host_.get()),
-                      CrossThreadUnretained(ice_transport_host), host_thread(),
-                      perspective, certificates));
+  PostCrossThreadTask(*host_thread(), FROM_HERE,
+                      CrossThreadBind(&QuicTransportHost::Initialize,
+                                      CrossThreadUnretained(host_.get()),
+                                      CrossThreadUnretained(ice_transport_host),
+                                      perspective, certificates));
 }
 
 QuicTransportProxy::~QuicTransportProxy() {
@@ -85,6 +85,36 @@
                                       CrossThreadUnretained(host_.get())));
 }
 
+QuicStreamProxy* QuicTransportProxy::CreateStream() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  auto stream_proxy = std::make_unique<QuicStreamProxy>();
+  auto stream_host = std::make_unique<QuicStreamHost>();
+  stream_proxy->set_host(stream_host->AsWeakPtr());
+  stream_host->set_proxy(stream_proxy->AsWeakPtr());
+
+  stream_proxy->Initialize(this);
+
+  PostCrossThreadTask(*host_thread(), FROM_HERE,
+                      CrossThreadBind(&QuicTransportHost::CreateStream,
+                                      CrossThreadUnretained(host_.get()),
+                                      WTF::Passed(std::move(stream_host))));
+
+  QuicStreamProxy* stream_proxy_ptr = stream_proxy.get();
+  stream_proxies_.insert(
+      std::make_pair(stream_proxy_ptr, std::move(stream_proxy)));
+  return stream_proxy_ptr;
+}
+
+void QuicTransportProxy::OnRemoveStream(
+    QuicStreamProxy* stream_proxy_to_remove) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  auto it = stream_proxies_.find(stream_proxy_to_remove);
+  DCHECK(it != stream_proxies_.end());
+  stream_proxies_.erase(it);
+}
+
 void QuicTransportProxy::OnConnected() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   delegate_->OnConnected();
@@ -92,6 +122,7 @@
 
 void QuicTransportProxy::OnRemoteStopped() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  stream_proxies_.clear();
   delegate_->OnRemoteStopped();
 }
 
@@ -99,6 +130,19 @@
                                             bool from_remote) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   delegate_->OnConnectionFailed(error_details, from_remote);
+  stream_proxies_.clear();
+}
+
+void QuicTransportProxy::OnStream(
+    std::unique_ptr<QuicStreamProxy> stream_proxy) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  stream_proxy->Initialize(this);
+
+  QuicStreamProxy* stream_proxy_ptr = stream_proxy.get();
+  stream_proxies_.insert(
+      std::make_pair(stream_proxy_ptr, std::move(stream_proxy)));
+  delegate_->OnStream(stream_proxy_ptr);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
index 3e158944..e3e949f 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_proxy.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_TRANSPORT_PROXY_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_QUIC_TRANSPORT_PROXY_H_
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/memory/scoped_refptr.h"
@@ -22,6 +23,7 @@
 namespace blink {
 
 class IceTransportProxy;
+class QuicStreamProxy;
 class QuicTransportHost;
 
 // This class allows the QUIC implementation (P2PQuicTransport) to run on a
@@ -49,6 +51,8 @@
     // locally by the framer or remotely by the peer.
     virtual void OnConnectionFailed(const std::string& error_details,
                                     bool from_remote) {}
+    // Called when the remote side has created a new stream.
+    virtual void OnStream(QuicStreamProxy* stream_proxy) {}
   };
 
   // Construct a Proxy with the underlying QUIC implementation running on the
@@ -72,18 +76,26 @@
       std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints);
   void Stop();
 
+  QuicStreamProxy* CreateStream();
+
+  // QuicStreamProxy callbacks.
+  void OnRemoveStream(QuicStreamProxy* stream_proxy);
+
  private:
   // Callbacks from QuicTransportHost.
   friend class QuicTransportHost;
   void OnConnected();
   void OnRemoteStopped();
   void OnConnectionFailed(const std::string& error_details, bool from_remote);
+  void OnStream(std::unique_ptr<QuicStreamProxy> stream_proxy);
 
   // Since the Host is deleted on the host thread (Via OnTaskRunnerDeleter), as
   // long as this is alive it is safe to post tasks to it (using unretained).
   std::unique_ptr<QuicTransportHost, base::OnTaskRunnerDeleter> host_;
   Delegate* const delegate_;
   IceTransportProxy* ice_transport_proxy_;
+  std::unordered_map<QuicStreamProxy*, std::unique_ptr<QuicStreamProxy>>
+      stream_proxies_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
index ef0cc25..66f32031 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
@@ -3,13 +3,20 @@
 // found in the LICENSE file.
 #include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h"
 
+#include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h"
 
 namespace blink {
 
-RTCQuicStream::RTCQuicStream(RTCQuicTransport* transport)
-    : transport_(transport) {
+RTCQuicStream::RTCQuicStream(ExecutionContext* context,
+                             RTCQuicTransport* transport,
+                             QuicStreamProxy* stream_proxy)
+    : EventTargetWithInlineData(),
+      ContextClient(context),
+      transport_(transport),
+      proxy_(stream_proxy) {
   DCHECK(transport_);
+  DCHECK(proxy_);
 }
 
 RTCQuicStream::~RTCQuicStream() = default;
@@ -42,13 +49,75 @@
   return write_buffered_amount_;
 }
 
+void RTCQuicStream::finish() {
+  if (!writeable_) {
+    return;
+  }
+  proxy_->Finish();
+  writeable_ = false;
+  if (readable_) {
+    DCHECK_EQ(state_, RTCQuicStreamState::kOpen);
+    state_ = RTCQuicStreamState::kClosing;
+  } else {
+    DCHECK_EQ(state_, RTCQuicStreamState::kClosing);
+    Close();
+  }
+}
+
+void RTCQuicStream::reset() {
+  if (IsClosed()) {
+    return;
+  }
+  proxy_->Reset();
+  writeable_ = false;
+  readable_ = false;
+  Close();
+}
+
 void RTCQuicStream::Stop() {
+  readable_ = false;
+  writeable_ = false;
   state_ = RTCQuicStreamState::kClosed;
+  proxy_ = nullptr;
+}
+
+void RTCQuicStream::Close() {
+  Stop();
+  transport_->RemoveStream(this);
+}
+
+void RTCQuicStream::OnRemoteReset() {
+  DCHECK_NE(state_, RTCQuicStreamState::kClosed);
+  Close();
+  DispatchEvent(*Event::Create(EventTypeNames::statechange));
+}
+
+void RTCQuicStream::OnRemoteFinish() {
+  DCHECK_NE(state_, RTCQuicStreamState::kClosed);
+  DCHECK(readable_);
+  readable_ = false;
+  if (writeable_) {
+    DCHECK_EQ(state_, RTCQuicStreamState::kOpen);
+    state_ = RTCQuicStreamState::kClosing;
+  } else {
+    DCHECK_EQ(state_, RTCQuicStreamState::kClosing);
+    Close();
+  }
+  DispatchEvent(*Event::Create(EventTypeNames::statechange));
+}
+
+const AtomicString& RTCQuicStream::InterfaceName() const {
+  return EventTargetNames::RTCQuicStream;
+}
+
+ExecutionContext* RTCQuicStream::GetExecutionContext() const {
+  return ContextClient::GetExecutionContext();
 }
 
 void RTCQuicStream::Trace(blink::Visitor* visitor) {
   visitor->Trace(transport_);
-  ScriptWrappable::Trace(visitor);
+  EventTargetWithInlineData::Trace(visitor);
+  ContextClient::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h
index db56e123..b2952b2 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_QUIC_STREAM_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_QUIC_STREAM_H_
 
+#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_proxy.h"
 
 namespace blink {
 
@@ -14,29 +16,54 @@
 
 enum class RTCQuicStreamState { kNew, kOpening, kOpen, kClosing, kClosed };
 
-class MODULES_EXPORT RTCQuicStream final : public ScriptWrappable {
+class MODULES_EXPORT RTCQuicStream final : public EventTargetWithInlineData,
+                                           public ContextClient,
+                                           public QuicStreamProxy::Delegate {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  RTCQuicStream(RTCQuicTransport* transport);
+  RTCQuicStream(ExecutionContext* context,
+                RTCQuicTransport* transport,
+                QuicStreamProxy* stream_proxy);
   ~RTCQuicStream() override;
 
+  // Called from the RTCQuicTransport when it is being stopped.
   void Stop();
+  bool IsClosed() const { return state_ == RTCQuicStreamState::kClosed; }
 
   // rtc_quic_stream.idl
   RTCQuicTransport* transport() const;
   String state() const;
   uint32_t readBufferedAmount() const;
   uint32_t writeBufferedAmount() const;
+  void finish();
+  void reset();
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(statechange);
+
+  // EventTarget overrides.
+  const AtomicString& InterfaceName() const override;
+  ExecutionContext* GetExecutionContext() const override;
 
   // For garbage collection.
   void Trace(blink::Visitor* visitor) override;
 
  private:
+  // Closes the stream. This will change the state to kClosed and deregister it
+  // from the RTCQuicTransport. The QuicStreamProxy can no longer be used after
+  // this point.
+  void Close();
+
+  // QuicStreamProxy::Delegate overrides.
+  void OnRemoteReset() override;
+  void OnRemoteFinish() override;
+
   Member<RTCQuicTransport> transport_;
-  RTCQuicStreamState state_ = RTCQuicStreamState::kNew;
+  RTCQuicStreamState state_ = RTCQuicStreamState::kOpen;
+  bool readable_ = true;
+  bool writeable_ = true;
   uint32_t read_buffered_amount_ = 0;
   uint32_t write_buffered_amount_ = 0;
+  QuicStreamProxy* proxy_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.idl b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.idl
index edc3184..945d6b4 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.idl
@@ -14,12 +14,15 @@
 // https://w3c.github.io/webrtc-quic/#quicstream*
 [
    Exposed=Window,
-   RuntimeEnabled=RTCQuicStream
-] interface RTCQuicStream {
+   RuntimeEnabled=RTCQuicTransport
+] interface RTCQuicStream : EventTarget {
     readonly attribute RTCQuicTransport transport;
     readonly attribute RTCQuicStreamState state;
     readonly attribute unsigned long readBufferedAmount;
     readonly attribute unsigned long writeBufferedAmount;
+    void finish();
+    void reset();
+    attribute EventHandler onstatechange;
     // TODO(crbug.com/868068): Implement remaining methods, attributes, and events.
 };
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.cc
new file mode 100644
index 0000000..af4228d
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event_init.h"
+
+namespace blink {
+
+RTCQuicStreamEvent* RTCQuicStreamEvent::Create(RTCQuicStream* stream) {
+  return new RTCQuicStreamEvent(stream);
+}
+
+RTCQuicStreamEvent* RTCQuicStreamEvent::Create(
+    const AtomicString& type,
+    const RTCQuicStreamEventInit& initializer) {
+  return new RTCQuicStreamEvent(type, initializer);
+}
+
+RTCQuicStreamEvent::RTCQuicStreamEvent(RTCQuicStream* stream)
+    : Event(EventTypeNames::quicstream, Bubbles::kNo, Cancelable::kNo),
+      stream_(stream) {}
+
+RTCQuicStreamEvent::RTCQuicStreamEvent(
+    const AtomicString& type,
+    const RTCQuicStreamEventInit& initializer)
+    : Event(type, initializer), stream_(initializer.stream()) {}
+
+RTCQuicStreamEvent::~RTCQuicStreamEvent() = default;
+
+RTCQuicStream* RTCQuicStreamEvent::stream() const {
+  return stream_.Get();
+}
+
+const AtomicString& RTCQuicStreamEvent::InterfaceName() const {
+  return EventNames::RTCQuicStreamEvent;
+}
+
+void RTCQuicStreamEvent::Trace(blink::Visitor* visitor) {
+  visitor->Trace(stream_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.h b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.h
new file mode 100644
index 0000000..6e60d9a
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_QUIC_STREAM_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_QUIC_STREAM_EVENT_H_
+
+#include "third_party/blink/renderer/modules/event_modules.h"
+
+namespace blink {
+
+class RTCQuicStream;
+class RTCQuicStreamEventInit;
+
+class RTCQuicStreamEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static RTCQuicStreamEvent* Create(RTCQuicStream* stream);
+  static RTCQuicStreamEvent* Create(const AtomicString& type,
+                                    const RTCQuicStreamEventInit& init);
+
+  ~RTCQuicStreamEvent() override;
+
+  // rtc_quic_stream_event.idl
+  RTCQuicStream* stream() const;
+
+  // Event overrides.
+  const AtomicString& InterfaceName() const override;
+
+  // For garbage collection.
+  void Trace(blink::Visitor*) override;
+
+ private:
+  RTCQuicStreamEvent(RTCQuicStream* stream);
+  RTCQuicStreamEvent(const AtomicString& type,
+                     const RTCQuicStreamEventInit& init);
+
+  Member<RTCQuicStream> stream_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_QUIC_STREAM_EVENT_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.idl b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.idl
new file mode 100644
index 0000000..ced237c
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.idl
@@ -0,0 +1,12 @@
+// 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.
+
+// https://w3c.github.io/webrtc-quic/#rtcquicstreamevent
+[
+   RuntimeEnabled=RTCQuicTransport,
+   Constructor(DOMString type, optional RTCQuicStreamEventInit eventInitDict),
+   Exposed=Window
+] interface RTCQuicStreamEvent : Event {
+    readonly attribute RTCQuicStream stream;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event_init.idl b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event_init.idl
new file mode 100644
index 0000000..53e3bcd
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event_init.idl
@@ -0,0 +1,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.
+
+// https://w3c.github.io/webrtc-quic/#dom-rtcquicstreameventinit
+dictionary RTCQuicStreamEventInit : EventInit {
+  RTCQuicStream stream;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
index 79d0957..17e2201 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_event.h"
 
 namespace blink {
 
@@ -199,14 +200,31 @@
 }
 
 RTCQuicStream* RTCQuicTransport::createStream(ExceptionState& exception_state) {
-  if (RaiseExceptionIfClosed(exception_state)) {
+  // TODO(github.com/w3c/webrtc-quic/issues/50): Maybe support createStream in
+  // the 'new' or 'connecting' states.
+  if (state_ != RTCQuicTransportState::kConnected) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "RTCQuicTransport.createStream() is only "
+                                      "valid in the 'connected' state.");
     return nullptr;
   }
-  RTCQuicStream* stream = new RTCQuicStream(this);
+  return AddStream(proxy_->CreateStream());
+}
+
+RTCQuicStream* RTCQuicTransport::AddStream(QuicStreamProxy* stream_proxy) {
+  auto* stream = new RTCQuicStream(GetExecutionContext(), this, stream_proxy);
+  stream_proxy->set_delegate(stream);
   streams_.insert(stream);
   return stream;
 }
 
+void RTCQuicTransport::RemoveStream(RTCQuicStream* stream) {
+  DCHECK(stream);
+  auto it = streams_.find(stream);
+  DCHECK(it != streams_.end());
+  streams_.erase(it);
+}
+
 void RTCQuicTransport::OnConnected() {
   state_ = RTCQuicTransportState::kConnected;
   DispatchEvent(*Event::Create(EventTypeNames::statechange));
@@ -223,6 +241,11 @@
   DispatchEvent(*Event::Create(EventTypeNames::statechange));
 }
 
+void RTCQuicTransport::OnStream(QuicStreamProxy* stream_proxy) {
+  RTCQuicStream* stream = AddStream(stream_proxy);
+  DispatchEvent(*RTCQuicStreamEvent::Create(stream));
+}
+
 bool RTCQuicTransport::RaiseExceptionIfClosed(
     ExceptionState& exception_state) const {
   if (IsClosed()) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h
index d734457..b66e191 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h
@@ -44,6 +44,9 @@
 
   ~RTCQuicTransport() override;
 
+  RTCQuicStream* AddStream(QuicStreamProxy* stream_proxy);
+  void RemoveStream(RTCQuicStream* stream);
+
   // https://w3c.github.io/webrtc-quic/#quic-transport*
   RTCIceTransport* transport() const;
   String state() const;
@@ -57,6 +60,7 @@
   RTCQuicStream* createStream(ExceptionState& exception_state);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(statechange);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(quicstream);
 
   // Called by the RTCIceTransport when its start() method is called.
   void OnTransportStarted();
@@ -85,6 +89,7 @@
   void OnConnectionFailed(const std::string& error_details,
                           bool from_remote) override;
   void OnRemoteStopped() override;
+  void OnStream(QuicStreamProxy* stream_proxy) override;
 
   bool IsClosed() const { return state_ == RTCQuicTransportState::kClosed; }
   bool RaiseExceptionIfClosed(ExceptionState& exception_state) const;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.idl b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.idl
index 6118b67..ab25d01 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.idl
@@ -28,8 +28,8 @@
     sequence<ArrayBuffer> getRemoteCertificates();
     [RaisesException] void start(RTCQuicParameters remoteParameters);
     void stop();
-    [RaisesException, RuntimeEnabled=RTCQuicStream] RTCQuicStream createStream();
+    [RaisesException] RTCQuicStream createStream();
     attribute EventHandler onstatechange;
     attribute EventHandler onerror;
-    // TODO(crbug.com/868068): Implement onstream.
+    attribute EventHandler onquicstream;
 };
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl b/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
index 6878cf7..7df4129 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_barcode.idl
@@ -6,6 +6,7 @@
 
 [
     Constructor,
+    Serializable,
     OriginTrialEnabled=ShapeDetection
 ] interface DetectedBarcode {
     // TODO(mcasas): Implement missing fields. https://crbug.com/646083
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_face.idl b/third_party/blink/renderer/modules/shapedetection/detected_face.idl
index e45b7c2..f367b6a 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_face.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_face.idl
@@ -6,6 +6,7 @@
 
 [
     Constructor,
+    Serializable,
     OriginTrialEnabled=ShapeDetection
 ] interface DetectedFace {
     // TODO(xianglu): Implement any other fields. https://crbug.com/646083
diff --git a/third_party/blink/renderer/modules/shapedetection/detected_text.idl b/third_party/blink/renderer/modules/shapedetection/detected_text.idl
index 656b604a..7afa4c66 100644
--- a/third_party/blink/renderer/modules/shapedetection/detected_text.idl
+++ b/third_party/blink/renderer/modules/shapedetection/detected_text.idl
@@ -6,6 +6,7 @@
 
 [
     Constructor,
+    Serializable,
     OriginTrialEnabled=ShapeDetection
 ] interface DetectedText {
     [SameObject] readonly attribute DOMString rawValue;
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 0412601..26b9b86 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -216,6 +216,10 @@
   RuntimeEnabledFeatures::SetNoHoverAfterLayoutChangeEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableNoHoverDuringScroll(bool enable) {
+  RuntimeEnabledFeatures::SetNoHoverDuringScrollEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableNotificationConstructor(bool enable) {
   RuntimeEnabledFeatures::SetNotificationConstructorEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
index 96c15bc..3158dc5 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -78,8 +78,8 @@
   DEFINE_THREAD_SAFE_STATIC_LOCAL(
       EnumerationHistogram, gamut_named_histogram,
       ("Blink.ColorGamut.Source", static_cast<int>(ColorSpaceGamut::kEnd)));
-  gamut_named_histogram.Count(
-      static_cast<int>(ColorSpaceUtilities::GetColorSpaceGamut(color_profile)));
+  gamut_named_histogram.Count(static_cast<int>(
+      color_space_utilities::GetColorSpaceGamut(color_profile)));
 }
 
 void BitmapImageMetrics::CountJpegArea(const IntSize& size) {
diff --git a/third_party/blink/renderer/platform/graphics/color_space_gamut.cc b/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
index 736049dc..1ca39ae 100644
--- a/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
+++ b/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
@@ -9,7 +9,7 @@
 
 namespace blink {
 
-namespace ColorSpaceUtilities {
+namespace color_space_utilities {
 
 ColorSpaceGamut GetColorSpaceGamut(const WebScreenInfo& screen_info) {
   const gfx::ColorSpace& color_space = screen_info.color_space;
@@ -19,7 +19,7 @@
   // wide gamut for HDR profiles).
   skcms_ICCProfile color_profile;
   color_space.GetRasterColorSpace().ToSkColorSpace()->toProfile(&color_profile);
-  return ColorSpaceUtilities::GetColorSpaceGamut(&color_profile);
+  return color_space_utilities::GetColorSpaceGamut(&color_profile);
 }
 
 ColorSpaceGamut GetColorSpaceGamut(const skcms_ICCProfile* color_profile) {
@@ -62,6 +62,6 @@
   return ColorSpaceGamut::kUltraWide;
 }
 
-}  // namespace ColorSpaceUtilities
+}  // namespace color_space_utilities
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/color_space_gamut.h b/third_party/blink/renderer/platform/graphics/color_space_gamut.h
index 20502a1a..fccb1e1 100644
--- a/third_party/blink/renderer/platform/graphics/color_space_gamut.h
+++ b/third_party/blink/renderer/platform/graphics/color_space_gamut.h
@@ -29,12 +29,12 @@
   kEnd
 };
 
-namespace ColorSpaceUtilities {
+namespace color_space_utilities {
 
 PLATFORM_EXPORT ColorSpaceGamut GetColorSpaceGamut(const WebScreenInfo&);
 ColorSpaceGamut GetColorSpaceGamut(const skcms_ICCProfile*);
 
-}  // namespace ColorSpaceUtilities
+}  // namespace color_space_utilities
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9092649..5e51f106 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -818,6 +818,10 @@
       settable_from_internals: true,
     },
     {
+      name: "NoHoverDuringScroll",
+      settable_from_internals: true,
+    },
+    {
       name: "NoIdleEncodingForLayoutTests",
       status: "test",
     },
@@ -1072,11 +1076,6 @@
       origin_trial_feature_name: "RtcPeerConnectionId",
       status: "experimental",
     },
-    // Enables the use of the RTCQuicStream object.
-    {
-      name: "RTCQuicStream",
-      status: "test",
-    },
     // Enables the use of the RTCQuicTransport object.
     {
       name: "RTCQuicTransport",
diff --git a/third_party/blink/tools/audit_non_blink_usage.py b/third_party/blink/tools/audit_non_blink_usage.py
index 59c7a0dd..b870be5 100755
--- a/third_party/blink/tools/audit_non_blink_usage.py
+++ b/third_party/blink/tools/audit_non_blink_usage.py
@@ -47,6 +47,7 @@
             'base::SequencedTaskRunner',
             'base::SingleThreadTaskRunner',
             'base::ScopedFD',
+            'base::SupportsWeakPtr',
             'base::SysInfo',
             'base::ThreadChecker',
             'base::Time',
@@ -290,6 +291,12 @@
         ],
     },
     {
+        'paths': ['third_party/blink/renderer/core/css/media_values.cc'],
+        'allowed': [
+            'color_space_utilities::GetColorSpaceGamut',
+        ],
+    },
+    {
         'paths': ['third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.cc'],
         'allowed': [
             # The existing code already contains gin::IsolateHolder.
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 141f86b..80b861d 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 688dcfa22ee906f83d49b905b69c7c53347c92d9
+Revision: 91781418bc4eb79e357687094811bab6430b10b2
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/CONTRIBUTORS b/third_party/crashpad/crashpad/CONTRIBUTORS
index 2080cc45..8724b7f 100644
--- a/third_party/crashpad/crashpad/CONTRIBUTORS
+++ b/third_party/crashpad/crashpad/CONTRIBUTORS
@@ -12,3 +12,4 @@
 Mark Mentovai <mark@chromium.org>
 Robert Sesek <rsesek@chromium.org>
 Scott Graham <scottmg@chromium.org>
+Joshua Peraza <jperaza@chromium.org>
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h
index 154d9ac..ea0e7b6 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -266,6 +266,12 @@
   //!     CaptureContext() or similar.
   static void DumpWithoutCrash(NativeCPUContext* context);
 
+  //! \brief Disables any installed crash handler, including any
+  //!     FirstChanceHandler and crashes the current process.
+  //!
+  //! \param[in] message A message to be logged before crashing.
+  static void CrashWithoutDump(const std::string& message);
+
   //! \brief The type for custom handlers installed by clients.
   using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
 
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
index 1e9d651..e8e7f46 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -78,28 +78,8 @@
 
 #endif  // OS_ANDROID
 
-class SignalHandler {
- public:
-  virtual void HandleCrashFatal(int signo,
-                                siginfo_t* siginfo,
-                                void* context) = 0;
-  virtual bool HandleCrashNonFatal(int signo,
-                                   siginfo_t* siginfo,
-                                   void* context) = 0;
-
-  void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
-    first_chance_handler_ = handler;
-  }
-
- protected:
-  SignalHandler() = default;
-  ~SignalHandler() = default;
-
-  CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
-};
-
 // Launches a single use handler to snapshot this process.
-class LaunchAtCrashHandler : public SignalHandler {
+class LaunchAtCrashHandler {
  public:
   static LaunchAtCrashHandler* Get() {
     static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
@@ -123,9 +103,7 @@
     return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);
   }
 
-  bool HandleCrashNonFatal(int signo,
-                           siginfo_t* siginfo,
-                           void* context) override {
+  bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) {
     if (first_chance_handler_ &&
         first_chance_handler_(
             signo, siginfo, static_cast<ucontext_t*>(context))) {
@@ -162,13 +140,19 @@
     return false;
   }
 
-  void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override {
-    if (HandleCrashNonFatal(signo, siginfo, context)) {
+  void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) {
+    if (enabled_ && HandleCrashNonFatal(signo, siginfo, context)) {
       return;
     }
     Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
   }
 
+  void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
+    first_chance_handler_ = handler;
+  }
+
+  static void Disable() { enabled_ = false; }
+
  private:
   LaunchAtCrashHandler() = default;
 
@@ -183,16 +167,20 @@
   std::vector<const char*> argv_;
   std::vector<std::string> envp_strings_;
   std::vector<const char*> envp_;
-  bool set_envp_ = false;
   ExceptionInformation exception_information_;
+  CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
+  bool set_envp_ = false;
+
+  static thread_local bool enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
 };
+thread_local bool LaunchAtCrashHandler::enabled_ = true;
 
 // A pointer to the currently installed crash signal handler. This allows
 // the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
 // using the currently configured crash handling strategy.
-static SignalHandler* g_crash_handler;
+static LaunchAtCrashHandler* g_crash_handler;
 
 }  // namespace
 
@@ -319,6 +307,12 @@
 }
 
 // static
+void CrashpadClient::CrashWithoutDump(const std::string& message) {
+  LaunchAtCrashHandler::Disable();
+  LOG(FATAL) << message;
+}
+
+// static
 void CrashpadClient::SetFirstChanceExceptionHandler(
     FirstChanceHandler handler) {
   DCHECK(g_crash_handler);
diff --git a/third_party/crashpad/crashpad/minidump/BUILD.gn b/third_party/crashpad/crashpad/minidump/BUILD.gn
index 88a1081..86966d82 100644
--- a/third_party/crashpad/crashpad/minidump/BUILD.gn
+++ b/third_party/crashpad/crashpad/minidump/BUILD.gn
@@ -154,6 +154,10 @@
     "minidump_writable_test.cc",
   ]
 
+  configs += [
+    "../build:crashpad_is_in_fuchsia",
+  ]
+
   deps = [
     ":test_support",
     "../snapshot:test_support",
diff --git a/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
index 382baaf..1d65485 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
@@ -100,6 +100,12 @@
   }
 }
 
+// Related tracking issues:
+// https://fuchsia.atlassian.net/browse/DX-487
+// https://bugs.chromium.org/p/chromium/issues/detail?id=872892
+// https://bugs.chromium.org/p/chromium/issues/detail?id=889582
+// TODO: Re-enable test once LUCI supports invalid UTF8 characters in test logs.
+#if !defined(CRASHPAD_IS_IN_FUCHSIA)
 TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) {
   StringFile string_file;
 
@@ -139,6 +145,7 @@
     EXPECT_NE(output_string.find(0xfffd), base::string16::npos);
   }
 }
+#endif  // !defined(CRASHPAD_IS_IN_FUCHSIA)
 
 TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) {
   StringFile string_file;
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
index 564587c5..3ad253e0 100644
--- a/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -642,11 +642,9 @@
       "win/session_end_watcher_test.cc",
     ]
 
-    if (crashpad_is_in_chromium) {
-      if (is_asan && is_component_build) {
-        # TODO(crbug.com/856174): Re-enable these once Windows ASan is fixed.
-        sources -= [ "stdlib/string_number_conversion_test.cc" ]
-      }
+    if (crashpad_is_in_chromium && is_asan && is_component_build) {
+      # TODO(crbug.com/856174): Re-enable these once Windows ASan is fixed.
+      sources -= [ "stdlib/string_number_conversion_test.cc" ]
     }
   }
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b7fb90d..2197e924 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -29694,6 +29694,7 @@
   <int value="178337215" label="enable-md-history"/>
   <int value="180074362" label="memory-pressure-thresholds"/>
   <int value="185991204" label="enable-webrtc-srtp-encrypted-headers"/>
+  <int value="188610022" label="NewMessageListView:enabled"/>
   <int value="189728101" label="FasterLocationReload:disabled"/>
   <int value="191737931" label="enable-mark-http-as"/>
   <int value="194573877" label="MacViewsNativeDialogs:disabled"/>
@@ -30244,6 +30245,7 @@
   <int value="1269940659" label="EnumerateAudioDevices:enabled"/>
   <int value="1269952439" label="AndroidAIAFetching:disabled"/>
   <int value="1272699563" label="enable-hosted-mode"/>
+  <int value="1272923911" label="NewMessageListView:disabled"/>
   <int value="1275507565" label="OfflinePagesRenovations:enabled"/>
   <int value="1276209777" label="ntp-switch-to-existing-tab"/>
   <int value="1277386636" label="QueryInOmnibox:disabled"/>
@@ -40565,6 +40567,11 @@
       label="Timestamp shown indicating the page was just updated on reload"/>
 </enum>
 
+<enum name="PreviewsLitePageInfoBarAction">
+  <int value="0" label="Infobar shown"/>
+  <int value="1" label="Infobar dismissed by user"/>
+</enum>
+
 <enum name="PreviewsOptimizationFilterStatus">
   <int value="0" label="Found a server blacklist configuration">
     Found configuration data for a server blacklist.
@@ -40608,6 +40615,7 @@
   <int value="1" label="HTTP POST"/>
   <int value="2" label="Subframe navigation"/>
   <int value="3" label="Server Unavailable"/>
+  <int value="4" label="Infobar hasn't been seen by the user and needs to be"/>
 </enum>
 
 <enum name="PreviewsServerLitePageServerResponse">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index bda6b0d..1021101 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -34677,10 +34677,23 @@
 </histogram>
 
 <histogram name="GPU.DirectComposition.OverlayFormatUsed" enum="OverlayFormat">
+  <obsolete>
+    Deprecated 10/2018. Replaced by GPU.DirectComposition.OverlayFormatUsed2.
+  </obsolete>
   <owner>sunnyps@chromium.org</owner>
   <summary>Which overlay format was chosen for YUV overlays.</summary>
 </histogram>
 
+<histogram name="GPU.DirectComposition.OverlayFormatUsed2" enum="OverlayFormat"
+    expires_after="M75">
+  <owner>sunnyps@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    Which overlay format was chosen for YUV overlays. Recorded once per GPU
+    process launch only if hardware overlays are supported.
+  </summary>
+</histogram>
+
 <histogram name="GPU.DirectComposition.OverlaysSupported"
     enum="BooleanOverlaySupported">
   <owner>jbauman@chromium.org</owner>
@@ -34740,11 +34753,35 @@
   <summary>Whether or not swap buffers succeeded.</summary>
 </histogram>
 
+<histogram base="true" name="GPU.DirectComposition.SwapChainCreationResult"
+    enum="BooleanSuccess" expires_after="M75">
+<!-- Name completed by histogram_suffixes name="GPU.DirectComposition.OverlayFormat" -->
+
+  <owner>sunnyps@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    Whether creating swap chain for overlay format succeeded. Recorded once per
+    swap chain creation.
+  </summary>
+</histogram>
+
 <histogram name="GPU.DirectComposition.SwapchainFormat" enum="SwapchainFormat">
+  <obsolete>
+    Deprecated 10/2018. Replaced by GPU.DirectComposition.SwapChainFormatUsed.
+  </obsolete>
   <owner>jbauman@chromium.org</owner>
   <summary>What type of swapchain was actually created for an overlay.</summary>
 </histogram>
 
+<histogram name="GPU.DirectComposition.SwapChainFormat2" enum="OverlayFormat"
+    expires_after="M75">
+  <owner>sunnyps@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    What format was used for each overlay swap chain on each swap buffers.
+  </summary>
+</histogram>
+
 <histogram name="GPU.DisplayCompositorLifetimeEvents"
     enum="GPUProcessLifetimeEvent">
   <obsolete>
@@ -81226,6 +81263,14 @@
   </summary>
 </histogram>
 
+<histogram name="Previews.LitePageNotificationInfoBar"
+    enum="PreviewsLitePageInfoBarAction">
+  <owner>robertogden@chromium.org</owner>
+  <summary>
+    User interactions with the HTTPS Server Previews notification InfoBar.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Previews.OptimizationFilterStatus"
     enum="PreviewsOptimizationFilterStatus">
   <owner>dougarnett@chromium.org</owner>
@@ -85451,6 +85496,9 @@
 
 <histogram name="RenderFrameHostImpl.ReceivedPostMessageFromNonDescendant"
     enum="BooleanReceived" expires_after="2018-10-30">
+  <obsolete>
+    Removed Oct 2018.
+  </obsolete>
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <summary>
@@ -97707,6 +97755,26 @@
   </summary>
 </histogram>
 
+<histogram name="SignedExchange.Time.CertificateFetch.Failure" units="ms"
+    expires_after="2019-10-01">
+  <owner>kinuko@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    The amount of time elapsed to fetch certificate chain from certUrl, for
+    which the fetch has failed.
+  </summary>
+</histogram>
+
+<histogram name="SignedExchange.Time.CertificateFetch.Success" units="ms"
+    expires_after="2019-10-01">
+  <owner>kinuko@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    The amount of time elapsed to fetch certificate chain from certUrl, for
+    which the fetch has succeeded.
+  </summary>
+</histogram>
+
 <histogram name="SignedExchange.Time.SignatureVerify" units="ms"
     expires_after="2019-10-01">
   <owner>kinuko@chromium.org</owner>
diff --git a/tools/perf/page_sets/rendering/tough_compositor_cases.py b/tools/perf/page_sets/rendering/tough_compositor_cases.py
index 999c7ea4..c574760 100644
--- a/tools/perf/page_sets/rendering/tough_compositor_cases.py
+++ b/tools/perf/page_sets/rendering/tough_compositor_cases.py
@@ -224,7 +224,7 @@
           selector='#container', speed_in_pixels_per_second=1000)
 
 
-# Why: Infinite root scroller with 1 layer baseline"""
+# Why: Infinite non-root scroller with 1 layer baseline"""
 class InfiniteScrollElementNLayers0(InfiniteScrollElementNLayersPage):
   BASE_NAME = 'infinite_scroll_element_n_layers_0'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
@@ -232,7 +232,7 @@
          'infinite_scroll_element_n_layers.html?layer_count=1')
 
 
-# Why: Infinite root scroller with 50%-ile layer count"""
+# Why: Infinite non-root scroller with 50%-ile layer count"""
 class InfiniteScrollElementNLayers50(InfiniteScrollElementNLayersPage):
   BASE_NAME = 'infinite_scroll_element_n_layers_50'
   SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
@@ -240,7 +240,7 @@
          'infinite_scroll_element_n_layers.html?layer_count=31')
 
 
-# Why: Infinite root scroller with 75%-ile layer count"""
+# Why: Infinite non-root scroller with 75%-ile layer count"""
 class InfiniteScrollElementNLayers75(InfiniteScrollElementNLayersPage):
   BASE_NAME = 'infinite_scroll_element_n_layers_75'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
@@ -248,7 +248,7 @@
          'infinite_scroll_element_n_layers.html?layer_count=53')
 
 
-# Why: Infinite root scroller with 95%-ile layer count"""
+# Why: Infinite non-root scroller with 95%-ile layer count"""
 class InfiniteScrollElementNLayers95(InfiniteScrollElementNLayersPage):
   BASE_NAME = 'infinite_scroll_element_n_layers_95'
   SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
@@ -256,7 +256,7 @@
          'infinite_scroll_element_n_layers.html?layer_count=144')
 
 
-# Why: Infinite root scroller with 99%-ile layer count"""
+# Why: Infinite non-root scroller with 99%-ile layer count"""
 class InfiniteScrollElementNLayers99(InfiniteScrollElementNLayersPage):
   BASE_NAME = 'infinite_scroll_element_n_layers_99'
   SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
@@ -272,7 +272,7 @@
                                speed_in_pixels_per_second=1000)
 
 
-# Why: Infinite non-root scroller with 1 layer baseline """
+# Why: Infinite root scroller with 1 layer baseline """
 class InfiniteScrollRootNLayers0(InfiniteScrollRootNLayersPage):
   BASE_NAME = 'infinite_scroll_root_n_layers_0'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
@@ -280,7 +280,7 @@
          'infinite_scroll_root_n_layers.html?layer_count=1')
 
 
-# Why: Infinite non-root scroller with 50%-ile layer count"""
+# Why: Infinite root scroller with 50%-ile layer count"""
 class InfiniteScrollRootNLayers50(InfiniteScrollRootNLayersPage):
   BASE_NAME = 'infinite_scroll_root_n_layers_50'
   SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
@@ -288,7 +288,7 @@
          'infinite_scroll_root_n_layers.html?layer_count=31')
 
 
-# Why: Infinite non-root scroller with 75%-ile layer count"""
+# Why: Infinite root scroller with 75%-ile layer count"""
 class InfiniteScrollRootNLayers75(InfiniteScrollRootNLayersPage):
   BASE_NAME = 'infinite_scroll_root_n_layers_75'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
@@ -296,7 +296,7 @@
          'infinite_scroll_root_n_layers.html?layer_count=53')
 
 
-# Why: Infinite non-root scroller with 95%-ile layer count"""
+# Why: Infinite root scroller with 95%-ile layer count"""
 class InfiniteScrollRootNLayers95(InfiniteScrollRootNLayersPage):
   BASE_NAME = 'infinite_scroll_root_n_layers_95'
   SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
@@ -304,9 +304,49 @@
          'infinite_scroll_root_n_layers.html?layer_count=144')
 
 
-# Why: Infinite non-root scroller with 99%-ile layer count"""
+# Why: Infinite root scroller with 99%-ile layer count"""
 class InfiniteScrollRootNLayers99(InfiniteScrollRootNLayersPage):
   BASE_NAME = 'infinite_scroll_root_n_layers_99'
   SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
   URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
          'infinite_scroll_root_n_layers.html?layer_count=306')
+
+
+# Why: Infinite root scroller + fixed element, with 1 layer baseline """
+class InfiniteScrollRootFixedNLayers0(InfiniteScrollRootNLayersPage):
+  BASE_NAME = 'infinite_scroll_root_fixed_n_layers_0'
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
+         'infinite_scroll_root_fixed_n_layers.html?layer_count=1')
+
+
+# Why: Infinite root scroller + fixed element, with 50%-ile layer count"""
+class InfiniteScrollRootFixedNLayers50(InfiniteScrollRootNLayersPage):
+  BASE_NAME = 'infinite_scroll_root_fixed_n_layers_50'
+  SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
+         'infinite_scroll_root_fixed_n_layers.html?layer_count=31')
+
+
+# Why: Infinite root scroller + fixed element, with 75%-ile layer count"""
+class InfiniteScrollRootFixedNLayers75(InfiniteScrollRootNLayersPage):
+  BASE_NAME = 'infinite_scroll_root_fixed_n_layers_75'
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
+         'infinite_scroll_root_fixed_n_layers.html?layer_count=53')
+
+
+# Why: Infinite root scroller + fixed element, with 95%-ile layer count"""
+class InfiniteScrollRootFixedNLayers95(InfiniteScrollRootNLayersPage):
+  BASE_NAME = 'infinite_scroll_root_fixed_n_layers_95'
+  SUPPORTED_PLATFORMS = platforms.NO_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
+         'infinite_scroll_root_fixed_n_layers.html?layer_count=144')
+
+
+# Why: Infinite root scroller + fixed element, with 99%-ile layer count"""
+class InfiniteScrollRootFixedNLayers99(InfiniteScrollRootNLayersPage):
+  BASE_NAME = 'infinite_scroll_root_fixed_n_layers_99'
+  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/tough_compositor_cases/'
+         'infinite_scroll_root_fixed_n_layers.html?layer_count=306')
diff --git a/ui/base/dragdrop/os_exchange_data_provider_mac.mm b/ui/base/dragdrop/os_exchange_data_provider_mac.mm
index 334bc9c..ff55487 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_mac.mm
+++ b/ui/base/dragdrop/os_exchange_data_provider_mac.mm
@@ -15,6 +15,7 @@
 #import "ui/base/clipboard/clipboard_util_mac.h"
 #include "ui/base/clipboard/custom_data_helper.h"
 #import "ui/base/dragdrop/cocoa_dnd_util.h"
+#include "ui/base/dragdrop/file_info.h"
 #include "url/gurl.h"
 
 namespace ui {
@@ -64,7 +65,16 @@
 
 void OSExchangeDataProviderMac::SetFilenames(
     const std::vector<FileInfo>& filenames) {
-  NOTIMPLEMENTED();
+  if (filenames.empty())
+    return;
+
+  NSMutableArray* paths = [NSMutableArray arrayWithCapacity:filenames.size()];
+
+  for (const auto& filename : filenames) {
+    NSString* path = base::SysUTF8ToNSString(filename.path.value());
+    [paths addObject:path];
+  }
+  [pasteboard_->get() setPropertyList:paths forType:NSFilenamesPboardType];
 }
 
 void OSExchangeDataProviderMac::SetPickledData(
@@ -137,8 +147,16 @@
 
 bool OSExchangeDataProviderMac::GetFilenames(
     std::vector<FileInfo>* filenames) const {
-  NOTIMPLEMENTED();
-  return false;
+  NSArray* paths =
+      [pasteboard_->get() propertyListForType:NSFilenamesPboardType];
+  if ([paths count] == 0)
+    return false;
+
+  for (NSString* path in paths)
+    filenames->push_back(
+        {base::FilePath(base::SysNSStringToUTF8(path)), base::FilePath()});
+
+  return true;
 }
 
 bool OSExchangeDataProviderMac::GetPickledData(
diff --git a/ui/base/dragdrop/os_exchange_data_unittest.cc b/ui/base/dragdrop/os_exchange_data_unittest.cc
index 62d98d1..9291ca57 100644
--- a/ui/base/dragdrop/os_exchange_data_unittest.cc
+++ b/ui/base/dragdrop/os_exchange_data_unittest.cc
@@ -12,6 +12,7 @@
 #include "net/base/filename_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
+#include "ui/base/dragdrop/file_info.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "url/gurl.h"
@@ -164,6 +165,30 @@
   EXPECT_EQ(2, value);
 }
 
+TEST_F(OSExchangeDataTest, TestFilenames) {
+#if defined(OS_WIN)
+  const std::vector<FileInfo> kTestFilenames = {
+      {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file1")),
+       base::FilePath()},
+      {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file2")),
+       base::FilePath()},
+  };
+#else
+  const std::vector<FileInfo> kTestFilenames = {
+      {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file1")), base::FilePath()},
+      {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file2")), base::FilePath()},
+  };
+#endif
+  OSExchangeData data;
+  data.SetFilenames(kTestFilenames);
+
+  OSExchangeData copy(data.provider().Clone());
+  std::vector<FileInfo> dropped_filenames;
+
+  EXPECT_TRUE(copy.GetFilenames(&dropped_filenames));
+  EXPECT_EQ(kTestFilenames, dropped_filenames);
+}
+
 #if defined(USE_AURA)
 TEST_F(OSExchangeDataTest, TestHTML) {
   OSExchangeData data;
diff --git a/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
index 3d6a92fd..09bf9b0d 100644
--- a/ui/base/dragdrop/os_exchange_data_win_unittest.cc
+++ b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
@@ -304,23 +304,6 @@
   EXPECT_EQ(file_contents, read_contents);
 }
 
-TEST(OSExchangeDataWinTest, Filenames) {
-  OSExchangeData data;
-  const std::vector<FileInfo> kTestFilenames = {
-      {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file1")),
-       base::FilePath()},
-      {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file2")),
-       base::FilePath()},
-  };
-  data.SetFilenames(kTestFilenames);
-
-  OSExchangeData copy(data.provider().Clone());
-  std::vector<FileInfo> dropped_filenames;
-
-  EXPECT_TRUE(copy.GetFilenames(&dropped_filenames));
-  EXPECT_EQ(kTestFilenames, dropped_filenames);
-}
-
 TEST(OSExchangeDataWinTest, CFHtml) {
   OSExchangeData data;
   GURL url("http://www.google.com/");
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 65ded87..ee659a97 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -155,55 +155,59 @@
 }
 
 jumbo_component("events") {
-  sources = [
+  public = [
     "cocoa/cocoa_event_utils.h",
-    "cocoa/cocoa_event_utils.mm",
-    "cocoa/events_mac.mm",
-    "event.cc",
     "event.h",
-    "event_dispatcher.cc",
     "event_dispatcher.h",
-    "event_handler.cc",
     "event_handler.h",
-    "event_modifiers.cc",
     "event_modifiers.h",
-    "event_processor.cc",
     "event_processor.h",
-    "event_rewriter.cc",
     "event_rewriter.h",
     "event_sink.h",
-    "event_source.cc",
     "event_source.h",
-    "event_target.cc",
     "event_target.h",
     "event_target_iterator.h",
     "event_targeter.h",
-    "event_utils.cc",
     "event_utils.h",
     "events_export.h",
-    "events_stub.cc",
-    "gestures/gesture_recognizer_impl_mac.cc",
     "gestures/gesture_recognizer_impl_mac.h",
-    "gestures/gesture_types.cc",
     "gestures/gesture_types.h",
     "keyboard_hook.h",
+    "null_event_targeter.h",
+    "scoped_target_handler.h",
+    "system_input_injector.h",
+    "win/events_win_utils.h",
+    "win/system_event_state_lookup.h",
+  ]
+
+  sources = [
+    "cocoa/cocoa_event_utils.mm",
+    "cocoa/events_mac.mm",
+    "event.cc",
+    "event_dispatcher.cc",
+    "event_handler.cc",
+    "event_modifiers.cc",
+    "event_processor.cc",
+    "event_rewriter.cc",
+    "event_source.cc",
+    "event_target.cc",
+    "event_utils.cc",
+    "events_stub.cc",
+    "gestures/gesture_recognizer_impl_mac.cc",
+    "gestures/gesture_types.cc",
     "keyboard_hook_base.cc",
     "keyboard_hook_base.h",
     "keycodes/platform_key_map_win.cc",
     "keycodes/platform_key_map_win.h",
     "mac/keyboard_hook_mac.mm",
     "null_event_targeter.cc",
-    "null_event_targeter.h",
     "scoped_target_handler.cc",
-    "scoped_target_handler.h",
     "system_input_injector.cc",
-    "system_input_injector.h",
     "win/events_win.cc",
     "win/events_win_utils.cc",
-    "win/events_win_utils.h",
     "win/keyboard_hook_win.cc",
+    "win/keyboard_hook_win.h",
     "win/system_event_state_lookup.cc",
-    "win/system_event_state_lookup.h",
   ]
 
   defines = [ "EVENTS_IMPLEMENTATION" ]
@@ -222,6 +226,12 @@
     "//ui/gfx/geometry",
   ]
 
+  # Expose the internals of this target to other packages in this BUILD file
+  # so the unit tests can access the private header files.
+  # Note: Only 'events_unittests' needs access in this file, however it uses a
+  # template which generates different target names on different platforms.
+  friend = [ ":*" ]
+
   if (use_x11) {
     sources += [
       "x/events_x.cc",
@@ -249,24 +259,26 @@
   }
 
   if (use_ozone) {
+    public += [ "ozone/events_ozone.h" ]
     sources += [
       "ozone/events_ozone.cc",
-      "ozone/events_ozone.h",
       "ozone/keyboard_hook_ozone.cc",
     ]
     deps += [ "//ui/events/ozone:events_ozone_layout" ]
   }
 
   if (use_aura) {
-    sources += [
-      "gestures/gesture_provider_aura.cc",
+    public += [
       "gestures/gesture_provider_aura.h",
       "gestures/gesture_recognizer.h",
-      "gestures/gesture_recognizer_impl.cc",
       "gestures/gesture_recognizer_impl.h",
-      "gestures/motion_event_aura.cc",
       "gestures/motion_event_aura.h",
     ]
+    sources += [
+      "gestures/gesture_provider_aura.cc",
+      "gestures/gesture_recognizer_impl.cc",
+      "gestures/motion_event_aura.cc",
+    ]
   }
 
   if (is_win || is_mac || use_x11 || use_ozone) {
@@ -274,21 +286,23 @@
   }
 
   if (is_android) {
-    sources += [
-      "android/drag_event_android.cc",
+    public += [
       "android/drag_event_android.h",
-      "android/event_handler_android.cc",
       "android/event_handler_android.h",
-      "android/gesture_event_android.cc",
       "android/gesture_event_android.h",
       "android/gesture_event_type.h",
-      "android/key_event_android.cc",
       "android/key_event_android.h",
-      "android/key_event_utils.cc",
       "android/key_event_utils.h",
+      "android/motion_event_android.h",
+    ]
+    sources += [
+      "android/drag_event_android.cc",
+      "android/event_handler_android.cc",
+      "android/gesture_event_android.cc",
+      "android/key_event_android.cc",
+      "android/key_event_utils.cc",
       "android/keyboard_hook_android.cc",
       "android/motion_event_android.cc",
-      "android/motion_event_android.h",
     ]
     deps += [
       ":keyevent_jni_headers",
@@ -301,11 +315,11 @@
   }
 
   if (is_fuchsia) {
-    sources += [
-      "fuchsia/input_event_dispatcher.cc",
+    public += [
       "fuchsia/input_event_dispatcher.h",
       "fuchsia/input_event_dispatcher_delegate.h",
     ]
+    sources += [ "fuchsia/input_event_dispatcher.cc" ]
     public_deps += [ "//third_party/fuchsia-sdk/sdk:input" ]
   }
 }
@@ -488,6 +502,7 @@
       "platform/platform_event_source_unittest.cc",
       "scoped_target_handler_unittest.cc",
       "win/event_utils_win_unittest.cc",
+      "win/keyboard_hook_win_unittest.cc",
     ]
 
     deps = [
diff --git a/ui/events/blink/blink_features.cc b/ui/events/blink/blink_features.cc
index 46384f2..a88686a 100644
--- a/ui/events/blink/blink_features.cc
+++ b/ui/events/blink/blink_features.cc
@@ -19,4 +19,7 @@
 
 const base::Feature kNoHoverAfterLayoutChange{
     "NoHoverAfterLayoutChange", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kNoHoverDuringScroll{"NoHoverDuringScroll",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
 }
diff --git a/ui/events/blink/blink_features.h b/ui/events/blink/blink_features.h
index 7a82663..c7ee6ef 100644
--- a/ui/events/blink/blink_features.h
+++ b/ui/events/blink/blink_features.h
@@ -20,8 +20,15 @@
 // the native platforms. crbug.com/450631
 extern const base::Feature kSendMouseLeaveEvents;
 
-// Do not update hover after the layout is changed.
+// When enabled, this feature prevents Blink from changing the hover state and
+// dispatching mouse enter/exit events for elements under the mouse after the
+// layout under the mouse cursor is changed.
 extern const base::Feature kNoHoverAfterLayoutChange;
+
+// When enabled, this feature prevents Blink from changing the hover state and
+// dispatching mouse enter/exit events for elements under the mouse as the page
+// is scrolled.
+extern const base::Feature kNoHoverDuringScroll;
 }
 
 #endif  // UI_EVENTS_BLINK_BLINK_FEATURES_H_
diff --git a/ui/events/keyboard_hook.h b/ui/events/keyboard_hook.h
index accb805..36e9b88 100644
--- a/ui/events/keyboard_hook.h
+++ b/ui/events/keyboard_hook.h
@@ -38,7 +38,7 @@
       KeyEventCallback callback);
 
   // True if |dom_code| is reserved for an active KeyboardLock request.
-  virtual bool IsKeyLocked(DomCode dom_code) = 0;
+  virtual bool IsKeyLocked(DomCode dom_code) const = 0;
 };
 
 }  // namespace ui
diff --git a/ui/events/keyboard_hook_base.cc b/ui/events/keyboard_hook_base.cc
index 6f971814..56381738 100644
--- a/ui/events/keyboard_hook_base.cc
+++ b/ui/events/keyboard_hook_base.cc
@@ -23,7 +23,7 @@
 
 KeyboardHookBase::~KeyboardHookBase() = default;
 
-bool KeyboardHookBase::IsKeyLocked(DomCode dom_code) {
+bool KeyboardHookBase::IsKeyLocked(DomCode dom_code) const {
   return ShouldCaptureKeyEvent(dom_code);
 }
 
diff --git a/ui/events/keyboard_hook_base.h b/ui/events/keyboard_hook_base.h
index 8d18d942..81ffae2 100644
--- a/ui/events/keyboard_hook_base.h
+++ b/ui/events/keyboard_hook_base.h
@@ -22,7 +22,7 @@
   ~KeyboardHookBase() override;
 
   // KeyboardHook implementation.
-  bool IsKeyLocked(DomCode dom_code) override;
+  bool IsKeyLocked(DomCode dom_code) const override;
 
  protected:
   // Indicates whether |dom_code| should be intercepted by the keyboard hook.
diff --git a/ui/events/win/keyboard_hook_win.cc b/ui/events/win/keyboard_hook_win.cc
index c5fa97e..042e2956 100644
--- a/ui/events/win/keyboard_hook_win.cc
+++ b/ui/events/win/keyboard_hook_win.cc
@@ -2,12 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/events/keyboard_hook_base.h"
+#include "ui/events/win/keyboard_hook_win.h"
 
 #include <utility>
 
-#include <windows.h>
-
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/optional.h"
@@ -16,30 +14,112 @@
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/win/events_win_utils.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace ui {
 
 namespace {
 
-// These keys interfere with the return value of ::GetKeyState() as observing
-// them directly in LowLevelKeyboardProc will cause their key combinations to be
-// ignored.  As an example, the KeyF event in an Alt + F combination will result
-// in a missing alt-down flag. Since a regular application can successfully
-// receive these keys without using LowLevelKeyboardProc, they can be ignored.
-bool IsOSReservedKey(DWORD vk) {
-  return vk == VK_SHIFT || vk == VK_LSHIFT || vk == VK_RSHIFT ||
-         vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL ||
-         vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU || vk == VK_LWIN ||
-         vk == VK_RWIN || vk == VK_CAPITAL || vk == VK_NUMLOCK ||
-         vk == VK_SCROLL;
+// The Windows KeyboardHook implementation uses a low-level hook in order to
+// intercept system key combinations such as Alt + Tab.  A standard hook or
+// other window message filtering mechanisms will allow us to observe key events
+// but we would be unable to block them or the corresponding system action.
+// There are downsides to this approach as described in the following blurbs.
+
+// Low-level hooks are not given a repeat state for each key event.  This is
+// because the events are intercepted prior to the OS handling them and tracking
+// their states the usual way.  We solve this by tracking the last key seen and
+// modifying the event manually to indicate it is a repeat.  This works for
+// every key except escape which is used to exit fullscreen and tear down the
+// hook.  The quirk is that after the hook is torn down, the first escape key
+// event passed to the window will appear like an initial keydown.  This is
+// because it *is* an initial keydown from Windows' perspective as it was being
+// intercepted before then.
+
+// Intercepting modifier keys (Ctrl, Shift, Win, Etc.) within a low-level
+// keyboard hook will result in an incorrect modifier state reported by the OS
+// for that key.  This is because the hook intercepts the event before the OS
+// has a chance to observe the event and update its internal state.  This
+// trade-off is necessary as we also want to intercept and prevent specific key
+// combos from taking effect.
+
+// A related side-effect occurs when intercepting printable characters.  Since
+// the key event is intercepted before the OS handles it, no char events are
+// produced.  So if we intercept scan codes which should generate a printable
+// character, the result is that the key up/down events are sent correctly but
+// no WM_CHAR message is generated.  This is unacceptable for some usages of the
+// hook as the behavior is very different between locked and unlocked states.
+
+// This is a fair number of downsides however the ability to block system key
+// combos is a hard requirement so we need to work around them.
+
+// The solution we have adopted is:
+// - Only intercept modifiers and allow all other keys to pass through
+//   Note: Shift is not included as otherwise it is not applied by the OS and
+//         the printable characters generated by the key event will be wrong.
+// - Track the repeat state ourselves
+// - Update the per-thread key state for the tracked modifiers using
+//   SetKeyboardState().
+
+// In practice this works well as intercepting the modifiers allows us to block
+// system keyboard combos and all other keys generate the proper events and
+// printable chars.
+
+// Using SetKeyboardState() allows us to tell Windows the current state for the
+// modifiers we intercept.  This state only works for the current thread,
+// meaning that other applications / threads which check the key state for the
+// modifiers will not receive an accurate value.  One constraint for the
+// KeyboardHook is that it is only engaged when our window is fullscreen and
+// focused so we don't need to worry too much about other apps.
+
+// Represents a VK_LCONTROL scancode with the 0x02 flag in the high word.
+// The 0x02 flag does not seem to be documented on MSDN or in the public Windows
+// headers.  I suspect it is related to the KF_ family of constants which skips
+// from 0x0100 to 0x0800 with 0x0200 and 0x0400 undocumented (likely reserved).
+constexpr DWORD kSynthesizedControlScanCodeForAltGr = 0x021D;
+
+// {Get|Set}KeyboardState() receives an array with 256 elements.  This is
+// described in MSDN but no constant exists for this value.  Function reference:
+// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getkeyboardstate
+// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setkeyboardstate
+constexpr int kKeyboardStateArraySize = 256;
+
+// Used for setting and testing the bits in the KeyboardState array.
+constexpr BYTE kKeyDown = 0x80;
+constexpr BYTE kKeyUp = 0x00;
+
+bool IsAltKey(DWORD vk) {
+  return vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU;
 }
 
-class KeyboardHookWin : public KeyboardHookBase {
+bool IsControlKey(DWORD vk) {
+  return vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL;
+}
+
+bool IsWindowsKey(DWORD vk) {
+  return vk == VK_LWIN || vk == VK_RWIN;
+}
+
+bool IsModifierKey(DWORD vk) {
+  // We don't track the state of the shift key as we want to allow the OS to
+  // know about its state so it is correctly applied to printable characters.
+  return IsAltKey(vk) || IsControlKey(vk) || IsWindowsKey(vk);
+}
+
+class KeyboardHookWinImpl : public KeyboardHookWin {
  public:
-  KeyboardHookWin(base::Optional<base::flat_set<DomCode>> dom_codes,
-                  KeyEventCallback callback);
-  ~KeyboardHookWin() override;
+  KeyboardHookWinImpl(base::Optional<base::flat_set<DomCode>> dom_codes,
+                      KeyEventCallback callback,
+                      bool enable_hook_registration);
+  ~KeyboardHookWinImpl() override;
+
+  // KeyboardHookWin implementation.
+  bool ProcessKeyEventMessage(WPARAM w_param,
+                              DWORD vk,
+                              DWORD scan_code,
+                              DWORD time_stamp) override;
 
   bool Register();
 
@@ -47,30 +127,50 @@
   static LRESULT CALLBACK ProcessKeyEvent(int code,
                                           WPARAM w_param,
                                           LPARAM l_param);
-  static KeyboardHookWin* instance_;
+
+  void UpdateModifierState(DWORD vk, bool key_down);
+
+  void ClearModifierStates();
+
+  static KeyboardHookWinImpl* instance_;
 
   THREAD_CHECKER(thread_checker_);
 
   HHOOK hook_ = nullptr;
 
-  // Tracks the last key down seen in order to determine if the current key
-  // event is a repeated key press.
+  // Tracks the last non-located key down seen in order to determine if the
+  // current key event should be marked as a repeated key press.
   DWORD last_key_down_ = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWin);
+  // Tracks the number of AltGr key sequences seen since the start of the most
+  // recent AltGr key down.  When the AltGr key is pressed, Windows injects a
+  // synthesized left control key event followed by the right alt key event.
+  // This sequence occurs on the initial keypress and every repeat.
+  int altgr_sequence_count_ = 0;
+
+  const bool enable_hook_registration_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinImpl);
 };
 
 // static
-KeyboardHookWin* KeyboardHookWin::instance_ = nullptr;
+KeyboardHookWinImpl* KeyboardHookWinImpl::instance_ = nullptr;
 
-KeyboardHookWin::KeyboardHookWin(
+KeyboardHookWinImpl::KeyboardHookWinImpl(
     base::Optional<base::flat_set<DomCode>> dom_codes,
-    KeyEventCallback callback)
-    : KeyboardHookBase(std::move(dom_codes), std::move(callback)) {}
+    KeyEventCallback callback,
+    bool enable_hook_registration)
+    : KeyboardHookWin(std::move(dom_codes), std::move(callback)),
+      enable_hook_registration_(enable_hook_registration) {}
 
-KeyboardHookWin::~KeyboardHookWin() {
+KeyboardHookWinImpl::~KeyboardHookWinImpl() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  ClearModifierStates();
+
+  if (!enable_hook_registration_)
+    return;
+
   DCHECK_EQ(instance_, this);
   instance_ = nullptr;
 
@@ -78,9 +178,12 @@
     DPLOG(ERROR) << "UnhookWindowsHookEx failed";
 }
 
-bool KeyboardHookWin::Register() {
+bool KeyboardHookWinImpl::Register() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  // If the hook was created for testing, |Register()| should not be called.
+  DCHECK(enable_hook_registration_);
+
   // Only one instance of this class can be registered at a time.
   DCHECK(!instance_);
   instance_ = this;
@@ -89,7 +192,7 @@
   // which installed it.
   hook_ = SetWindowsHookEx(
       WH_KEYBOARD_LL,
-      reinterpret_cast<HOOKPROC>(&KeyboardHookWin::ProcessKeyEvent),
+      reinterpret_cast<HOOKPROC>(&KeyboardHookWinImpl::ProcessKeyEvent),
       /*hMod=*/nullptr,
       /*dwThreadId=*/0);
   DPLOG_IF(ERROR, !hook_) << "SetWindowsHookEx failed";
@@ -97,10 +200,127 @@
   return hook_ != nullptr;
 }
 
+void KeyboardHookWinImpl::ClearModifierStates() {
+  BYTE keyboard_state[kKeyboardStateArraySize] = {0};
+  if (!GetKeyboardState(keyboard_state)) {
+    DPLOG(ERROR) << "GetKeyboardState() failed: ";
+    return;
+  }
+
+  // Reset each modifier position.
+  keyboard_state[VK_CONTROL] = kKeyUp;
+  keyboard_state[VK_LCONTROL] = kKeyUp;
+  keyboard_state[VK_RCONTROL] = kKeyUp;
+  keyboard_state[VK_MENU] = kKeyUp;
+  keyboard_state[VK_LMENU] = kKeyUp;
+  keyboard_state[VK_RMENU] = kKeyUp;
+  keyboard_state[VK_LWIN] = kKeyUp;
+  keyboard_state[VK_RWIN] = kKeyUp;
+
+  if (!SetKeyboardState(keyboard_state))
+    DPLOG(ERROR) << "SetKeyboardState() failed: ";
+}
+
+bool KeyboardHookWinImpl::ProcessKeyEventMessage(WPARAM w_param,
+                                                 DWORD vk,
+                                                 DWORD scan_code,
+                                                 DWORD time_stamp) {
+  // The |vk| delivered to the low-level hook includes a location which is
+  // needed to track individual keystates such as when both left and right
+  // control keys are pressed.  Make sure that location information was retained
+  // when the vkey was passed into this method.
+  DCHECK_NE(vk, static_cast<DWORD>(VK_CONTROL));
+  DCHECK_NE(vk, static_cast<DWORD>(VK_MENU));
+
+  if (!IsModifierKey(vk))
+    return false;
+
+  // Windows synthesizes an additional key event when AltGr is pressed.  The
+  // event has the left control scan code in the low word and a 0x02 flag set in
+  // the high word.  The VK_RMENU event will be sent once this key is processed.
+  bool is_altgr_sequence = false;
+  if (scan_code == kSynthesizedControlScanCodeForAltGr) {
+    is_altgr_sequence = true;
+    scan_code = KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_LEFT);
+  }
+
+  // If the caller has only requested that Alt be captured, then we don't want
+  // to bail early on the injected control event.
+  DomCode dom_code = DomCode::NONE;
+  if (is_altgr_sequence)
+    dom_code = DomCode::ALT_RIGHT;
+  else
+    dom_code = KeycodeConverter::NativeKeycodeToDomCode(scan_code);
+
+  if (!ShouldCaptureKeyEvent(dom_code))
+    return false;
+
+  if (is_altgr_sequence)
+    altgr_sequence_count_++;
+  else if (vk != VK_RMENU)
+    altgr_sequence_count_ = 0;
+
+  // The Windows key is always located, the other modifiers are not.
+  DWORD non_located_vk =
+      IsWindowsKey(vk)
+          ? vk
+          : LocatedToNonLocatedKeyboardCode(static_cast<KeyboardCode>(vk));
+
+  bool is_repeat = false;
+  MSG msg = {nullptr, w_param, non_located_vk, GetLParamFromScanCode(scan_code),
+             time_stamp};
+  EventType event_type = EventTypeFromMSG(msg);
+  if (event_type == ET_KEY_PRESSED) {
+    UpdateModifierState(vk, /*key_down=*/true);
+    // We use the non-located vkey to determine whether a key event is a repeat
+    // or not.  The exception is for AltGr which has a two key sequence which
+    // alternates.
+    is_repeat = (last_key_down_ == non_located_vk) || altgr_sequence_count_ > 1;
+    last_key_down_ = non_located_vk;
+  } else {
+    DCHECK_EQ(event_type, ET_KEY_RELEASED);
+    UpdateModifierState(vk, /*key_down=*/false);
+    altgr_sequence_count_ = 0;
+    last_key_down_ = 0;
+  }
+
+  std::unique_ptr<KeyEvent> key_event =
+      std::make_unique<KeyEvent>(KeyEventFromMSG(msg));
+  if (is_repeat)
+    key_event->set_flags(key_event->flags() | EF_IS_REPEAT);
+  ForwardCapturedKeyEvent(std::move(key_event));
+
+  return true;
+}
+
+void KeyboardHookWinImpl::UpdateModifierState(DWORD vk, bool is_key_down) {
+  BYTE keyboard_state[kKeyboardStateArraySize] = {0};
+  if (!GetKeyboardState(keyboard_state)) {
+    DPLOG(ERROR) << "GetKeyboardState() failed: ";
+    return;
+  }
+
+  // Update the located virtual key first.
+  keyboard_state[vk] = is_key_down ? kKeyDown : kKeyUp;
+
+  // Now update the non-located virtual key.
+  keyboard_state[VK_CONTROL] = (keyboard_state[VK_LCONTROL] == kKeyDown ||
+                                keyboard_state[VK_RCONTROL] == kKeyDown)
+                                   ? kKeyDown
+                                   : kKeyUp;
+  keyboard_state[VK_MENU] = (keyboard_state[VK_LMENU] == kKeyDown ||
+                             keyboard_state[VK_RMENU] == kKeyDown)
+                                ? kKeyDown
+                                : kKeyUp;
+
+  if (!SetKeyboardState(keyboard_state))
+    PLOG(ERROR) << "SetKeyboardState() failed: ";
+}
+
 // static
-LRESULT CALLBACK KeyboardHookWin::ProcessKeyEvent(int code,
-                                                  WPARAM w_param,
-                                                  LPARAM l_param) {
+LRESULT CALLBACK KeyboardHookWinImpl::ProcessKeyEvent(int code,
+                                                      WPARAM w_param,
+                                                      LPARAM l_param) {
   // If there is an error unhooking, this method could be called with a null
   // |instance_|.  Ensure we have a valid instance and that |code| is correct
   // before proceeding.
@@ -110,33 +330,20 @@
   DCHECK_CALLED_ON_VALID_THREAD(instance_->thread_checker_);
 
   KBDLLHOOKSTRUCT* ll_hooks = reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param);
-  DomCode dom_code =
-      KeycodeConverter::NativeKeycodeToDomCode(ll_hooks->scanCode);
-  if (!IsOSReservedKey(ll_hooks->vkCode) &&
-      instance_->ShouldCaptureKeyEvent(dom_code)) {
-    MSG msg = {nullptr, static_cast<UINT>(w_param), ll_hooks->vkCode,
-               (ll_hooks->scanCode << 16) | (ll_hooks->flags & 0xFFFF),
-               ll_hooks->time};
-    KeyEvent key_event = KeyEventFromMSG(msg);
 
-    // Determine if this key event represents a repeated key event.
-    // A low level KB Hook is not passed a parameter or flag which indicates
-    // whether the key event is a repeat or not as it is called very early in
-    // input handling pipeline. That means we need to apply our own heuristic
-    // to determine if it should be treated as a repeated key press or not.
-    if (key_event.type() == ET_KEY_PRESSED) {
-      if (instance_->last_key_down_ != 0 &&
-          instance_->last_key_down_ == ll_hooks->vkCode) {
-        key_event.set_flags(key_event.flags() | EF_IS_REPEAT);
-      }
-      instance_->last_key_down_ = ll_hooks->vkCode;
-    } else {
-      DCHECK_EQ(key_event.type(), ET_KEY_RELEASED);
-      instance_->last_key_down_ = 0;
-    }
-    instance_->ForwardCapturedKeyEvent(std::make_unique<KeyEvent>(key_event));
+  // This vkey represents both a vkey and a location on the keyboard such as
+  // VK_LCONTROL or VK_RCONTROL.
+  DWORD vk = ll_hooks->vkCode;
+
+  // Apply the extended flag prior to passing |scan_code| since |instance_| does
+  // not have access to the low-level hook flags.
+  DWORD scan_code = ll_hooks->scanCode;
+  if (ll_hooks->flags & LLKHF_EXTENDED)
+    scan_code |= 0xE000;
+
+  if (instance_->ProcessKeyEventMessage(w_param, vk, scan_code, ll_hooks->time))
     return 1;
-  }
+
   return CallNextHookEx(nullptr, code, w_param, l_param);
 }
 
@@ -147,9 +354,10 @@
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyEventCallback callback) {
-  std::unique_ptr<KeyboardHookWin> keyboard_hook =
-      std::make_unique<KeyboardHookWin>(std::move(dom_codes),
-                                        std::move(callback));
+  std::unique_ptr<KeyboardHookWinImpl> keyboard_hook =
+      std::make_unique<KeyboardHookWinImpl>(std::move(dom_codes),
+                                            std::move(callback),
+                                            /*enable_hook_registration=*/true);
 
   if (!keyboard_hook->Register())
     return nullptr;
@@ -157,4 +365,19 @@
   return keyboard_hook;
 }
 
+std::unique_ptr<KeyboardHookWin> KeyboardHookWin::CreateForTesting(
+    base::Optional<base::flat_set<DomCode>> dom_codes,
+    KeyEventCallback callback) {
+  return std::make_unique<KeyboardHookWinImpl>(
+      std::move(dom_codes), std::move(callback),
+      /*enable_hook_registration=*/false);
+}
+
+KeyboardHookWin::KeyboardHookWin(
+    base::Optional<base::flat_set<DomCode>> dom_codes,
+    KeyEventCallback callback)
+    : KeyboardHookBase(std::move(dom_codes), std::move(callback)) {}
+
+KeyboardHookWin::~KeyboardHookWin() = default;
+
 }  // namespace ui
diff --git a/ui/events/win/keyboard_hook_win.h b/ui/events/win/keyboard_hook_win.h
new file mode 100644
index 0000000..627ba313
--- /dev/null
+++ b/ui/events/win/keyboard_hook_win.h
@@ -0,0 +1,52 @@
+// 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 UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
+#define UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
+
+#include <memory>
+
+#include <windows.h>
+
+#include "base/containers/flat_set.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "ui/events/event.h"
+#include "ui/events/events_export.h"
+#include "ui/events/keyboard_hook_base.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+
+namespace ui {
+
+// Exposes a method to drive the Windows KeyboardHook implementation by feeding
+// it key event data.  This method is used by both the low-level keyboard hook
+// and by unit tests which simulate the hooked behavior w/o actually installing
+// a hook (doing so would cause problems with test parallelization).
+class EVENTS_EXPORT KeyboardHookWin : public KeyboardHookBase {
+ public:
+  KeyboardHookWin(base::Optional<base::flat_set<DomCode>> dom_codes,
+                  KeyEventCallback callback);
+  ~KeyboardHookWin() override;
+
+  // Create a KeyboardHookWin instance which does not register a low-level hook.
+  static std::unique_ptr<KeyboardHookWin> CreateForTesting(
+      base::Optional<base::flat_set<DomCode>> dom_codes,
+      KeyEventCallback callback);
+
+  // Called when a key event message is delivered via the low-level hook.
+  // Exposed here to allow for testing w/o engaging the low-level hook.
+  // Returns true if the message was handled.
+  virtual bool ProcessKeyEventMessage(WPARAM w_param,
+                                      DWORD vk,
+                                      DWORD scan_code,
+                                      DWORD time_stamp) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWin);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
diff --git a/ui/events/win/keyboard_hook_win_unittest.cc b/ui/events/win/keyboard_hook_win_unittest.cc
new file mode 100644
index 0000000..d8f3dea
--- /dev/null
+++ b/ui/events/win/keyboard_hook_win_unittest.cc
@@ -0,0 +1,1085 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/flat_set.h"
+#include "base/optional.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/events/keyboard_hook.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/test/keyboard_layout.h"
+#include "ui/events/win/keyboard_hook_win.h"
+#include "ui/events/win/system_event_state_lookup.h"
+
+namespace ui {
+
+class KeyboardHookWinTest : public testing::Test {
+ public:
+  KeyboardHookWinTest();
+  ~KeyboardHookWinTest() override;
+
+  // testing::Test overrides.
+  void SetUp() override;
+
+  void HandleKeyPress(KeyEvent* key_event);
+
+ protected:
+  KeyboardHookWin* keyboard_hook() { return keyboard_hook_.get(); }
+
+  uint32_t next_time_stamp() { return time_stamp_++; }
+
+  std::vector<KeyEvent>* key_events() { return &key_events_; }
+
+  // Used for sending key events which are handled by the hook.
+  void SendModifierKeyDownEvent(KeyboardCode key_code,
+                                DomCode dom_code,
+                                int repeat_count = 1);
+  void SendModifierKeyUpEvent(KeyboardCode key_code, DomCode dom_code);
+
+  void SetKeyboardLayoutForTest(KeyboardLayout new_layout);
+
+ private:
+  uint32_t time_stamp_ = 0;
+  std::unique_ptr<KeyboardHookWin> keyboard_hook_;
+  std::vector<KeyEvent> key_events_;
+  std::unique_ptr<ScopedKeyboardLayout> keyboard_layout_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinTest);
+};
+
+KeyboardHookWinTest::KeyboardHookWinTest() = default;
+
+KeyboardHookWinTest::~KeyboardHookWinTest() = default;
+
+void KeyboardHookWinTest::SetUp() {
+  keyboard_hook_ = KeyboardHookWin::CreateForTesting(
+      base::Optional<base::flat_set<DomCode>>(),
+      base::BindRepeating(&KeyboardHookWinTest::HandleKeyPress,
+                          base::Unretained(this)));
+
+  keyboard_layout_ = std::make_unique<ScopedKeyboardLayout>(
+      KeyboardLayout::KEYBOARD_LAYOUT_ENGLISH_US);
+}
+
+void KeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) {
+  key_events_.push_back(*key_event);
+}
+
+void KeyboardHookWinTest::SendModifierKeyDownEvent(KeyboardCode key_code,
+                                                   DomCode dom_code,
+                                                   int repeat_count /*=1*/) {
+  // Ensure we have a valid repeat count and the modifer passed in contains
+  // location information.
+  DCHECK_GT(repeat_count, 0);
+  DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL);
+  DCHECK_NE(key_code, KeyboardCode::VKEY_MENU);
+
+  for (int i = 0; i < repeat_count; i++) {
+    ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+        WM_KEYDOWN, key_code,
+        KeycodeConverter::DomCodeToNativeKeycode(dom_code), next_time_stamp()));
+  }
+}
+
+void KeyboardHookWinTest::SendModifierKeyUpEvent(KeyboardCode key_code,
+                                                 DomCode dom_code) {
+  // Ensure we have a valid repeat count and the modifer passed in contains
+  // location information.
+  DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL);
+  DCHECK_NE(key_code, KeyboardCode::VKEY_MENU);
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, key_code, KeycodeConverter::DomCodeToNativeKeycode(dom_code),
+      next_time_stamp()));
+}
+
+void VerifyKeyEvent(KeyEvent* key_event,
+                    KeyboardCode non_located_key_code,
+                    DomCode dom_code,
+                    bool key_down,
+                    bool is_repeat) {
+  if (key_down) {
+    ASSERT_EQ(key_event->type(), ET_KEY_PRESSED);
+    ASSERT_EQ(key_event->is_repeat(), is_repeat);
+  } else {
+    ASSERT_EQ(key_event->type(), ET_KEY_RELEASED);
+    ASSERT_FALSE(key_event->is_repeat());
+  }
+  ASSERT_EQ(key_event->key_code(), non_located_key_code);
+  ASSERT_EQ(key_event->code(), dom_code);
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLeftControlKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
+  const DomCode dom_code = DomCode::CONTROL_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true,
+                 false);
+  ASSERT_TRUE(down_event.IsControlDown());
+  ASSERT_FALSE(down_event.IsAltDown());
+  ASSERT_FALSE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingLeftControlKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
+  const DomCode dom_code = DomCode::CONTROL_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true,
+                   should_repeat);
+    ASSERT_TRUE(event.IsControlDown());
+    ASSERT_FALSE(event.IsAltDown());
+    ASSERT_FALSE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleRightControlKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
+  const DomCode dom_code = DomCode::CONTROL_RIGHT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true,
+                 false);
+  ASSERT_TRUE(down_event.IsControlDown());
+  ASSERT_FALSE(down_event.IsAltDown());
+  ASSERT_FALSE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingRightControlKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
+  const DomCode dom_code = DomCode::CONTROL_RIGHT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true,
+                   should_repeat);
+    ASSERT_TRUE(event.IsControlDown());
+    ASSERT_FALSE(event.IsAltDown());
+    ASSERT_FALSE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLifoControlSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
+  const DomCode left_dom_code = DomCode::CONTROL_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
+  const DomCode right_dom_code = DomCode::CONTROL_RIGHT;
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key down, repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
+                 true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
+                 true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // First key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleFifoControlSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
+  const DomCode left_dom_code = DomCode::CONTROL_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
+  const DomCode right_dom_code = DomCode::CONTROL_RIGHT;
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true,
+                 true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key down, repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // First key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+
+  // Second key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLeftAltKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
+  const DomCode dom_code = DomCode::ALT_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false);
+  ASSERT_FALSE(down_event.IsControlDown());
+  ASSERT_TRUE(down_event.IsAltDown());
+  ASSERT_FALSE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingLeftAltKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
+  const DomCode dom_code = DomCode::ALT_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true,
+                   should_repeat);
+    ASSERT_FALSE(event.IsControlDown());
+    ASSERT_TRUE(event.IsAltDown());
+    ASSERT_FALSE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleRightAltKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode dom_code = DomCode::ALT_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false);
+  ASSERT_FALSE(down_event.IsControlDown());
+  ASSERT_TRUE(down_event.IsAltDown());
+  ASSERT_FALSE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingRightAltKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode dom_code = DomCode::ALT_RIGHT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true,
+                   should_repeat);
+    ASSERT_FALSE(event.IsControlDown());
+    ASSERT_TRUE(event.IsAltDown());
+    ASSERT_FALSE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLifoAltSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
+  const DomCode left_dom_code = DomCode::ALT_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode right_dom_code = DomCode::ALT_RIGHT;
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, false);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key down, repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // First key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false);
+  ASSERT_FALSE(event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleFifoAltSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
+  const DomCode left_dom_code = DomCode::ALT_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode right_dom_code = DomCode::ALT_RIGHT;
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, false);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key down, repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // First key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false);
+  ASSERT_TRUE(event.IsAltDown());
+
+  // Second key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false);
+  ASSERT_FALSE(event.IsAltDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLeftWinKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
+  const DomCode dom_code = DomCode::META_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  // VKEY_LWIN is the 'non-located' version of the Windows key.
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_LWIN, dom_code, true, false);
+  ASSERT_FALSE(down_event.IsControlDown());
+  ASSERT_FALSE(down_event.IsAltDown());
+  ASSERT_TRUE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingLeftWinKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
+  const DomCode dom_code = DomCode::META_LEFT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    // VKEY_LWIN is the 'non-located' version of the Windows key.
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, dom_code, true,
+                   should_repeat);
+    ASSERT_FALSE(event.IsControlDown());
+    ASSERT_FALSE(event.IsAltDown());
+    ASSERT_TRUE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleRightWinKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
+  const DomCode dom_code = DomCode::META_RIGHT;
+  SendModifierKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1ULL);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2ULL);
+
+  KeyEvent down_event = key_events()->at(0);
+  // VKEY_LWIN is the 'non-located' version of the Windows key.
+  VerifyKeyEvent(&down_event, KeyboardCode::VKEY_RWIN, dom_code, true, false);
+  ASSERT_FALSE(down_event.IsControlDown());
+  ASSERT_FALSE(down_event.IsAltDown());
+  ASSERT_TRUE(down_event.IsCommandDown());
+
+  KeyEvent up_event = key_events()->at(1);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingRightWinKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
+  const DomCode dom_code = DomCode::META_RIGHT;
+  SendModifierKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendModifierKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    // VKEY_LWIN is the 'non-located' version of the Windows key.
+    VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, dom_code, true,
+                   should_repeat);
+    ASSERT_FALSE(event.IsControlDown());
+    ASSERT_FALSE(event.IsAltDown());
+    ASSERT_TRUE(event.IsCommandDown());
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false);
+  ASSERT_FALSE(up_event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleLifoWinSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
+  const DomCode left_dom_code = DomCode::META_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
+  const DomCode right_dom_code = DomCode::META_RIGHT;
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key down, no repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // First key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false);
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleFifoWinSequenceTest) {
+  const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
+  const DomCode left_dom_code = DomCode::META_LEFT;
+  const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
+  const DomCode right_dom_code = DomCode::META_RIGHT;
+  SendModifierKeyDownEvent(right_key_code, right_dom_code, 2);
+  SendModifierKeyDownEvent(left_key_code, left_dom_code, 2);
+  SendModifierKeyUpEvent(right_key_code, right_dom_code);
+  SendModifierKeyUpEvent(left_key_code, left_dom_code);
+  ASSERT_EQ(key_events()->size(), 6ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key down, no repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // First key up.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false);
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key up.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false);
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, CombinedModifierLifoSequenceKeypressTest) {
+  const KeyboardCode first_key_code = KeyboardCode::VKEY_LCONTROL;
+  const DomCode first_dom_code = DomCode::CONTROL_LEFT;
+  const KeyboardCode second_key_code = KeyboardCode::VKEY_RWIN;
+  const DomCode second_dom_code = DomCode::META_RIGHT;
+  const KeyboardCode third_key_code = KeyboardCode::VKEY_LMENU;
+  const DomCode third_dom_code = DomCode::ALT_LEFT;
+  SendModifierKeyDownEvent(first_key_code, first_dom_code, 2);
+  SendModifierKeyDownEvent(second_key_code, second_dom_code, 2);
+  SendModifierKeyDownEvent(third_key_code, third_dom_code, 2);
+  SendModifierKeyUpEvent(third_key_code, third_dom_code);
+  SendModifierKeyUpEvent(second_key_code, second_dom_code);
+  SendModifierKeyUpEvent(first_key_code, first_dom_code);
+  ASSERT_EQ(key_events()->size(), 9ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
+                 true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Second key down, no repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Third key down, no repeat.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Third key still down, repeat.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Third key up.
+  event = key_events()->at(6);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key up.
+  event = key_events()->at(7);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, false,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // First key up.
+  event = key_events()->at(8);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, CombinedModifierFifoSequenceKeypressTest) {
+  const KeyboardCode first_key_code = KeyboardCode::VKEY_RCONTROL;
+  const DomCode first_dom_code = DomCode::CONTROL_RIGHT;
+  const KeyboardCode second_key_code = KeyboardCode::VKEY_LWIN;
+  const DomCode second_dom_code = DomCode::META_LEFT;
+  const KeyboardCode third_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode third_dom_code = DomCode::ALT_RIGHT;
+  SendModifierKeyDownEvent(first_key_code, first_dom_code, 2);
+  SendModifierKeyDownEvent(second_key_code, second_dom_code, 2);
+  SendModifierKeyDownEvent(third_key_code, third_dom_code, 2);
+  SendModifierKeyUpEvent(first_key_code, first_dom_code);
+  SendModifierKeyUpEvent(second_key_code, second_dom_code);
+  SendModifierKeyUpEvent(third_key_code, third_dom_code);
+  ASSERT_EQ(key_events()->size(), 9ULL);
+
+  // First key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // First key still down, repeat.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true,
+                 true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Second key down, no repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key still down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Third key down, no repeat.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Third key still down, repeat.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // First key up.
+  event = key_events()->at(6);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_TRUE(event.IsCommandDown());
+
+  // Second key up.
+  event = key_events()->at(7);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_TRUE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Third key up.
+  event = key_events()->at(8);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, VerifyPlatformModifierStateTest) {
+  SendModifierKeyDownEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT);
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_FALSE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyDownEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT);
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_FALSE(win::IsAltPressed());
+  ASSERT_TRUE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyDownEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT);
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsAltRightPressed());
+  ASSERT_TRUE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyUpEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT);
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyUpEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT);
+  ASSERT_FALSE(win::IsCtrlPressed());
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyUpEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT);
+  ASSERT_FALSE(win::IsCtrlPressed());
+  ASSERT_FALSE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyDownEvent(KeyboardCode::VKEY_RMENU, DomCode::ALT_RIGHT);
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_TRUE(win::IsAltRightPressed());
+}
+
+TEST_F(KeyboardHookWinTest, SimpleAltGrKeyPressTest) {
+  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
+
+  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
+  // and an event for VK_RMENU.  We simulate that sequence here.
+  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
+  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
+  const DWORD control_scan_code =
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
+  const DWORD injected_control_scan_code = control_scan_code | 0x0200;
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
+      next_time_stamp()));
+  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
+      next_time_stamp()));
+  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_EQ(key_events()->size(), 4ULL);
+
+  // Injected control key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Altgr key down.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Injected control key up.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // AltGr key up.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, RepeatingAltGrKeyPressTest) {
+  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
+
+  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
+  // and an event for VK_RMENU.  This sequence repeats for each repeated key
+  // press.  We simulate that sequence here.
+  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
+  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
+  const DWORD control_scan_code =
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
+  const DWORD injected_control_scan_code = control_scan_code | 0x0200;
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
+      next_time_stamp()));
+  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
+      next_time_stamp()));
+  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
+      next_time_stamp()));
+  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
+      next_time_stamp()));
+  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_EQ(key_events()->size(), 8ULL);
+
+  // Injected control key down, no repeat.
+  KeyEvent event = key_events()->at(0);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
+                 false);
+  ASSERT_TRUE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Altgr key down.
+  event = key_events()->at(1);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Injected control key down, repeat.
+  event = key_events()->at(2);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
+                 true);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Altgr key down, repeat.
+  event = key_events()->at(3);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Injected control key down, repeat.
+  event = key_events()->at(4);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true,
+                 true);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Altgr key down, repeat.
+  event = key_events()->at(5);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // Injected control key up.
+  event = key_events()->at(6);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false,
+                 false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_TRUE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+
+  // AltGr key up.
+  event = key_events()->at(7);
+  VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false);
+  ASSERT_FALSE(event.IsControlDown());
+  ASSERT_FALSE(event.IsAltDown());
+  ASSERT_FALSE(event.IsAltGrDown());
+  ASSERT_FALSE(event.IsCommandDown());
+}
+
+TEST_F(KeyboardHookWinTest, VerifyAltGrPlatformModifierStateTest) {
+  ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
+
+  // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
+  // and an event for VK_RMENU.  We simulate that sequence here.
+  const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU;
+  const DomCode altgr_dom_code = DomCode::ALT_RIGHT;
+  const DomCode control_dom_code = DomCode::CONTROL_LEFT;
+  const DWORD control_scan_code =
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code);
+  const DWORD injected_control_scan_code = control_scan_code | 0x0200;
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code,
+      next_time_stamp()));
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_FALSE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsAltRightPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_TRUE(win::IsCtrlPressed());
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_TRUE(win::IsAltRightPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_LCONTROL,
+      KeycodeConverter::DomCodeToNativeKeycode(control_dom_code),
+      next_time_stamp()));
+  ASSERT_FALSE(win::IsCtrlPressed());
+  ASSERT_TRUE(win::IsAltPressed());
+  ASSERT_TRUE(win::IsAltRightPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+
+  SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code);
+  ASSERT_FALSE(win::IsCtrlPressed());
+  ASSERT_FALSE(win::IsAltPressed());
+  ASSERT_FALSE(win::IsAltRightPressed());
+  ASSERT_FALSE(win::IsWindowsKeyPressed());
+}
+
+TEST_F(KeyboardHookWinTest, NonInterceptedKeysTest) {
+  // Here we try a few keys we do not expect to be intercepted / handled.
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_RSHIFT,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
+      next_time_stamp()));
+
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE),
+      next_time_stamp()));
+
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_A,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_A,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
+      next_time_stamp()));
+}
+
+}  // namespace ui
diff --git a/ui/file_manager/file_manager/test/crostini.js b/ui/file_manager/file_manager/test/crostini.js
index 1e94667..cde9238 100644
--- a/ui/file_manager/file_manager/test/crostini.js
+++ b/ui/file_manager/file_manager/test/crostini.js
@@ -97,7 +97,7 @@
       .then(() => {
         // Click OK button to close.
         assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container.shown');
+        return test.waitForElementLost('.cr-dialog-container');
       })
       .then(() => {
         // Reset chrome.fileManagerPrivate.mountCrostini.
@@ -215,7 +215,7 @@
             'Let Linux apps open <b>hello.txt</b>.',
             document.querySelector('.cr-dialog-text').innerHTML);
         assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container.shown');
+        return test.waitForElementLost('.cr-dialog-container');
       })
       .then(() => {
         // Ensure fmp.sharePathWithCrostini, fmp.executeTask called.
@@ -275,7 +275,7 @@
                 'first copy to Linux files folder.',
             document.querySelector('.cr-dialog-text').innerText);
         assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container.shown');
+        return test.waitForElementLost('.cr-dialog-container');
       })
       .then(() => {
         // Restore fmp.getFileTasks.
diff --git a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
index 1e36a99..a83ec3e 100644
--- a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
+++ b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
@@ -277,8 +277,9 @@
       }
     }
   }
-  var error =
-      new Error('webkitResolveLocalFileSystemURL not found: [' + url + ']');
+  const message = `webkitResolveLocalFileSystemURL not found: ${url}`;
+  console.warn(message);
+  const error = new DOMException(message, 'NotFoundError');
   if (errorCallback) {
     setTimeout(errorCallback, 0, error);
   } else {
diff --git a/ui/file_manager/integration_tests/file_manager/context_menu.js b/ui/file_manager/integration_tests/file_manager/context_menu.js
index f7364ca..9640afc 100644
--- a/ui/file_manager/integration_tests/file_manager/context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -276,22 +276,6 @@
 };
 
 /**
- * Tests that the New Folder menu item is enabled if a read-write folder is
- * selected.
- */
-testcase.checkNewFolderEnabledForReadWriteFolder = function() {
-  checkContextMenu('new-folder', 'photos', true);
-};
-
-/**
- * Tests that the New Folder menu item is disabled if a read-only folder is
- * selected.
- */
-testcase.checkNewFolderDisabledForReadOnlyFolder = function() {
-  checkContextMenu('new-folder', 'Read-Only Folder', false);
-};
-
-/**
  * Tests that text selection context menus are disabled in tablet mode.
  */
 testcase.checkContextMenusForInputElements = function() {
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 10bfb43..9ae47c3 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -224,6 +224,8 @@
       "skia_vector_animation.cc",
       "skia_vector_animation.h",
       "skia_vector_animation_observer.h",
+      "skottie_wrapper.cc",
+      "skottie_wrapper.h",
     ]
   }
 
diff --git a/ui/gfx/skia_vector_animation.cc b/ui/gfx/skia_vector_animation.cc
index 191e327..8e16dc9 100644
--- a/ui/gfx/skia_vector_animation.cc
+++ b/ui/gfx/skia_vector_animation.cc
@@ -13,6 +13,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/skia_vector_animation_observer.h"
+#include "ui/gfx/skottie_wrapper.h"
 
 namespace gfx {
 
@@ -68,15 +69,8 @@
   return end_offset_.InMillisecondsF() * progress_per_millisecond_;
 }
 
-SkiaVectorAnimation::SkiaVectorAnimation(
-    const scoped_refptr<base::RefCountedMemory>& data_stream) {
-  TRACE_EVENT0("ui", "SkiaVectorAnimation Parse");
-  SkMemoryStream sk_stream(data_stream->front(), data_stream->size());
-  animation_ = skottie::Animation::Make(&sk_stream);
-}
-
-SkiaVectorAnimation::SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream)
-    : animation_(skottie::Animation::Make(stream.get())) {}
+SkiaVectorAnimation::SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie)
+    : skottie_(skottie) {}
 
 SkiaVectorAnimation::~SkiaVectorAnimation() {}
 
@@ -88,13 +82,13 @@
 
 base::TimeDelta SkiaVectorAnimation::GetAnimationDuration() const {
   return base::TimeDelta::FromMilliseconds(
-      std::floor(SkScalarToFloat(animation_->duration()) * 1000.f));
+      std::floor(SkScalarToFloat(skottie_->duration()) * 1000.f));
 }
 
 gfx::Size SkiaVectorAnimation::GetOriginalSize() const {
 #if DCHECK_IS_ON()
   // The size should have no fractional component.
-  gfx::SizeF float_size = gfx::SkSizeToSizeF(animation_->size());
+  gfx::SizeF float_size = gfx::SkSizeToSizeF(skottie_->size());
   gfx::Size rounded_size = gfx::ToRoundedSize(float_size);
 
   float height_diff = std::abs(float_size.height() - rounded_size.height());
@@ -103,7 +97,7 @@
   DCHECK_LE(height_diff, std::numeric_limits<float>::epsilon());
   DCHECK_LE(width_diff, std::numeric_limits<float>::epsilon());
 #endif
-  return gfx::ToRoundedSize(gfx::SkSizeToSizeF(animation_->size()));
+  return gfx::ToRoundedSize(gfx::SkSizeToSizeF(skottie_->size()));
 }
 
 void SkiaVectorAnimation::Start(Style style) {
@@ -158,8 +152,7 @@
       } else {
         // It may be that the timer hasn't been initialized which may happen if
         // the animation was paused while it was in |kScheculePlay| state.
-        return scheduled_start_offset_.InMillisecondsF() /
-               animation_->duration();
+        return scheduled_start_offset_.InMillisecondsF() / skottie_->duration();
       }
     case PlayState::kSchedulePlay:
     case PlayState::kPlaying:
@@ -214,17 +207,16 @@
   DCHECK_GE(t, 0.f);
   DCHECK_LE(t, 1.f);
 
-  animation_->seek(t);
   float scale = canvas->UndoDeviceScaleFactor();
+  gfx::Size pixel_size = gfx::ScaleToRoundedSize(size, scale);
 
   SkBitmap bitmap;
-  bitmap.allocN32Pixels(std::round(size.width() * scale),
-                        std::round(size.height() * scale), false);
+  bitmap.allocN32Pixels(std::round(pixel_size.width()),
+                        std::round(pixel_size.height()), false);
   SkCanvas skcanvas(bitmap);
   skcanvas.clear(SK_ColorTRANSPARENT);
-  SkRect dst = SkRect::MakeXYWH(0, 0, std::round(size.width() * scale),
-                                std::round(size.height() * scale));
-  animation_->render(&skcanvas, &dst);
+
+  skottie_->Draw(&skcanvas, t, pixel_size);
 
   canvas->DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), 0, 0);
 }
diff --git a/ui/gfx/skia_vector_animation.h b/ui/gfx/skia_vector_animation.h
index f0c133f..14081bfd 100644
--- a/ui/gfx/skia_vector_animation.h
+++ b/ui/gfx/skia_vector_animation.h
@@ -20,6 +20,7 @@
 class Canvas;
 class SkiaVectorAnimationTest;
 class SkiaVectorAnimationObserver;
+class SkottieWrapper;
 
 // This class is a wrapper over the Skia object for lottie vector graphic
 // animations. It has its own timeline manager for the animation controls. The
@@ -80,9 +81,7 @@
     kLoop         // Same as LINEAR, except the animation repeats after it ends.
   };
 
-  explicit SkiaVectorAnimation(
-      const scoped_refptr<base::RefCountedMemory>& data_stream);
-  explicit SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream);
+  explicit SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie);
   ~SkiaVectorAnimation();
 
   void SetAnimationObserver(SkiaVectorAnimationObserver* Observer);
@@ -138,6 +137,9 @@
   // paint.
   void PaintFrame(gfx::Canvas* canvas, float t, const gfx::Size& size);
 
+  // Returns the skottie object that contins the animation data.
+  scoped_refptr<SkottieWrapper> skottie() const { return skottie_; }
+
  private:
   friend class SkiaVectorAnimationTest;
 
@@ -229,7 +231,7 @@
 
   SkiaVectorAnimationObserver* observer_ = nullptr;
 
-  sk_sp<skottie::Animation> animation_;
+  scoped_refptr<SkottieWrapper> skottie_;
 
   DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimation);
 };
diff --git a/ui/gfx/skia_vector_animation_unittest.cc b/ui/gfx/skia_vector_animation_unittest.cc
index f9d9737..e7394e2 100644
--- a/ui/gfx/skia_vector_animation_unittest.cc
+++ b/ui/gfx/skia_vector_animation_unittest.cc
@@ -15,6 +15,7 @@
 #include "third_party/skia/include/core/SkStream.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/skia_vector_animation_observer.h"
+#include "ui/gfx/skottie_wrapper.h"
 
 namespace gfx {
 namespace {
@@ -101,6 +102,9 @@
   void SetUp() override {
     canvas_.reset(new gfx::Canvas(gfx::Size(kAnimationWidth, kAnimationHeight),
                                   1.f, false));
+    skottie_ = base::MakeRefCounted<SkottieWrapper>(
+        std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+    animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
   }
 
   void TearDown() override { animation_.reset(nullptr); }
@@ -186,6 +190,7 @@
 
  protected:
   std::unique_ptr<SkiaVectorAnimation> animation_;
+  scoped_refptr<SkottieWrapper> skottie_;
 
  private:
   std::unique_ptr<gfx::Canvas> canvas_;
@@ -197,15 +202,17 @@
 TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
   auto bytes = base::MakeRefCounted<base::RefCountedBytes>(
       std::vector<unsigned char>(kData, kData + std::strlen(kData)));
-  animation_ = std::make_unique<SkiaVectorAnimation>(bytes.get());
+  skottie_ = base::MakeRefCounted<SkottieWrapper>(bytes.get());
+  animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
   EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
   EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
   EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
                   kAnimationDuration);
   EXPECT_TRUE(IsStopped());
 
-  animation_ = std::make_unique<SkiaVectorAnimation>(
+  skottie_ = base::MakeRefCounted<SkottieWrapper>(
       std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
   EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
   EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
   EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
@@ -215,8 +222,6 @@
 
 TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
   TestAnimationObserver observer;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
   animation_->SetAnimationObserver(&observer);
 
   // Advance clock by 300 milliseconds.
@@ -256,8 +261,6 @@
 
 TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) {
   TestAnimationObserver observer;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
   animation_->SetAnimationObserver(&observer);
 
   // Advance clock by 300 milliseconds.
@@ -285,8 +288,6 @@
 
   TestAnimationObserver observer;
 
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
   animation_->SetAnimationObserver(&observer);
 
   // Advance clock by 300 milliseconds.
@@ -344,8 +345,6 @@
   const int start_time_ms = 400;
   const int duration_ms = 1000;
   const float total_duration_ms = kAnimationDuration * 1000.f;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
 
@@ -390,8 +389,6 @@
 
 TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
   TestAnimationObserver observer;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
   animation_->SetAnimationObserver(&observer);
 
   // Advance clock by 300 milliseconds.
@@ -436,8 +433,6 @@
   const int duration_ms = 1000;
   const float total_duration_ms = kAnimationDuration * 1000.f;
 
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
@@ -498,8 +493,6 @@
   const int start_time_ms = 400;
   const int duration_ms = 1000;
   const float total_duration_ms = kAnimationDuration * 1000.f;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
@@ -550,8 +543,6 @@
 }
 
 TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
@@ -607,8 +598,6 @@
   const int duration_ms = 1000;
   const float total_duration_ms = kAnimationDuration * 1000.f;
 
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
@@ -688,8 +677,6 @@
   const int start_time_ms = 400;
   const int duration_ms = 1000;
   const float total_duration_ms = kAnimationDuration * 1000.f;
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   AdvanceClock(200);
 
@@ -782,8 +769,6 @@
 
   // Test to see if the race condition is handled correctly. It may happen that
   // we pause the video before it even starts playing.
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   TestAnimationObserver observer;
   animation_->SetAnimationObserver(&observer);
@@ -814,8 +799,6 @@
 TEST_F(SkiaVectorAnimationTest, PaintTest) {
   std::unique_ptr<gfx::Canvas> canvas(new gfx::Canvas(
       gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false));
-  animation_ = std::make_unique<SkiaVectorAnimation>(
-      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
 
   // Advance clock by 300 milliseconds.
   AdvanceClock(300);
diff --git a/ui/gfx/skottie_wrapper.cc b/ui/gfx/skottie_wrapper.cc
new file mode 100644
index 0000000..1cba085f
--- /dev/null
+++ b/ui/gfx/skottie_wrapper.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 "ui/gfx/skottie_wrapper.h"
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+
+SkottieWrapper::SkottieWrapper(
+    const scoped_refptr<base::RefCountedMemory>& data_stream) {
+  TRACE_EVENT0("ui", "SkottieWrapper Parse");
+  SkMemoryStream sk_stream(data_stream->front(), data_stream->size());
+  animation_ = skottie::Animation::Make(&sk_stream);
+}
+
+SkottieWrapper::SkottieWrapper(std::unique_ptr<SkMemoryStream> stream)
+    : animation_(skottie::Animation::Make(stream.get())) {}
+
+SkottieWrapper::~SkottieWrapper() {}
+
+void SkottieWrapper::Draw(SkCanvas* canvas, float t, const gfx::Size& size) {
+  SkRect dst = SkRect::MakeXYWH(0, 0, size.width(), size.height());
+  {
+    base::AutoLock lock(lock_);
+    animation_->seek(t);
+    animation_->render(canvas, &dst);
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/skottie_wrapper.h b/ui/gfx/skottie_wrapper.h
new file mode 100644
index 0000000..006d0cd2
--- /dev/null
+++ b/ui/gfx/skottie_wrapper.h
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_SKOTTIE_WRAPPER_H_
+#define UI_GFX_SKOTTIE_WRAPPER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "third_party/skia/modules/skottie/include/Skottie.h"
+#include "ui/gfx/gfx_export.h"
+
+class SkCanvas;
+class SkMemoryStream;
+
+namespace base {
+class RefCountedMemory;
+}  // namespace base
+
+namespace gfx {
+class Size;
+
+// A wrapper over Skia's Skottie object that can be shared by multiple
+// SkiaVectorAnimation objects. This class is thread safe when performing a draw
+// on an SkCanvas.
+class GFX_EXPORT SkottieWrapper
+    : public base::RefCountedThreadSafe<SkottieWrapper> {
+ public:
+  explicit SkottieWrapper(
+      const scoped_refptr<base::RefCountedMemory>& data_stream);
+  explicit SkottieWrapper(std::unique_ptr<SkMemoryStream> stream);
+
+  // A thread safe call that will draw an image of size |size| for the frame at
+  // normalized time instant |t| onto the |canvas|.
+  void Draw(SkCanvas* canvas, float t, const Size& size);
+
+  float duration() const { return animation_->duration(); }
+  SkSize size() const { return animation_->size(); }
+
+ private:
+  friend class base::RefCountedThreadSafe<SkottieWrapper>;
+  ~SkottieWrapper();
+
+  base::Lock lock_;
+  sk_sp<skottie::Animation> animation_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkottieWrapper);
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_SKOTTIE_WRAPPER_H_
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 77eae44..51b1afd 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -128,6 +128,7 @@
     "gpu_switching_manager.h",
     "gpu_timing.cc",
     "gpu_timing.h",
+    "progress_reporter.h",
     "scoped_binders.cc",
     "scoped_binders.h",
     "scoped_make_current.cc",
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 00cdf20..2c96347a 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -16,6 +16,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
 #include "base/trace_event/trace_event.h"
@@ -492,10 +493,8 @@
 void AddInitDisplay(std::vector<DisplayType>* init_displays,
                     DisplayType display_type) {
   // Make sure to not add the same display type twice.
-  if (std::find(init_displays->begin(), init_displays->end(), display_type) ==
-      init_displays->end()) {
+  if (!base::ContainsValue(*init_displays, display_type))
     init_displays->push_back(display_type);
-  }
 }
 
 const char* GetDebugMessageTypeString(EGLint source) {
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc
index b8634d2..598b98cdf 100644
--- a/ui/gl/init/create_gr_gl_interface.cc
+++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -5,6 +5,7 @@
 #include "ui/gl/init/create_gr_gl_interface.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_version_info.h"
+#include "ui/gl/progress_reporter.h"
 
 namespace gl {
 namespace init {
@@ -17,6 +18,28 @@
   return [func, api](Args... args) { return (api->*func)(args...); };
 }
 
+class ScopedProgressReporter {
+ public:
+  ScopedProgressReporter(gl::ProgressReporter* progress_reporter)
+      : progress_reporter_(progress_reporter) {}
+  ~ScopedProgressReporter() { progress_reporter_->ReportProgress(); }
+
+ private:
+  gl::ProgressReporter* progress_reporter_;
+};
+
+template <typename R, typename... Args>
+GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow(
+    R(GL_BINDING_CALL* func)(Args...),
+    gl::ProgressReporter* progress_reporter) {
+  if (!progress_reporter)
+    return func;
+  return [func, progress_reporter](Args... args) {
+    ScopedProgressReporter scoped_reporter(progress_reporter);
+    return func(args...);
+  };
+};
+
 const GLubyte* GetStringHook(const char* version_string, GLenum name) {
   switch (name) {
     case GL_VERSION:
@@ -47,7 +70,8 @@
 
 sk_sp<GrGLInterface> CreateGrGLInterface(
     const gl::GLVersionInfo& version_info,
-    bool use_version_es2) {
+    bool use_version_es2,
+    gl::ProgressReporter* progress_reporter) {
   // Can't fake ES with desktop GL.
   use_version_es2 &= version_info.is_es;
 
@@ -119,17 +143,23 @@
   // functions->fClearTexSubImage = nullptr;
 
   functions->fColorMask = gl->glColorMaskFn;
-  functions->fCompileShader = gl->glCompileShaderFn;
-  functions->fCompressedTexImage2D = gl->glCompressedTexImage2DFn;
-  functions->fCompressedTexSubImage2D = gl->glCompressedTexSubImage2DFn;
-  functions->fCopyTexSubImage2D = gl->glCopyTexSubImage2DFn;
+  functions->fCompileShader =
+      bind_slow(gl->glCompileShaderFn, progress_reporter);
+  functions->fCompressedTexImage2D =
+      bind_slow(gl->glCompressedTexImage2DFn, progress_reporter);
+  functions->fCompressedTexSubImage2D =
+      bind_slow(gl->glCompressedTexSubImage2DFn, progress_reporter);
+  functions->fCopyTexSubImage2D =
+      bind_slow(gl->glCopyTexSubImage2DFn, progress_reporter);
   functions->fCreateProgram = gl->glCreateProgramFn;
   functions->fCreateShader = gl->glCreateShaderFn;
   functions->fCullFace = gl->glCullFaceFn;
-  functions->fDeleteBuffers = gl->glDeleteBuffersARBFn;
-  functions->fDeleteProgram = gl->glDeleteProgramFn;
+  functions->fDeleteBuffers =
+      bind_slow(gl->glDeleteBuffersARBFn, progress_reporter);
+  functions->fDeleteProgram =
+      bind_slow(gl->glDeleteProgramFn, progress_reporter);
   functions->fDeleteQueries = gl->glDeleteQueriesFn;
-  functions->fDeleteShader = gl->glDeleteShaderFn;
+  functions->fDeleteShader = bind_slow(gl->glDeleteShaderFn, progress_reporter);
   functions->fDeleteTextures = gl->glDeleteTexturesFn;
   functions->fDepthMask = gl->glDepthMaskFn;
   functions->fDisable = gl->glDisableFn;
@@ -151,8 +181,8 @@
   functions->fEnable = gl->glEnableFn;
   functions->fEnableVertexAttribArray = gl->glEnableVertexAttribArrayFn;
   functions->fEndQuery = gl->glEndQueryFn;
-  functions->fFinish = gl->glFinishFn;
-  functions->fFlush = gl->glFlushFn;
+  functions->fFinish = bind_slow(gl->glFinishFn, progress_reporter);
+  functions->fFlush = bind_slow(gl->glFlushFn, progress_reporter);
   functions->fFrontFace = gl->glFrontFaceFn;
   functions->fGenBuffers = gl->glGenBuffersARBFn;
   functions->fGetBufferParameteriv = gl->glGetBufferParameterivFn;
@@ -179,7 +209,7 @@
   functions->fGetUniformLocation = gl->glGetUniformLocationFn;
   functions->fIsTexture = gl->glIsTextureFn;
   functions->fLineWidth = gl->glLineWidthFn;
-  functions->fLinkProgram = gl->glLinkProgramFn;
+  functions->fLinkProgram = bind_slow(gl->glLinkProgramFn, progress_reporter);
   functions->fMapBuffer = gl->glMapBufferFn;
 
   // GL 4.3 or GL_ARB_multi_draw_indirect or ES+GL_EXT_multi_draw_indirect
@@ -206,7 +236,7 @@
   functions->fStencilOpSeparate = gl->glStencilOpSeparateFn;
   functions->fTexBuffer = gl->glTexBufferFn;
   functions->fTexBufferRange = gl->glTexBufferRangeFn;
-  functions->fTexImage2D = gl->glTexImage2DFn;
+  functions->fTexImage2D = bind_slow(gl->glTexImage2DFn, progress_reporter);
   functions->fTexParameteri = gl->glTexParameteriFn;
   functions->fTexParameteriv = gl->glTexParameterivFn;
   functions->fTexStorage2D = gl->glTexStorage2DEXTFn;
@@ -266,7 +296,8 @@
   functions->fBindFramebuffer = gl->glBindFramebufferEXTFn;
   functions->fFramebufferTexture2D = gl->glFramebufferTexture2DEXTFn;
   functions->fCheckFramebufferStatus = gl->glCheckFramebufferStatusEXTFn;
-  functions->fDeleteFramebuffers = gl->glDeleteFramebuffersEXTFn;
+  functions->fDeleteFramebuffers =
+      bind_slow(gl->glDeleteFramebuffersEXTFn, progress_reporter);
   functions->fRenderbufferStorage = gl->glRenderbufferStorageEXTFn;
   functions->fGenRenderbuffers = gl->glGenRenderbuffersEXTFn;
   functions->fDeleteRenderbuffers = gl->glDeleteRenderbuffersEXTFn;
diff --git a/ui/gl/init/create_gr_gl_interface.h b/ui/gl/init/create_gr_gl_interface.h
index 4b23ee7..9243fbe 100644
--- a/ui/gl/init/create_gr_gl_interface.h
+++ b/ui/gl/init/create_gr_gl_interface.h
@@ -11,6 +11,7 @@
 
 namespace gl {
 struct GLVersionInfo;
+class ProgressReporter;
 }
 
 namespace gl {
@@ -20,7 +21,8 @@
 // GL bindings.
 GL_INIT_EXPORT sk_sp<GrGLInterface> CreateGrGLInterface(
     const gl::GLVersionInfo& version_info,
-    bool use_version_es2);
+    bool use_version_es2,
+    gl::ProgressReporter* progress_reporter = nullptr);
 
 }  // namespace init
 }  // namespace gl
diff --git a/ui/gl/progress_reporter.h b/ui/gl/progress_reporter.h
new file mode 100644
index 0000000..5ebcfe0
--- /dev/null
+++ b/ui/gl/progress_reporter.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_PROGRESS_REPORTER_H_
+#define UI_GL_PROGRESS_REPORTER_H_
+
+namespace gl {
+
+// ProgressReporter is used by ContextGroup and GrGLInterface to report when it
+// is making forward progress in execution, delaying activation of the watchdog
+// timeout.
+class ProgressReporter {
+ public:
+  virtual ~ProgressReporter() = default;
+
+  virtual void ReportProgress() = 0;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_PROGRESS_REPORTER_H_
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc
index 659cedf..c4a0840 100644
--- a/ui/gl/test/gl_image_test_support.cc
+++ b/ui/gl/test/gl_image_test_support.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/stl_util.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/half_float.h"
 #include "ui/gl/init/gl_factory.h"
@@ -33,8 +34,7 @@
   DCHECK(!allowed_impls.empty());
 
   GLImplementation impl = prefered_impl ? *prefered_impl : allowed_impls[0];
-  DCHECK(std::find(allowed_impls.begin(), allowed_impls.end(), impl) !=
-         allowed_impls.end());
+  DCHECK(base::ContainsValue(allowed_impls, impl));
 
   GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true);
 #if defined(USE_OZONE)
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 7d3c40afd..9740ad9 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -43,14 +43,16 @@
     "wayland_input_method_context_factory.h",
     "wayland_keyboard.cc",
     "wayland_keyboard.h",
-    "wayland_native_display_delegate.cc",
-    "wayland_native_display_delegate.h",
     "wayland_object.cc",
     "wayland_object.h",
     "wayland_output.cc",
     "wayland_output.h",
+    "wayland_output_manager.cc",
+    "wayland_output_manager.h",
     "wayland_pointer.cc",
     "wayland_pointer.h",
+    "wayland_screen.cc",
+    "wayland_screen.h",
     "wayland_surface_factory.cc",
     "wayland_surface_factory.h",
     "wayland_touch.cc",
@@ -114,6 +116,7 @@
     "//ui/ozone/common/linux",
     "//ui/ozone/public/interfaces/wayland:wayland_interfaces",
     "//ui/platform_window",
+    "//ui/platform_window/platform_window_handler",
   ]
 
   defines = [ "OZONE_IMPLEMENTATION" ]
@@ -156,6 +159,7 @@
     "wayland_input_method_context_unittest.cc",
     "wayland_keyboard_unittest.cc",
     "wayland_pointer_unittest.cc",
+    "wayland_screen_unittest.cc",
     "wayland_surface_factory_unittest.cc",
     "wayland_test.cc",
     "wayland_test.h",
diff --git a/ui/ozone/platform/wayland/DEPS b/ui/ozone/platform/wayland/DEPS
index 06292b5..56a383a0 100644
--- a/ui/ozone/platform/wayland/DEPS
+++ b/ui/ozone/platform/wayland/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ui/base/hit_test.h", # UI hit test doesn't bring in all of ui/base.
   "+ui/base/ui_features.h",  # UI features doesn't bring in all of ui/base.
   "+ui/base/ui_base_features.h",
   "+ui/base/ime/composition_text.h",
diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc
index d9edf99f..62e7f8c 100644
--- a/ui/ozone/platform/wayland/fake_server.cc
+++ b/ui/ozone/platform/wayland/fake_server.cc
@@ -562,6 +562,21 @@
   GetUserDataAs<MockXdgSurface>(resource)->SetAppId(app_id);
 }
 
+void Move(wl_client* client,
+          wl_resource* resource,
+          wl_resource* seat,
+          uint32_t serial) {
+  GetUserDataAs<MockXdgSurface>(resource)->Move(serial);
+}
+
+void Resize(wl_client* client,
+            wl_resource* resource,
+            wl_resource* seat,
+            uint32_t serial,
+            uint32_t edges) {
+  GetUserDataAs<MockXdgSurface>(resource)->Resize(serial, edges);
+}
+
 void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) {
   GetUserDataAs<MockXdgSurface>(resource)->AckConfigure(serial);
 }
@@ -604,8 +619,8 @@
     &SetTitle,           // set_title
     &SetAppId,           // set_app_id
     nullptr,             // show_window_menu
-    nullptr,             // move
-    nullptr,             // resize
+    &Move,               // move
+    &Resize,             // resize
     &AckConfigure,       // ack_configure
     &SetWindowGeometry,  // set_window_geometry
     &SetMaximized,       // set_maximized
@@ -707,8 +722,8 @@
     &SetTitle,         // set_title
     &SetAppId,         // set_app_id
     nullptr,           // show_window_menu
-    nullptr,           // move
-    nullptr,           // resize
+    &Move,             // move
+    &Resize,           // resize
     nullptr,           // set_max_size
     nullptr,           // set_min_size
     &SetMaximized,     // set_maximized
@@ -1068,7 +1083,8 @@
 MockDataDeviceManager::~MockDataDeviceManager() {}
 
 MockOutput::MockOutput()
-    : Global(&wl_output_interface, nullptr, kOutputVersion) {}
+    : Global(&wl_output_interface, nullptr, kOutputVersion),
+      rect_(gfx::Rect(0, 0, 800, 600)) {}
 
 MockOutput::~MockOutput() {}
 
diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h
index 27b8f07..bd7decc8 100644
--- a/ui/ozone/platform/wayland/fake_server.h
+++ b/ui/ozone/platform/wayland/fake_server.h
@@ -59,6 +59,8 @@
   MOCK_METHOD1(SetParent, void(wl_resource* parent));
   MOCK_METHOD1(SetTitle, void(const char* title));
   MOCK_METHOD1(SetAppId, void(const char* app_id));
+  MOCK_METHOD1(Move, void(uint32_t serial));
+  MOCK_METHOD2(Resize, void(uint32_t serial, uint32_t edges));
   MOCK_METHOD1(AckConfigure, void(uint32_t serial));
   MOCK_METHOD4(SetWindowGeometry,
                void(int32_t x, int32_t y, int32_t width, int32_t height));
@@ -459,6 +461,8 @@
     return &zwp_text_input_manager_v1_;
   }
 
+  wl_display* display() const { return display_.get(); }
+
  private:
   void DoPause();
 
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index f845a6b..8074471 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -15,12 +15,13 @@
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 #include "ui/ozone/platform/wayland/wayland_connection_connector.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context_factory.h"
-#include "ui/ozone/platform/wayland/wayland_native_display_delegate.h"
+#include "ui/ozone/platform/wayland/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/wayland_surface_factory.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 #include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_screen.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
 #if BUILDFLAG(USE_XKBCOMMON)
@@ -99,7 +100,14 @@
 
   std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate()
       override {
-    return std::make_unique<WaylandNativeDisplayDelegate>(connection_.get());
+    return std::make_unique<display::FakeDisplayDelegate>();
+  }
+
+  std::unique_ptr<PlatformScreen> CreateScreen() override {
+    // The WaylandConnection and the WaylandOutputManager must be created before
+    // PlatformScreen.
+    DCHECK(connection_ && connection_->wayland_output_manager());
+    return connection_->wayland_output_manager()->CreateWaylandScreen();
   }
 
   void InitializeUI(const InitParams& args) override {
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc
index 0ddacd8..d4ce8fb 100644
--- a/ui/ozone/platform/wayland/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -18,6 +18,7 @@
 #include "ui/ozone/platform/wayland/wayland_buffer_manager.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
+#include "ui/ozone/platform/wayland/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 
 static_assert(XDG_SHELL_VERSION_CURRENT == 5, "Unsupported xdg-shell version");
@@ -65,9 +66,10 @@
   }
 
   wl_registry_add_listener(registry_.get(), &registry_listener, this);
-
-  while (!PrimaryOutput() || !PrimaryOutput()->is_ready())
+  while (!wayland_output_manager_ ||
+         !wayland_output_manager_->IsPrimaryOutputReady()) {
     wl_display_roundtrip(display_.get());
+  }
 
   if (!compositor_) {
     LOG(ERROR) << "No wl_compositor object";
@@ -150,12 +152,6 @@
   window_map_.erase(widget);
 }
 
-WaylandOutput* WaylandConnection::PrimaryOutput() const {
-  if (!output_list_.size())
-    return nullptr;
-  return output_list_.front().get();
-}
-
 void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps,
                                         const gfx::Point& location) {
   if (!pointer_ || !pointer_->cursor())
@@ -287,6 +283,11 @@
   data_device_->RequestDragData(mime_type, std::move(callback));
 }
 
+void WaylandConnection::ResetPointerFlags() {
+  if (pointer_)
+    pointer_->ResetFlags();
+}
+
 void WaylandConnection::GetAvailableMimeTypes(
     ClipboardDelegate::GetMimeTypesClosure callback) {
   std::move(callback).Run(data_device_->GetAvailableMimeTypes());
@@ -340,11 +341,6 @@
   buffer_manager_->ClearState();
 }
 
-const std::vector<std::unique_ptr<WaylandOutput>>&
-WaylandConnection::GetOutputList() const {
-  return output_list_;
-}
-
 // static
 void WaylandConnection::Global(void* data,
                                wl_registry* registry,
@@ -428,11 +424,12 @@
       return;
     }
 
-    if (!connection->output_list_.empty())
-      NOTIMPLEMENTED() << "Multiple screens support is not implemented";
-
-    connection->output_list_.push_back(base::WrapUnique(new WaylandOutput(
-        connection->get_next_display_id(), output.release())));
+    if (!connection->wayland_output_manager_) {
+      connection->wayland_output_manager_ =
+          std::make_unique<WaylandOutputManager>();
+    }
+    connection->wayland_output_manager_->AddWaylandOutput(name,
+                                                          output.release());
   } else if (!connection->data_device_manager_ &&
              strcmp(interface, "wl_data_device_manager") == 0) {
     wl::Object<wl_data_device_manager> data_device_manager =
@@ -473,7 +470,15 @@
 void WaylandConnection::GlobalRemove(void* data,
                                      wl_registry* registry,
                                      uint32_t name) {
-  NOTIMPLEMENTED();
+  WaylandConnection* connection = static_cast<WaylandConnection*>(data);
+  // The Wayland protocol distinguishes global objects by unique numeric names,
+  // which the WaylandOutputManager uses as unique output ids. But, it is only
+  // possible to figure out, what global object is going to be removed on the
+  // WaylandConnection::GlobalRemove call. Thus, whatever unique |name| comes,
+  // it's forwarded to the WaylandOutputManager, which checks if such a global
+  // output object exists and removes it.
+  if (connection->wayland_output_manager_)
+    connection->wayland_output_manager_->RemoveWaylandOutput(name);
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index 15eecf7..d2487cd 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -26,8 +26,9 @@
 
 namespace ui {
 
-class WaylandWindow;
 class WaylandBufferManager;
+class WaylandOutputManager;
+class WaylandWindow;
 
 class WaylandConnection : public PlatformEventSource,
                           public ClipboardDelegate,
@@ -71,8 +72,8 @@
   wl_compositor* compositor() { return compositor_.get(); }
   wl_subcompositor* subcompositor() { return subcompositor_.get(); }
   wl_shm* shm() { return shm_.get(); }
-  xdg_shell* shell() { return shell_.get(); }
-  zxdg_shell_v6* shell_v6() { return shell_v6_.get(); }
+  xdg_shell* shell() const { return shell_.get(); }
+  zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); }
   wl_seat* seat() { return seat_.get(); }
   wl_data_device* data_device() { return data_device_->data_device(); }
   wp_presentation* presentation() const { return presentation_.get(); }
@@ -86,10 +87,6 @@
   void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window);
   void RemoveWindow(gfx::AcceleratedWidget widget);
 
-  int64_t get_next_display_id() { return next_display_id_++; }
-  const std::vector<std::unique_ptr<WaylandOutput>>& GetOutputList() const;
-  WaylandOutput* PrimaryOutput() const;
-
   void set_serial(uint32_t serial) { serial_ = serial; }
   uint32_t serial() { return serial_; }
 
@@ -103,6 +100,10 @@
 
   WaylandDataSource* drag_data_source() { return drag_data_source_.get(); }
 
+  WaylandOutputManager* wayland_output_manager() const {
+    return wayland_output_manager_.get();
+  }
+
   // Clipboard implementation.
   ClipboardDelegate* GetClipboardDelegate();
   void DataSourceCancelled();
@@ -146,6 +147,13 @@
   void RequestDragData(const std::string& mime_type,
                        base::OnceCallback<void(const std::string&)> callback);
 
+  // Resets flags and keyboard modifiers.
+  //
+  // This method is specially handy for cases when the WaylandPointer state is
+  // modified by a POINTER_DOWN event, but the respective POINTER_UP event is
+  // not delivered.
+  void ResetPointerFlags();
+
  private:
   // WaylandInputMethodContextFactory needs access to DispatchUiEvent
   friend class WaylandInputMethodContextFactory;
@@ -198,8 +206,9 @@
   std::unique_ptr<WaylandDataDevice> data_device_;
   std::unique_ptr<WaylandDataSource> data_source_;
   std::unique_ptr<WaylandDataSource> drag_data_source_;
-  std::unique_ptr<WaylandPointer> pointer_;
   std::unique_ptr<WaylandKeyboard> keyboard_;
+  std::unique_ptr<WaylandOutputManager> wayland_output_manager_;
+  std::unique_ptr<WaylandPointer> pointer_;
   std::unique_ptr<WaylandTouch> touch_;
 
   // Objects that are using when GPU runs in own process.
@@ -211,9 +220,6 @@
 
   uint32_t serial_ = 0;
 
-  int64_t next_display_id_ = 0;
-  std::vector<std::unique_ptr<WaylandOutput>> output_list_;
-
   // Holds a temporary instance of the client's clipboard content
   // so that we can asynchronously write to it.
   ClipboardDelegate::DataMap* data_map_ = nullptr;
diff --git a/ui/ozone/platform/wayland/wayland_connection_unittest.cc b/ui/ozone/platform/wayland/wayland_connection_unittest.cc
index dea1530..9c6cc51 100644
--- a/ui/ozone/platform/wayland/wayland_connection_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_connection_unittest.cc
@@ -8,42 +8,14 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
-#include "ui/ozone/platform/wayland/wayland_output.h"
 
 namespace ui {
 
 namespace {
-
-const uint32_t kXdgVersion5 = 5;
-const uint32_t kNumOfDisplays = 1;
-const uint32_t kWidth = 800;
-const uint32_t kHeight = 600;
-
-void CheckDisplaySize(const std::vector<display::DisplaySnapshot*>& displays) {
-  ASSERT_TRUE(displays.size() == kNumOfDisplays);
-
-  // TODO(msisov): add multiple displays support.
-  display::DisplaySnapshot* display_snapshot = displays.front();
-  ASSERT_TRUE(display_snapshot->current_mode()->size() ==
-              gfx::Size(kWidth, kHeight));
+constexpr uint32_t kXdgVersion5 = 5;
 }
-}
-
-class OutputObserver : public WaylandOutput::Observer {
- public:
-  explicit OutputObserver(const base::Closure& closure) : closure_(closure) {}
-
-  void OnOutputReadyForUse() override {
-    if (!closure_.is_null())
-      closure_.Run();
-  }
-
- private:
-  const base::Closure closure_;
-};
 
 TEST(WaylandConnectionTest, UseUnstableVersion) {
   base::MessageLoopForUI message_loop;
@@ -78,26 +50,4 @@
   server.Pause();
 }
 
-TEST(WaylandConnectionTest, Output) {
-  base::MessageLoopForUI message_loop;
-  wl::FakeServer server;
-  ASSERT_TRUE(server.Start(kXdgVersion5));
-  server.output()->SetRect(gfx::Rect(0, 0, kWidth, kHeight));
-  WaylandConnection connection;
-  ASSERT_TRUE(connection.Initialize());
-  connection.StartProcessingEvents();
-
-  base::RunLoop run_loop;
-  OutputObserver observer(run_loop.QuitClosure());
-  connection.PrimaryOutput()->SetObserver(&observer);
-  run_loop.Run();
-
-  connection.PrimaryOutput()->GetDisplaysSnapshot(
-      base::BindOnce(&CheckDisplaySize));
-
-  server.Resume();
-  base::RunLoop().RunUntilIdle();
-  server.Pause();
-}
-
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_native_display_delegate.cc b/ui/ozone/platform/wayland/wayland_native_display_delegate.cc
deleted file mode 100644
index 029da54..0000000
--- a/ui/ozone/platform/wayland/wayland_native_display_delegate.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/wayland/wayland_native_display_delegate.h"
-
-#include "ui/display/types/display_snapshot.h"
-#include "ui/display/types/native_display_observer.h"
-#include "ui/ozone/platform/wayland/wayland_connection.h"
-
-namespace ui {
-
-WaylandNativeDisplayDelegate::WaylandNativeDisplayDelegate(
-    WaylandConnection* connection)
-    : connection_(connection) {}
-
-WaylandNativeDisplayDelegate::~WaylandNativeDisplayDelegate() {
-  connection_->PrimaryOutput()->SetObserver(nullptr);
-}
-
-void WaylandNativeDisplayDelegate::Initialize() {
-  // TODO(msisov): Add support for secondary output.
-  WaylandOutput* primary_output = connection_->PrimaryOutput();
-  if (!primary_output)
-    NOTREACHED() << "Asynchronous display data fetching is not available";
-
-  primary_output->SetObserver(this);
-}
-
-void WaylandNativeDisplayDelegate::TakeDisplayControl(
-    display::DisplayControlCallback callback) {
-  NOTREACHED();
-}
-
-void WaylandNativeDisplayDelegate::RelinquishDisplayControl(
-    display::DisplayControlCallback callback) {
-  NOTREACHED();
-}
-
-void WaylandNativeDisplayDelegate::GetDisplays(
-    display::GetDisplaysCallback callback) {
-  if (displays_ready_)
-    connection_->PrimaryOutput()->GetDisplaysSnapshot(std::move(callback));
-}
-
-void WaylandNativeDisplayDelegate::Configure(
-    const display::DisplaySnapshot& output,
-    const display::DisplayMode* mode,
-    const gfx::Point& origin,
-    display::ConfigureCallback callback) {
-  NOTREACHED();
-}
-
-void WaylandNativeDisplayDelegate::GetHDCPState(
-    const display::DisplaySnapshot& output,
-    display::GetHDCPStateCallback callback) {
-  NOTREACHED();
-}
-
-void WaylandNativeDisplayDelegate::SetHDCPState(
-    const display::DisplaySnapshot& output,
-    display::HDCPState state,
-    display::SetHDCPStateCallback callback) {
-  NOTREACHED();
-}
-
-bool WaylandNativeDisplayDelegate::SetColorMatrix(
-    int64_t display_id,
-    const std::vector<float>& color_matrix) {
-  NOTREACHED();
-  return false;
-}
-
-bool WaylandNativeDisplayDelegate::SetGammaCorrection(
-    int64_t display_id,
-    const std::vector<display::GammaRampRGBEntry>& degamma_lut,
-    const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
-  NOTREACHED();
-  return false;
-}
-
-void WaylandNativeDisplayDelegate::AddObserver(
-    display::NativeDisplayObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void WaylandNativeDisplayDelegate::RemoveObserver(
-    display::NativeDisplayObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-display::FakeDisplayController*
-WaylandNativeDisplayDelegate::GetFakeDisplayController() {
-  return nullptr;
-}
-
-void WaylandNativeDisplayDelegate::OnOutputReadyForUse() {
-  if (!displays_ready_)
-    displays_ready_ = true;
-
-  for (display::NativeDisplayObserver& observer : observers_)
-    observer.OnConfigurationChanged();
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_native_display_delegate.h b/ui/ozone/platform/wayland/wayland_native_display_delegate.h
deleted file mode 100644
index e0559cb3..0000000
--- a/ui/ozone/platform/wayland/wayland_native_display_delegate.h
+++ /dev/null
@@ -1,65 +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.
-
-#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_
-#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/observer_list.h"
-#include "ui/display/types/native_display_delegate.h"
-#include "ui/ozone/platform/wayland/wayland_output.h"
-
-namespace ui {
-
-class WaylandConnection;
-
-class WaylandNativeDisplayDelegate : public display::NativeDisplayDelegate,
-                                     public WaylandOutput::Observer {
- public:
-  explicit WaylandNativeDisplayDelegate(WaylandConnection* connection);
-  ~WaylandNativeDisplayDelegate() override;
-
-  // display::NativeDisplayDelegate overrides:
-  void Initialize() override;
-  void TakeDisplayControl(display::DisplayControlCallback callback) override;
-  void RelinquishDisplayControl(
-      display::DisplayControlCallback callback) override;
-  void GetDisplays(display::GetDisplaysCallback callback) override;
-  void Configure(const display::DisplaySnapshot& output,
-                 const display::DisplayMode* mode,
-                 const gfx::Point& origin,
-                 display::ConfigureCallback callback) override;
-  void GetHDCPState(const display::DisplaySnapshot& output,
-                    display::GetHDCPStateCallback callback) override;
-  void SetHDCPState(const display::DisplaySnapshot& output,
-                    display::HDCPState state,
-                    display::SetHDCPStateCallback callback) override;
-  bool SetColorMatrix(int64_t display_id,
-                      const std::vector<float>& color_matrix) override;
-  bool SetGammaCorrection(
-      int64_t display_id,
-      const std::vector<display::GammaRampRGBEntry>& degamma_lut,
-      const std::vector<display::GammaRampRGBEntry>& gamma_lut) override;
-  void AddObserver(display::NativeDisplayObserver* observer) override;
-  void RemoveObserver(display::NativeDisplayObserver* observer) override;
-  display::FakeDisplayController* GetFakeDisplayController() override;
-
-  // WaylandOutput::Observer overrides:
-  void OnOutputReadyForUse() override;
-
- private:
-  WaylandConnection* connection_;  // Not owned.
-
-  base::ObserverList<display::NativeDisplayObserver>::Unchecked observers_;
-
-  bool displays_ready_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(WaylandNativeDisplayDelegate);
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_
diff --git a/ui/ozone/platform/wayland/wayland_output.cc b/ui/ozone/platform/wayland/wayland_output.cc
index d9f8c44..fd14f45 100644
--- a/ui/ozone/platform/wayland/wayland_output.cc
+++ b/ui/ozone/platform/wayland/wayland_output.cc
@@ -11,26 +11,32 @@
 
 namespace ui {
 
-WaylandOutput::WaylandOutput(const int64_t display_id, wl_output* output)
-    : display_id_(display_id), output_(output), observer_(nullptr) {
+namespace {
+constexpr float kDefaultScaleFactor = 1.0f;
+}
+
+WaylandOutput::WaylandOutput(const uint32_t output_id, wl_output* output)
+    : output_id_(output_id),
+      output_(output),
+      device_scale_factor_(kDefaultScaleFactor),
+      rect_in_physical_pixels_(gfx::Rect()) {}
+
+WaylandOutput::~WaylandOutput() = default;
+
+void WaylandOutput::Initialize(Delegate* delegate) {
+  DCHECK(!delegate_);
+  delegate_ = delegate;
   static const wl_output_listener output_listener = {
       &WaylandOutput::OutputHandleGeometry, &WaylandOutput::OutputHandleMode,
+      &WaylandOutput::OutputHandleDone, &WaylandOutput::OutputHandleScale,
   };
-  wl_output_add_listener(output, &output_listener, this);
+  wl_output_add_listener(output_.get(), &output_listener, this);
 }
 
-WaylandOutput::~WaylandOutput() {}
-
-void WaylandOutput::SetObserver(Observer* observer) {
-  observer_ = observer;
-  if (current_mode_)
-    observer_->OnOutputReadyForUse();
-}
-
-void WaylandOutput::GetDisplaysSnapshot(display::GetDisplaysCallback callback) {
-  std::vector<display::DisplaySnapshot*> snapshot;
-  snapshot.push_back(current_snapshot_.get());
-  std::move(callback).Run(snapshot);
+void WaylandOutput::TriggerDelegateNotification() const {
+  DCHECK(!rect_in_physical_pixels_.IsEmpty());
+  delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_,
+                                   device_scale_factor_);
 }
 
 // static
@@ -45,13 +51,8 @@
                                          const char* model,
                                          int32_t output_transform) {
   WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data);
-  wayland_output->current_snapshot_.reset(new display::DisplaySnapshot(
-      wayland_output->display_id_, gfx::Point(x, y),
-      gfx::Size(physical_width, physical_height),
-      display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false,
-      false, false, false, gfx::ColorSpace(), model, base::FilePath(),
-      display::DisplaySnapshot::DisplayModeList(), std::vector<uint8_t>(),
-      nullptr, nullptr, 0, 0, gfx::Size()));
+  if (wayland_output)
+    wayland_output->rect_in_physical_pixels_.set_origin(gfx::Point(x, y));
 }
 
 // static
@@ -61,17 +62,27 @@
                                      int32_t width,
                                      int32_t height,
                                      int32_t refresh) {
-  WaylandOutput* output = static_cast<WaylandOutput*>(data);
+  WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data);
+  if (wayland_output && (flags & WL_OUTPUT_MODE_CURRENT)) {
+    wayland_output->rect_in_physical_pixels_.set_width(width);
+    wayland_output->rect_in_physical_pixels_.set_height(height);
+    wayland_output->TriggerDelegateNotification();
+  }
+}
 
-  if (flags & WL_OUTPUT_MODE_CURRENT) {
-    std::unique_ptr<display::DisplayMode> previous_mode =
-        std::move(output->current_mode_);
-    output->current_mode_.reset(
-        new display::DisplayMode(gfx::Size(width, height), false, refresh));
-    output->current_snapshot_->set_current_mode(output->current_mode_.get());
+// static
+void WaylandOutput::OutputHandleDone(void* data, struct wl_output* wl_output) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
 
-    if (output->observer())
-      output->observer()->OnOutputReadyForUse();
+// static
+void WaylandOutput::OutputHandleScale(void* data,
+                                      struct wl_output* wl_output,
+                                      int32_t factor) {
+  WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data);
+  if (wayland_output) {
+    wayland_output->device_scale_factor_ = factor;
+    wayland_output->TriggerDelegateNotification();
   }
 }
 
diff --git a/ui/ozone/platform/wayland/wayland_output.h b/ui/ozone/platform/wayland/wayland_output.h
index 94088ca..ddf7946b 100644
--- a/ui/ozone/platform/wayland/wayland_output.h
+++ b/ui/ozone/platform/wayland/wayland_output.h
@@ -18,21 +18,27 @@
 // that are available to the application.
 class WaylandOutput {
  public:
-  class Observer {
+  class Delegate {
    public:
-    // Will be called when wl_output is available.
-    virtual void OnOutputReadyForUse() = 0;
+    virtual ~Delegate() {}
+
+    virtual void OnOutputHandleMetrics(uint32_t output_id,
+                                       const gfx::Rect& new_bounds,
+                                       int32_t scale_factor) = 0;
   };
 
-  WaylandOutput(const int64_t display_id, wl_output* output);
+  WaylandOutput(const uint32_t output_id, wl_output* output);
   ~WaylandOutput();
 
-  void SetObserver(Observer* observer);
-  Observer* observer() { return observer_; }
+  void Initialize(Delegate* delegate);
 
-  bool is_ready() const { return !!current_mode_; }
+  void TriggerDelegateNotification() const;
 
-  void GetDisplaysSnapshot(display::GetDisplaysCallback callback);
+  uint32_t output_id() const { return output_id_; }
+
+  // Tells if the output has already received physical screen dimensions in the
+  // global compositor space.
+  bool is_ready() const { return !rect_in_physical_pixels_.IsEmpty(); }
 
  private:
   // Callback functions used for setting geometric properties of the output
@@ -54,14 +60,17 @@
                                int32_t width,
                                int32_t height,
                                int32_t refresh);
+  static void OutputHandleDone(void* data, struct wl_output* wl_output);
+  static void OutputHandleScale(void* data,
+                                struct wl_output* wl_output,
+                                int32_t factor);
 
-  const int64_t display_id_ = 0;
+  const uint32_t output_id_ = 0;
   wl::Object<wl_output> output_;
+  float device_scale_factor_;
+  gfx::Rect rect_in_physical_pixels_;
 
-  Observer* observer_;
-
-  std::unique_ptr<display::DisplaySnapshot> current_snapshot_;
-  std::unique_ptr<display::DisplayMode> current_mode_;
+  Delegate* delegate_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(WaylandOutput);
 };
diff --git a/ui/ozone/platform/wayland/wayland_output_manager.cc b/ui/ozone/platform/wayland/wayland_output_manager.cc
new file mode 100644
index 0000000..c9db25c
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_output_manager.cc
@@ -0,0 +1,117 @@
+// 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 "ui/ozone/platform/wayland/wayland_output_manager.h"
+
+#include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_output.h"
+
+namespace ui {
+
+WaylandOutputManager::WaylandOutputManager() = default;
+
+WaylandOutputManager::~WaylandOutputManager() = default;
+
+bool WaylandOutputManager::IsPrimaryOutputReady() const {
+  if (output_list_.empty())
+    return false;
+
+  // The very first output in the list is always treated as a primary output.
+  const auto& primary_output = output_list_.front();
+  return primary_output->is_ready();
+}
+
+void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id,
+                                            wl_output* output) {
+  // Make sure an output with |output_id| has not been added yet. It's very
+  // unlikely to happen, unless a compositor has a bug in the numeric names
+  // representation of global objects.
+  auto output_it = std::find_if(output_list_.begin(), output_list_.end(),
+                                [output_id](const auto& output) {
+                                  return output->output_id() == output_id;
+                                });
+  DCHECK(output_it == output_list_.end());
+  auto wayland_output = std::make_unique<WaylandOutput>(output_id, output);
+  WaylandOutput* wayland_output_ptr = wayland_output.get();
+  output_list_.push_back(std::move(wayland_output));
+
+  OnWaylandOutputAdded(output_id);
+
+  // If WaylandScreen has already been created, the output can be initialized,
+  // which results in setting up a wl_listener and getting the geometry and the
+  // scaling factor from the Wayland Compositor.
+  wayland_output_ptr->Initialize(this);
+}
+
+void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) {
+  auto output_it = std::find_if(output_list_.begin(), output_list_.end(),
+                                [output_id](const auto& output) {
+                                  return output->output_id() == output_id;
+                                });
+
+  // Check the comment in the WaylandConnetion::GlobalRemove.
+  if (output_it == output_list_.end())
+    return;
+
+  bool was_primary_output = IsPrimaryOutput(output_id);
+  output_list_.erase(output_it);
+
+  // If it was a primary output removed, make sure the second output, which
+  // became a primary one, announces that to observers.
+  if (was_primary_output && !output_list_.empty())
+    output_list_.front()->TriggerDelegateNotification();
+
+  OnWaylandOutputRemoved(output_id);
+}
+
+std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen() {
+  auto wayland_screen = std::make_unique<WaylandScreen>();
+  wayland_screen_ = wayland_screen->GetWeakPtr();
+
+  // As long as |wl_output| sends geometry and other events asynchronously (that
+  // is, the initial configuration is sent once the interface is bound), we'll
+  // have to tell each output to manually inform the delegate about available
+  // geometry, scale factor and etc, which will result in feeding the
+  // WaylandScreen with the data through OnOutputHandleGeometry and
+  // OutOutputHandleScale. All the other hot geometry and scale changes are done
+  // automatically, and the |wayland_screen_| is notified immediately about the
+  // changes.
+  if (!output_list_.empty()) {
+    for (auto& output : output_list_) {
+      OnWaylandOutputAdded(output->output_id());
+      output->TriggerDelegateNotification();
+    }
+  }
+
+  return wayland_screen;
+}
+
+void WaylandOutputManager::OnWaylandOutputAdded(uint32_t output_id) {
+  if (wayland_screen_)
+    wayland_screen_->OnOutputAdded(output_id, IsPrimaryOutput(output_id));
+}
+
+void WaylandOutputManager::OnWaylandOutputRemoved(uint32_t output_id) {
+  if (wayland_screen_)
+    wayland_screen_->OnOutputRemoved(output_id);
+}
+
+bool WaylandOutputManager::IsPrimaryOutput(uint32_t output_id) const {
+  DCHECK(!output_list_.empty());
+  // The very first object in the |output_list_| is always treated as a primary
+  // output.
+  const auto& primary_output = output_list_.front();
+  return primary_output->output_id() == output_id;
+}
+
+void WaylandOutputManager::OnOutputHandleMetrics(uint32_t output_id,
+                                                 const gfx::Rect& new_bounds,
+                                                 int32_t scale_factor) {
+  if (wayland_screen_) {
+    wayland_screen_->OnOutputMetricsChanged(output_id, new_bounds, scale_factor,
+                                            IsPrimaryOutput(output_id));
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_output_manager.h b/ui/ozone/platform/wayland/wayland_output_manager.h
new file mode 100644
index 0000000..5fcdced
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_output_manager.h
@@ -0,0 +1,59 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_
+
+#include "ui/ozone/platform/wayland/wayland_object.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/ozone/platform/wayland/wayland_output.h"
+#include "ui/ozone/platform/wayland/wayland_screen.h"
+
+struct wl_output;
+
+namespace ui {
+
+class WaylandOutput;
+
+class WaylandOutputManager : public WaylandOutput::Delegate {
+ public:
+  WaylandOutputManager();
+  ~WaylandOutputManager() override;
+
+  // The first output in the vector is always a primary output.
+  bool IsPrimaryOutputReady() const;
+
+  void AddWaylandOutput(const uint32_t output_id, wl_output* output);
+  void RemoveWaylandOutput(const uint32_t output_id);
+
+  // Creates a platform screen and feeds it with existing outputs.
+  std::unique_ptr<WaylandScreen> CreateWaylandScreen();
+
+ private:
+  void OnWaylandOutputAdded(uint32_t output_id);
+  void OnWaylandOutputRemoved(uint32_t output_id);
+
+  bool IsPrimaryOutput(uint32_t output_id) const;
+
+  // WaylandOutput::Delegate:
+  void OnOutputHandleMetrics(uint32_t output_id,
+                             const gfx::Rect& new_bounds,
+                             int32_t scale_factor) override;
+
+  std::vector<std::unique_ptr<WaylandOutput>> output_list_;
+
+  // Non-owned wayland screen instance.
+  base::WeakPtr<WaylandScreen> wayland_screen_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandOutputManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_
diff --git a/ui/ozone/platform/wayland/wayland_pointer.cc b/ui/ozone/platform/wayland/wayland_pointer.cc
index 5aceb2f2..f977f63 100644
--- a/ui/ozone/platform/wayland/wayland_pointer.cc
+++ b/ui/ozone/platform/wayland/wayland_pointer.cc
@@ -212,4 +212,9 @@
   return flags_;
 }
 
+void WaylandPointer::ResetFlags() {
+  flags_ = 0;
+  keyboard_modifiers_ = 0;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_pointer.h b/ui/ozone/platform/wayland/wayland_pointer.h
index 50f60f4..0c398d1 100644
--- a/ui/ozone/platform/wayland/wayland_pointer.h
+++ b/ui/ozone/platform/wayland/wayland_pointer.h
@@ -27,6 +27,7 @@
   }
 
   int GetFlagsWithKeyboardModifiers();
+  void ResetFlags();
 
   WaylandCursor* cursor() { return cursor_.get(); }
 
diff --git a/ui/ozone/platform/wayland/wayland_screen.cc b/ui/ozone/platform/wayland/wayland_screen.cc
new file mode 100644
index 0000000..98ef35e
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_screen.cc
@@ -0,0 +1,107 @@
+// 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 "ui/ozone/platform/wayland/wayland_screen.h"
+
+#include "ui/display/display.h"
+#include "ui/display/display_finder.h"
+#include "ui/display/display_observer.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+WaylandScreen::WaylandScreen() : weak_factory_(this) {}
+
+WaylandScreen::~WaylandScreen() = default;
+
+void WaylandScreen::OnOutputAdded(uint32_t output_id, bool is_primary) {
+  display::Display new_display(output_id);
+  display_list_.AddDisplay(std::move(new_display),
+                           is_primary
+                               ? display::DisplayList::Type::PRIMARY
+                               : display::DisplayList::Type::NOT_PRIMARY);
+}
+
+void WaylandScreen::OnOutputRemoved(uint32_t output_id) {
+  display_list_.RemoveDisplay(output_id);
+}
+
+void WaylandScreen::OnOutputMetricsChanged(uint32_t output_id,
+                                           const gfx::Rect& new_bounds,
+                                           float device_pixel_ratio,
+                                           bool is_primary) {
+  display::Display changed_display(output_id);
+  changed_display.set_device_scale_factor(device_pixel_ratio);
+  changed_display.set_bounds(new_bounds);
+  changed_display.set_work_area(new_bounds);
+
+  display_list_.UpdateDisplay(
+      changed_display, is_primary ? display::DisplayList::Type::PRIMARY
+                                  : display::DisplayList::Type::NOT_PRIMARY);
+}
+
+base::WeakPtr<WaylandScreen> WaylandScreen::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+const std::vector<display::Display>& WaylandScreen::GetAllDisplays() const {
+  return display_list_.displays();
+}
+
+display::Display WaylandScreen::GetPrimaryDisplay() const {
+  auto iter = display_list_.GetPrimaryDisplayIterator();
+  if (iter == display_list_.displays().end())
+    return display::Display::GetDefaultDisplay();
+  return *iter;
+}
+
+display::Display WaylandScreen::GetDisplayForAcceleratedWidget(
+    gfx::AcceleratedWidget widget) const {
+  // TODO(msisov): implement wl_surface_listener::enter and
+  // wl_surface_listener::leave for a wl_surface to know what surface the window
+  // is located on.
+  //
+  // https://crbug.com/890271
+  NOTIMPLEMENTED_LOG_ONCE();
+  return GetPrimaryDisplay();
+}
+
+gfx::Point WaylandScreen::GetCursorScreenPoint() const {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return gfx::Point();
+}
+
+gfx::AcceleratedWidget WaylandScreen::GetAcceleratedWidgetAtScreenPoint(
+    const gfx::Point& point) const {
+  // TODO(msisov): implement this once wl_surface_listener::enter and ::leave
+  // are used.
+  //
+  // https://crbug.com/890271
+  NOTIMPLEMENTED_LOG_ONCE();
+  return gfx::kNullAcceleratedWidget;
+}
+
+display::Display WaylandScreen::GetDisplayNearestPoint(
+    const gfx::Point& point) const {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return GetPrimaryDisplay();
+}
+
+display::Display WaylandScreen::GetDisplayMatching(
+    const gfx::Rect& match_rect) const {
+  // TODO(msisov): https://crbug.com/890272
+  NOTIMPLEMENTED_LOG_ONCE();
+  return GetPrimaryDisplay();
+}
+
+void WaylandScreen::AddObserver(display::DisplayObserver* observer) {
+  display_list_.AddObserver(observer);
+}
+
+void WaylandScreen::RemoveObserver(display::DisplayObserver* observer) {
+  display_list_.RemoveObserver(observer);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_screen.h b/ui/ozone/platform/wayland/wayland_screen.h
new file mode 100644
index 0000000..f6a0a661
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_screen.h
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_
+#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "ui/display/display_list.h"
+#include "ui/ozone/platform/wayland/wayland_output.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_screen.h"
+
+namespace ui {
+
+// A PlatformScreen implementation for Wayland.
+class WaylandScreen : public PlatformScreen {
+ public:
+  WaylandScreen();
+  ~WaylandScreen() override;
+
+  void OnOutputAdded(uint32_t output_id, bool is_primary);
+  void OnOutputRemoved(uint32_t output_id);
+  void OnOutputMetricsChanged(uint32_t output_id,
+                              const gfx::Rect& bounds,
+                              float device_pixel_ratio,
+                              bool is_primary);
+
+  base::WeakPtr<WaylandScreen> GetWeakPtr();
+
+  // display::Screen implementation.
+  const std::vector<display::Display>& GetAllDisplays() const override;
+  display::Display GetPrimaryDisplay() const override;
+  display::Display GetDisplayForAcceleratedWidget(
+      gfx::AcceleratedWidget widget) const override;
+  gfx::Point GetCursorScreenPoint() const override;
+  gfx::AcceleratedWidget GetAcceleratedWidgetAtScreenPoint(
+      const gfx::Point& point) const override;
+  display::Display GetDisplayNearestPoint(
+      const gfx::Point& point) const override;
+  display::Display GetDisplayMatching(
+      const gfx::Rect& match_rect) const override;
+  void AddObserver(display::DisplayObserver* observer) override;
+  void RemoveObserver(display::DisplayObserver* observer) override;
+
+ private:
+  display::DisplayList display_list_;
+
+  base::ObserverList<display::DisplayObserver>::Unchecked observers_;
+
+  base::WeakPtrFactory<WaylandScreen> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandScreen);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_
diff --git a/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
new file mode 100644
index 0000000..75878aea
--- /dev/null
+++ b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
@@ -0,0 +1,189 @@
+// 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 <wayland-server.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display_observer.h"
+#include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_output_manager.h"
+#include "ui/ozone/platform/wayland/wayland_screen.h"
+#include "ui/ozone/platform/wayland/wayland_test.h"
+
+namespace ui {
+
+namespace {
+
+constexpr uint32_t kNumberOfDisplays = 1;
+constexpr uint32_t kOutputWidth = 1024;
+constexpr uint32_t kOutputHeight = 768;
+
+class TestDisplayObserver : public display::DisplayObserver {
+ public:
+  TestDisplayObserver() {}
+  ~TestDisplayObserver() override {}
+
+  display::Display GetDisplay() { return std::move(display_); }
+  uint32_t GetAndClearChangedMetrics() {
+    uint32_t changed_metrics = changed_metrics_;
+    changed_metrics_ = 0;
+    return changed_metrics;
+  }
+
+  // display::DisplayObserver:
+  void OnDisplayAdded(const display::Display& new_display) override {
+    display_ = new_display;
+  }
+
+  void OnDisplayRemoved(const display::Display& old_display) override {
+    display_ = old_display;
+  }
+
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override {
+    changed_metrics_ = changed_metrics;
+    display_ = display;
+  }
+
+ private:
+  uint32_t changed_metrics_ = 0;
+  display::Display display_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDisplayObserver);
+};
+
+}  // namespace
+
+class WaylandScreenTest : public WaylandTest {
+ public:
+  WaylandScreenTest() {}
+  ~WaylandScreenTest() override {}
+
+  void SetUp() override {
+    output_ = server_.output();
+    output_->SetRect(gfx::Rect(0, 0, kOutputWidth, kOutputHeight));
+
+    WaylandTest::SetUp();
+
+    output_manager_ = connection_->wayland_output_manager();
+    ASSERT_TRUE(output_manager_);
+  }
+
+ protected:
+  wl::MockOutput* output_ = nullptr;
+  WaylandOutputManager* output_manager_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WaylandScreenTest);
+};
+
+// Tests whether a primary output has been initialized before PlatformScreen is
+// created.
+TEST_P(WaylandScreenTest, OutputBaseTest) {
+  EXPECT_TRUE(output_manager_->IsPrimaryOutputReady());
+
+  std::unique_ptr<WaylandScreen> platform_screen =
+      output_manager_->CreateWaylandScreen();
+
+  // Ensure there is only one display, which is the primary one.
+  auto& all_displays = platform_screen->GetAllDisplays();
+  EXPECT_EQ(all_displays.size(), kNumberOfDisplays);
+
+  // Ensure the size property of the primary display.
+  EXPECT_EQ(platform_screen->GetPrimaryDisplay().bounds(),
+            gfx::Rect(0, 0, kOutputWidth, kOutputHeight));
+}
+
+TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) {
+  EXPECT_TRUE(output_manager_->IsPrimaryOutputReady());
+  std::unique_ptr<WaylandScreen> platform_screen =
+      output_manager_->CreateWaylandScreen();
+
+  TestDisplayObserver observer;
+  platform_screen->AddObserver(&observer);
+
+  // Add a second display.
+  wl::MockOutput output1;
+  output1.Initialize(server_.display());
+
+  Sync();
+
+  // Ensure that second display is not a primary one and have a different id.
+  int64_t added_display_id = observer.GetDisplay().id();
+  EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id);
+
+  // Remove the second output.
+  output_manager_->RemoveWaylandOutput(added_display_id);
+
+  Sync();
+
+  // Ensure that removed display has correct id.
+  int64_t removed_display_id = observer.GetDisplay().id();
+  EXPECT_EQ(added_display_id, removed_display_id);
+
+  // Add a second display again.
+  wl::MockOutput output2;
+  output2.Initialize(server_.display());
+
+  Sync();
+
+  // The newly added display is not a primary yet.
+  added_display_id = observer.GetDisplay().id();
+  EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id);
+
+  // Make sure the geometry changes are sent by syncing one more time again.
+  Sync();
+
+  int64_t old_primary_display_id = platform_screen->GetPrimaryDisplay().id();
+  output_manager_->RemoveWaylandOutput(old_primary_display_id);
+
+  // Ensure that previously added display is now a primary one.
+  EXPECT_EQ(platform_screen->GetPrimaryDisplay().id(), added_display_id);
+  // Ensure that the removed display was the one, which was a primary display.
+  EXPECT_EQ(observer.GetDisplay().id(), old_primary_display_id);
+}
+
+TEST_P(WaylandScreenTest, OutputPropertyChanges) {
+  std::unique_ptr<WaylandScreen> platform_screen =
+      output_manager_->CreateWaylandScreen();
+  TestDisplayObserver observer;
+  platform_screen->AddObserver(&observer);
+
+  const gfx::Rect new_rect(0, 0, 800, 600);
+  wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(),
+                          0 /* physical_width */, 0 /* physical_height */,
+                          0 /* subpixel */, "unkown_make", "unknown_model",
+                          0 /* transform */);
+  wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT,
+                      new_rect.width(), new_rect.height(), 0 /* refresh */);
+
+  Sync();
+
+  uint32_t changed_values = 0;
+  changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS;
+  changed_values |= display::DisplayObserver::DISPLAY_METRIC_WORK_AREA;
+  EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
+  EXPECT_EQ(observer.GetDisplay().bounds(), new_rect);
+
+  const float new_scale_value = 2.0f;
+  wl_output_send_scale(output_->resource(), new_scale_value);
+
+  Sync();
+
+  changed_values = 0;
+  changed_values |=
+      display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR;
+  EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
+  EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value);
+}
+
+INSTANTIATE_TEST_CASE_P(XdgVersionV5Test,
+                        WaylandScreenTest,
+                        ::testing::Values(kXdgShellV5));
+INSTANTIATE_TEST_CASE_P(XdgVersionV6Test,
+                        WaylandScreenTest,
+                        ::testing::Values(kXdgShellV6));
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_util.cc b/ui/ozone/platform/wayland/wayland_util.cc
index 1ad2b556..0cd0534 100644
--- a/ui/ozone/platform/wayland/wayland_util.cc
+++ b/ui/ozone/platform/wayland/wayland_util.cc
@@ -4,10 +4,15 @@
 
 #include "ui/ozone/platform/wayland/wayland_util.h"
 
+#include <xdg-shell-unstable-v5-client-protocol.h>
+#include <xdg-shell-unstable-v6-client-protocol.h>
+
 #include "base/memory/shared_memory.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "ui/base/hit_test.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/ozone/platform/wayland/wayland_connection.h"
 
 namespace wl {
 
@@ -16,6 +21,84 @@
 const uint32_t kShmFormat = WL_SHM_FORMAT_ARGB8888;
 const SkColorType kColorType = kBGRA_8888_SkColorType;
 
+uint32_t IdentifyDirectionV5(int hittest) {
+  uint32_t direction = 0;
+  switch (hittest) {
+    case HTBOTTOM:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM;
+      break;
+    case HTBOTTOMLEFT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM_LEFT;
+      break;
+    case HTBOTTOMRIGHT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM_RIGHT;
+      break;
+    case HTLEFT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_LEFT;
+      break;
+    case HTRIGHT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_RIGHT;
+      break;
+    case HTTOP:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP;
+      break;
+    case HTTOPLEFT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP_LEFT;
+      break;
+    case HTTOPRIGHT:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP_RIGHT;
+      break;
+    default:
+      direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_NONE;
+      break;
+      ;
+  }
+  return direction;
+}
+
+uint32_t IdentifyDirectionV6(int hittest) {
+  uint32_t direction = 0;
+  switch (hittest) {
+    case HTBOTTOM:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM;
+      break;
+    case HTBOTTOMLEFT:
+      direction = zxdg_toplevel_v6_resize_edge::
+          ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT;
+      break;
+    case HTBOTTOMRIGHT:
+      direction = zxdg_toplevel_v6_resize_edge::
+          ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT;
+      break;
+    case HTLEFT:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT;
+      break;
+    case HTRIGHT:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT;
+      break;
+    case HTTOP:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP;
+      break;
+    case HTTOPLEFT:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT;
+      break;
+    case HTTOPRIGHT:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT;
+      break;
+    default:
+      direction =
+          zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE;
+      break;
+  }
+  return direction;
+}
+
 }  // namespace
 
 wl_buffer* CreateSHMBuffer(const gfx::Size& size,
@@ -68,4 +151,12 @@
   canvas->drawBitmapRect(bitmap, damage, nullptr);
 }
 
+uint32_t IdentifyDirection(const ui::WaylandConnection& connection,
+                           int hittest) {
+  if (connection.shell_v6())
+    return IdentifyDirectionV6(hittest);
+  DCHECK(connection.shell());
+  return IdentifyDirectionV5(hittest);
+}
+
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/wayland_util.h b/ui/ozone/platform/wayland/wayland_util.h
index d104fc0..2a2db3ab 100644
--- a/ui/ozone/platform/wayland/wayland_util.h
+++ b/ui/ozone/platform/wayland/wayland_util.h
@@ -7,6 +7,8 @@
 
 #include <wayland-client.h>
 
+#include <stdint.h>
+
 #include "base/callback.h"
 #include "base/macros.h"
 #include "ui/ozone/platform/wayland/wayland_object.h"
@@ -17,6 +19,10 @@
 class SharedMemory;
 }
 
+namespace ui {
+class WaylandConnection;
+}
+
 namespace gfx {
 class Size;
 enum class SwapResult;
@@ -36,6 +42,11 @@
                       const base::SharedMemory& shared_memory,
                       const SkBitmap& bitmap);
 
+// Identifies the direction of the "hittest" for Wayland. |connection|
+// is used to identify whether values from shell v5 or v6 must be used.
+uint32_t IdentifyDirection(const ui::WaylandConnection& connection,
+                           int hittest);
+
 }  // namespace wl
 
 #endif  // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_UTIL_H_
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index cb5e40b4..27ddd18 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/hit_test.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/ozone/events_ozone.h"
@@ -73,7 +74,11 @@
     : delegate_(delegate),
       connection_(connection),
       xdg_shell_objects_factory_(new XDGShellObjectFactory()),
-      state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL) {}
+      state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL) {
+  // Set a class property key, which allows |this| to be used for interactive
+  // events, e.g. move or resize.
+  SetWmMoveResizeHandler(this, AsWmMoveResizeHandler());
+}
 
 WaylandWindow::~WaylandWindow() {
   PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
@@ -196,6 +201,20 @@
   connection_->ScheduleFlush();
 }
 
+void WaylandWindow::DispatchHostWindowDragMovement(
+    int hittest,
+    const gfx::Point& pointer_location) {
+  DCHECK(xdg_surface_);
+
+  connection_->ResetPointerFlags();
+  if (hittest == HTCAPTION)
+    xdg_surface_->SurfaceMove(connection_);
+  else
+    xdg_surface_->SurfaceResize(connection_, hittest);
+
+  connection_->ScheduleFlush();
+}
+
 void WaylandWindow::Show() {
   if (!is_tooltip_)  // Tooltip windows should not get keyboard focus
     set_keyboard_focus(true);
@@ -572,4 +591,8 @@
   return parent_window;
 }
 
+WmMoveResizeHandler* WaylandWindow::AsWmMoveResizeHandler() {
+  return static_cast<WmMoveResizeHandler*>(this);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h
index 4148648..e1cb0f6 100644
--- a/ui/ozone/platform/wayland/wayland_window.h
+++ b/ui/ozone/platform/wayland/wayland_window.h
@@ -12,6 +12,7 @@
 #include "ui/ozone/platform/wayland/wayland_object.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
 
 namespace gfx {
 class PointF;
@@ -32,7 +33,9 @@
 class XDGShellObjectFactory;
 }  // namespace
 
-class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
+class WaylandWindow : public PlatformWindow,
+                      public PlatformEventDispatcher,
+                      public WmMoveResizeHandler {
  public:
   WaylandWindow(PlatformWindowDelegate* delegate,
                 WaylandConnection* connection);
@@ -73,6 +76,11 @@
 
   bool is_active() const { return is_active_; }
 
+  // WmMoveResizeHandler
+  void DispatchHostWindowDragMovement(
+      int hittest,
+      const gfx::Point& pointer_location) override;
+
   // PlatformWindow
   void Show() override;
   void Hide() override;
@@ -131,6 +139,8 @@
   // Gets a parent window for this window.
   WaylandWindow* GetParentWindow(gfx::AcceleratedWidget parent_widget);
 
+  WmMoveResizeHandler* AsWmMoveResizeHandler();
+
   PlatformWindowDelegate* delegate_;
   WaylandConnection* connection_;
   WaylandWindow* parent_window_ = nullptr;
diff --git a/ui/ozone/platform/wayland/wayland_window_unittest.cc b/ui/ozone/platform/wayland/wayland_window_unittest.cc
index 2d885e3..a97b92eb 100644
--- a/ui/ozone/platform/wayland/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_window_unittest.cc
@@ -13,10 +13,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/hit_test.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
+#include "ui/ozone/platform/wayland/wayland_util.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
@@ -132,6 +134,17 @@
     return window;
   }
 
+  void InitializeWithSupportedHitTestValues(std::vector<int>* hit_tests) {
+    hit_tests->push_back(static_cast<int>(HTBOTTOM));
+    hit_tests->push_back(static_cast<int>(HTBOTTOMLEFT));
+    hit_tests->push_back(static_cast<int>(HTBOTTOMRIGHT));
+    hit_tests->push_back(static_cast<int>(HTLEFT));
+    hit_tests->push_back(static_cast<int>(HTRIGHT));
+    hit_tests->push_back(static_cast<int>(HTTOP));
+    hit_tests->push_back(static_cast<int>(HTTOPLEFT));
+    hit_tests->push_back(static_cast<int>(HTTOPRIGHT));
+  }
+
   wl::MockXdgSurface* xdg_surface_;
 
   MouseEvent test_mouse_event_;
@@ -649,6 +662,25 @@
   Sync();
 }
 
+TEST_P(WaylandWindowTest, DispatchWindowMove) {
+  EXPECT_CALL(*GetXdgSurface(), Move(_));
+  window_->DispatchHostWindowDragMovement(HTCAPTION, gfx::Point());
+}
+
+// Makes sure hit tests are converted into right edges.
+TEST_P(WaylandWindowTest, DispatchWindowResize) {
+  std::vector<int> hit_test_values;
+  InitializeWithSupportedHitTestValues(&hit_test_values);
+
+  for (const int value : hit_test_values) {
+    {
+      uint32_t direction = wl::IdentifyDirection(*(connection_.get()), value);
+      EXPECT_CALL(*GetXdgSurface(), Resize(_, Eq(direction)));
+      window_->DispatchHostWindowDragMovement(value, gfx::Point());
+    }
+  }
+}
+
 INSTANTIATE_TEST_CASE_P(XdgVersionV5Test,
                         WaylandWindowTest,
                         ::testing::Values(kXdgShellV5));
diff --git a/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc b/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc
index eb8f289..7f9cdf8 100644
--- a/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc
+++ b/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc
@@ -7,7 +7,9 @@
 #include <xdg-shell-unstable-v5-client-protocol.h>
 
 #include "base/strings/utf_string_conversions.h"
+#include "ui/base/hit_test.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_util.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 
 namespace ui {
@@ -53,20 +55,15 @@
 }
 
 void XDGSurfaceWrapperV5::SurfaceMove(WaylandConnection* connection) {
-  NOTIMPLEMENTED();
+  xdg_surface_move(xdg_surface_.get(), connection->seat(),
+                   connection->serial());
 }
 
 void XDGSurfaceWrapperV5::SurfaceResize(WaylandConnection* connection,
                                         uint32_t hittest) {
-  // TODO(msisov): implement resizing.
-  /*
-   * int direction;
-   * if (!IdentifyDirection(hittest, &direction))
-   *   return;
-   * xdg_surface_resize(xdg_surface_.get(), connection->seat(),
-   *                    connection->serial(), direction);
-   */
-  NOTIMPLEMENTED();
+  xdg_surface_resize(xdg_surface_.get(), connection->seat(),
+                     connection->serial(),
+                     wl::IdentifyDirection(*connection, hittest));
 }
 
 void XDGSurfaceWrapperV5::SetTitle(const base::string16& title) {
diff --git a/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc b/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc
index 14ab8434..f913d20 100644
--- a/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc
+++ b/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc
@@ -7,7 +7,9 @@
 #include <xdg-shell-unstable-v6-client-protocol.h>
 
 #include "base/strings/utf_string_conversions.h"
+#include "ui/base/hit_test.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_util.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 
 namespace ui {
@@ -82,20 +84,17 @@
 }
 
 void XDGSurfaceWrapperV6::SurfaceMove(WaylandConnection* connection) {
-  NOTIMPLEMENTED();
+  DCHECK(zxdg_toplevel_v6_);
+  zxdg_toplevel_v6_move(zxdg_toplevel_v6_.get(), connection->seat(),
+                        connection->serial());
 }
 
 void XDGSurfaceWrapperV6::SurfaceResize(WaylandConnection* connection,
                                         uint32_t hittest) {
-  // TODO(msisov): implement resizing.
-  /*
-   * int direction;
-   * if (!IdentifyDirection(hittest, &direction))
-   *   return;
-   * xdg_surface_resize(xdg_surface_.get(), connection->seat(),
-   *                    connection->serial(), direction);
-   */
-  NOTIMPLEMENTED();
+  DCHECK(zxdg_toplevel_v6_);
+  zxdg_toplevel_v6_resize(zxdg_toplevel_v6_.get(), connection->seat(),
+                          connection->serial(),
+                          wl::IdentifyDirection(*connection, hittest));
 }
 
 void XDGSurfaceWrapperV6::SetTitle(const base::string16& title) {
diff --git a/ui/platform_window/DEPS b/ui/platform_window/DEPS
index 0e42d64..b51da65 100644
--- a/ui/platform_window/DEPS
+++ b/ui/platform_window/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+ui/base/cursor",
-  "+ui/base/ime",
+  "+ui/base",
   "+ui/gfx",
 ]
diff --git a/ui/platform_window/platform_window.h b/ui/platform_window/platform_window.h
index 181ad85f..c7611ee 100644
--- a/ui/platform_window/platform_window.h
+++ b/ui/platform_window/platform_window.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/strings/string16.h"
+#include "ui/base/class_property.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
@@ -24,7 +25,7 @@
 //
 // Each instance of PlatformWindow represents a single window in the
 // underlying platform windowing system (i.e. X11/Win/OSX).
-class PlatformWindow {
+class PlatformWindow : public PropertyHandler {
  public:
   virtual ~PlatformWindow() {}
 
diff --git a/ui/platform_window/platform_window_handler/BUILD.gn b/ui/platform_window/platform_window_handler/BUILD.gn
new file mode 100644
index 0000000..be75a75
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+
+jumbo_component("platform_window_handler") {
+  output_name = "platform_window_handler_libs"
+
+  sources = [
+    "wm_move_resize_handler.cc",
+    "wm_move_resize_handler.h",
+    "wm_platform_export.h",
+  ]
+
+  defines = [ "PLATFORM_WINDOW_HANDLER_IMPLEMENTATION" ]
+
+  deps = [
+    "//ui/base",
+    "//ui/platform_window",
+  ]
+}
diff --git a/ui/platform_window/platform_window_handler/DEPS b/ui/platform_window/platform_window_handler/DEPS
new file mode 100644
index 0000000..381a2e478
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ui/base/class_property.h",
+  "+ui/platform_window/platform_window.h",
+]
diff --git a/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc b/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc
new file mode 100644
index 0000000..5a295a0
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc
@@ -0,0 +1,28 @@
+// 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 "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+
+#include "ui/base/class_property.h"
+#include "ui/platform_window/platform_window.h"
+
+DEFINE_UI_CLASS_PROPERTY_TYPE(WmMoveResizeHandler*);
+
+namespace ui {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(WmMoveResizeHandler*,
+                             kWmMoveResizeHandlerKey,
+                             nullptr);
+
+void SetWmMoveResizeHandler(PlatformWindow* platform_window,
+                            WmMoveResizeHandler* move_resize_handler) {
+  platform_window->SetProperty(kWmMoveResizeHandlerKey, move_resize_handler);
+}
+
+WmMoveResizeHandler* GetWmMoveResizeHandler(
+    const PlatformWindow& platform_window) {
+  return platform_window.GetProperty(kWmMoveResizeHandlerKey);
+}
+
+}  // namespace ui
diff --git a/ui/platform_window/platform_window_handler/wm_move_resize_handler.h b/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
new file mode 100644
index 0000000..cd5a696a
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
+
+#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
+
+namespace gfx {
+class Point;
+}  // namespace gfx
+
+namespace ui {
+
+class PlatformWindow;
+
+class WmMoveResizeHandler {
+ public:
+  // A system window manager starts interactive drag or resize of a window based
+  // on the |hittest| value. The |hittest| value identifies in which direction
+  // the window should be resized or whether it should be moved. See
+  // ui/base/hit_test.h for a concrete example with chromium symbolic names
+  // defined. The |pointer_location| indicates the position of the button press
+  // with respect to the platform window, which is needed when sending a
+  // move/resize request in such backends as X11. See _NET_WM_MOVERESIZE section
+  // in https://specifications.freedesktop.org/wm-spec/1.4/ar01s04.html.
+  //
+  // There is no need to implement this by all the platforms except Ozone/X11
+  // and Ozone/Wayland, compositors of which support interactive move/resize.
+  //
+  // This API must be used on mouse or touch events, which are targeted for
+  // non-client components (check ui/base/hit_test.h again) except the ones
+  // targeted for components like HTMAXBUTTON. In that case, the mouse events
+  // are used to identify clicks on maximize/minimize/restore buttons located in
+  // the top non-client area of the chromium window. See
+  // WindowEventFilter::OnMouseEvent for a concrete example of how mouse events
+  // are identified as client or non-client.
+  //
+  // When the API is called, there is no way to know that the call was
+  // successful or not. The browser continues performing as usual except that a
+  // system compositor does not send any mouse/keyboard/etc events until user
+  // releases a mouse button. Instead, the compositor sends new bounds, which a
+  // client uses to recreate gpu buffers and redraw visual represantation of the
+  // browser.
+  virtual void DispatchHostWindowDragMovement(
+      int hittest,
+      const gfx::Point& pointer_location) = 0;
+
+ protected:
+  virtual ~WmMoveResizeHandler() {}
+};
+
+WM_PLATFORM_EXPORT void SetWmMoveResizeHandler(
+    PlatformWindow* platform_window,
+    WmMoveResizeHandler* move_resize_handler);
+WM_PLATFORM_EXPORT WmMoveResizeHandler* GetWmMoveResizeHandler(
+    const PlatformWindow& platform_window);
+
+}  // namespace ui
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
diff --git a/ui/platform_window/platform_window_handler/wm_platform_export.h b/ui/platform_window/platform_window_handler/wm_platform_export.h
new file mode 100644
index 0000000..37165fb7
--- /dev/null
+++ b/ui/platform_window/platform_window_handler/wm_platform_export.h
@@ -0,0 +1,32 @@
+// 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 UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
+
+// Defines WM_PLATFORM_EXPORT so that functionality implemented by the
+// wm_platform module can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION)
+#define WM_PLATFORM_EXPORT __declspec(dllexport)
+#else
+#define WM_PLATFORM_EXPORT __declspec(dllimport)
+#endif  // defined(WM_PLATFORM_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION)
+#define WM_PLATFORM_EXPORT __attribute__((visibility("default")))
+#else
+#define WM_PLATFORM_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define WM_PLATFORM_EXPORT
+#endif
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index e78872c..0cb7f71 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -665,6 +665,7 @@
       "//services/ws/public/mojom",
       "//ui/aura",
       "//ui/platform_window",
+      "//ui/platform_window/platform_window_handler",
       "//ui/touch_selection",
       "//ui/wm",
       "//ui/wm/public",
@@ -1223,12 +1224,23 @@
       "corewm/desktop_capture_controller_unittest.cc",
       "widget/native_widget_aura_interactive_uitest.cc",
     ]
+
     deps += [
       "//ui/aura",
       "//ui/aura:test_support",
       "//ui/wm",
       "//ui/wm/public",
     ]
+
+    if (!is_chromeos && ((is_linux && !use_x11) || is_fuchsia)) {
+      sources += [ "widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc" ]
+    }
+
+    deps += [
+      "//ui/events/platform",
+      "//ui/platform_window",
+      "//ui/platform_window/platform_window_handler",
+    ]
   }
 
   if (use_x11) {
diff --git a/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
index a5e871e..6dd57cd7 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h"
 
+#include "ui/aura/screen_ozone.h"
 #include "ui/display/display.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_snapshot.h"
@@ -55,7 +56,16 @@
 //////////////////////////////////////////////////////////////////////////////
 
 display::Screen* CreateDesktopScreen() {
-  return new DesktopScreenOzone;
+  auto platform_screen = ui::OzonePlatform::GetInstance()->CreateScreen();
+  if (!platform_screen) {
+    // TODO: At the moment, only the Ozone/Headless uses this patch. Fix it:
+    // https://crbug.com/891613
+    LOG(ERROR) << "PlatformScreen is not implemented for this ozone platform. "
+                  "Falling back to old DesktopScreenOzone implementation. See "
+                  "https://crbug.com/872339 for details";
+    return new DesktopScreenOzone;
+  }
+  return new aura::ScreenOzone(std::move(platform_screen));
 }
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index dcf1d7d..fbfa8df1 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -6,13 +6,16 @@
 
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/transient_window_client.h"
+#include "ui/base/hit_test.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 #include "ui/views/corewm/tooltip_aura.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/desktop_aura/window_event_filter.h"
 #include "ui/views/widget/widget_aura_utils.h"
 #include "ui/views/window/native_frame_view.h"
 #include "ui/wm/core/window_util.h"
@@ -91,6 +94,19 @@
 void DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(
     const Widget::InitParams& params) {
   native_widget_delegate_->OnNativeWidgetCreated(true);
+
+  // Setup a non_client_window_event_filter, which handles resize/move, double
+  // click and other events.
+  DCHECK(!non_client_window_event_filter_);
+  std::unique_ptr<WindowEventFilter> window_event_filter =
+      std::make_unique<WindowEventFilter>(this);
+  auto* wm_move_resize_handler = GetWmMoveResizeHandler(*platform_window());
+  if (wm_move_resize_handler)
+    window_event_filter->SetWmMoveResizeHandler(
+        GetWmMoveResizeHandler(*(platform_window())));
+
+  non_client_window_event_filter_ = std::move(window_event_filter);
+  window()->AddPreTargetHandler(non_client_window_event_filter_.get());
 }
 
 void DesktopWindowTreeHostPlatform::OnWidgetInitDone() {}
@@ -135,6 +151,8 @@
   if (!weak_ref || got_on_closed_)
     return;
 
+  RemoveNonClientEventFilter();
+
   native_widget_delegate_->OnNativeWidgetDestroying();
 
   got_on_closed_ = true;
@@ -449,7 +467,34 @@
   return true;
 }
 
+void DesktopWindowTreeHostPlatform::DispatchEvent(ui::Event* event) {
+#if defined(USE_OZONE)
+  // Make sure the |event| is marked as a non-client if it's a non-client
+  // mouse down event. This is needed to make sure the WindowEventDispatcher
+  // does not set a |mouse_pressed_handler_| for such events, because they are
+  // not always followed with non-client mouse up events in case of
+  // Ozone/Wayland or Ozone/X11.
+  //
+  // Also see the comment in WindowEventDispatcher::PreDispatchMouseEvent..
+  aura::Window* content_window = desktop_native_widget_aura_->content_window();
+  if (content_window && content_window->delegate()) {
+    if (event->IsMouseEvent()) {
+      ui::MouseEvent* mouse_event = event->AsMouseEvent();
+      int flags = mouse_event->flags();
+      int hit_test_code = content_window->delegate()->GetNonClientComponent(
+          mouse_event->location());
+      if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE)
+        flags |= ui::EF_IS_NON_CLIENT;
+      mouse_event->set_flags(flags);
+    }
+  }
+#endif
+
+  WindowTreeHostPlatform::DispatchEvent(event);
+}
+
 void DesktopWindowTreeHostPlatform::OnClosed() {
+  RemoveNonClientEventFilter();
   got_on_closed_ = true;
   desktop_native_widget_aura_->OnHostClosed();
 }
@@ -492,6 +537,14 @@
   widget->GetRootView()->Layout();
 }
 
+void DesktopWindowTreeHostPlatform::RemoveNonClientEventFilter() {
+  if (!non_client_window_event_filter_)
+    return;
+
+  window()->RemovePreTargetHandler(non_client_window_event_filter_.get());
+  non_client_window_event_filter_.reset();
+}
+
 Widget* DesktopWindowTreeHostPlatform::GetWidget() {
   return native_widget_delegate_->AsWidget();
 }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index b6219dd..e0b98abd 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -12,6 +12,8 @@
 
 namespace views {
 
+class WindowEventFilter;
+
 class VIEWS_EXPORT DesktopWindowTreeHostPlatform
     : public aura::WindowTreeHostPlatform,
       public DesktopWindowTreeHost {
@@ -90,14 +92,19 @@
   bool ShouldCreateVisibilityController() const override;
 
   // WindowTreeHostPlatform:
+  void DispatchEvent(ui::Event* event) override;
   void OnClosed() override;
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
   void OnCloseRequest() override;
   void OnActivationChanged(bool active) override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostPlatformTest, HitTest);
+
   void Relayout();
 
+  void RemoveNonClientEventFilter();
+
   Widget* GetWidget();
 
   gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const;
@@ -113,6 +120,9 @@
 
   bool is_active_ = false;
 
+  // A handler for events intended for non client area.
+  std::unique_ptr<WindowEventFilter> non_client_window_event_filter_;
+
   base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatform);
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc
new file mode 100644
index 0000000..f959917f
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc
@@ -0,0 +1,272 @@
+// 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 "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
+
+#include "ui/aura/window_tree_host_platform.h"
+#include "ui/base/hit_test.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/views/test/views_interactive_ui_test_base.h"
+#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/desktop_aura/window_event_filter.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/native_frame_view.h"
+
+namespace views {
+
+namespace {
+
+bool IsNonClientComponent(int hittest) {
+  switch (hittest) {
+    case HTBOTTOM:
+    case HTBOTTOMLEFT:
+    case HTBOTTOMRIGHT:
+    case HTCAPTION:
+    case HTLEFT:
+    case HTRIGHT:
+    case HTTOP:
+    case HTTOPLEFT:
+    case HTTOPRIGHT:
+      return true;
+    default:
+      return false;
+  }
+  return true;
+}
+
+// A fake handler, which just stores the hittest and pointer location values.
+class FakeWmMoveResizeHandler : public ui::WmMoveResizeHandler {
+ public:
+  using SetBoundsCallback = base::RepeatingCallback<void(gfx::Rect)>;
+  explicit FakeWmMoveResizeHandler(ui::PlatformWindow* window)
+      : platform_window_(window), hittest_(-1) {}
+  ~FakeWmMoveResizeHandler() override = default;
+
+  void Reset() {
+    hittest_ = -1;
+    pointer_location_ = gfx::Point();
+  }
+
+  int hittest() const { return hittest_; }
+  gfx::Point pointer_location() const { return pointer_location_; }
+
+  void set_bounds(const gfx::Rect& bounds) { bounds_ = bounds; }
+
+  // ui::WmMoveResizeHandler
+  void DispatchHostWindowDragMovement(
+      int hittest,
+      const gfx::Point& pointer_location) override {
+    hittest_ = hittest;
+    pointer_location_ = pointer_location;
+
+    platform_window_->SetBounds(bounds_);
+  }
+
+ private:
+  ui::PlatformWindow* platform_window_;
+  gfx::Rect bounds_;
+
+  int hittest_ = -1;
+  gfx::Point pointer_location_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeWmMoveResizeHandler);
+};
+
+void SetExpectationBasedOnHittestValue(int hittest,
+                                       const FakeWmMoveResizeHandler& handler,
+                                       const gfx::Point& pointer_location) {
+  if (IsNonClientComponent(hittest)) {
+    // Ensure both the pointer location and the hit test value are passed to the
+    // fake move/resize handler.
+    EXPECT_EQ(handler.pointer_location().ToString(),
+              pointer_location.ToString());
+    EXPECT_EQ(handler.hittest(), hittest);
+    return;
+  }
+
+  // Ensure the handler does not receive the hittest value or the pointer
+  // location.
+  EXPECT_TRUE(handler.pointer_location().IsOrigin());
+  EXPECT_NE(handler.hittest(), hittest);
+}
+
+// This is used to return a customized result to NonClientHitTest.
+class HitTestNonClientFrameView : public NativeFrameView {
+ public:
+  explicit HitTestNonClientFrameView(Widget* widget)
+      : NativeFrameView(widget), hit_test_result_(HTNOWHERE) {}
+  ~HitTestNonClientFrameView() override {}
+
+  void set_hit_test_result(int component) { hit_test_result_ = component; }
+
+  // NonClientFrameView overrides:
+  int NonClientHitTest(const gfx::Point& point) override {
+    return hit_test_result_;
+  }
+
+ private:
+  int hit_test_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(HitTestNonClientFrameView);
+};
+
+// This is used to return HitTestNonClientFrameView on create call.
+class HitTestWidgetDelegate : public views::WidgetDelegate {
+ public:
+  HitTestWidgetDelegate(views::Widget* widget,
+                        HitTestNonClientFrameView* frame_view)
+      : widget_(widget), frame_view_(frame_view) {}
+  ~HitTestWidgetDelegate() override {}
+
+  void set_can_resize(bool can_resize) {
+    can_resize_ = can_resize;
+    widget_->OnSizeConstraintsChanged();
+  }
+
+  // views::WidgetDelegate:
+  bool CanResize() const override { return can_resize_; }
+  views::Widget* GetWidget() override { return widget_; }
+  views::Widget* GetWidget() const override { return widget_; }
+  views::NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
+    return frame_view_;
+  }
+
+ private:
+  views::Widget* widget_;
+  HitTestNonClientFrameView* frame_view_;
+  bool can_resize_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(HitTestWidgetDelegate);
+};
+
+}  // namespace
+
+class DesktopWindowTreeHostPlatformTest : public ViewsInteractiveUITestBase {
+ public:
+  DesktopWindowTreeHostPlatformTest() = default;
+  ~DesktopWindowTreeHostPlatformTest() override = default;
+
+ protected:
+  Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) {
+    Widget* toplevel = new Widget;
+    frame_view_ = new HitTestNonClientFrameView(toplevel);
+    delegate_ = new HitTestWidgetDelegate(toplevel, frame_view_);
+    Widget::InitParams toplevel_params =
+        CreateParams(Widget::InitParams::TYPE_WINDOW);
+    toplevel_params.native_widget =
+        new views::DesktopNativeWidgetAura(toplevel);
+    toplevel_params.delegate = delegate_;
+    toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    toplevel_params.bounds = bounds;
+    toplevel_params.remove_standard_frame = true;
+    toplevel->Init(toplevel_params);
+    return toplevel;
+  }
+
+  std::unique_ptr<ui::MouseEvent> CreateMouseEvent(
+      const gfx::Point& pointer_location,
+      ui::EventType event_type,
+      int flags) {
+    std::unique_ptr<ui::MouseEvent> mouse_event =
+        std::make_unique<ui::MouseEvent>(event_type, pointer_location,
+                                         pointer_location,
+                                         base::TimeTicks::Now(), flags, flags);
+    return mouse_event;
+  }
+
+  HitTestNonClientFrameView* frame_view_ = nullptr;
+  HitTestWidgetDelegate* delegate_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatformTest);
+};
+
+TEST_F(DesktopWindowTreeHostPlatformTest, HitTest) {
+  gfx::Rect bounds(0, 0, 100, 100);
+  std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds));
+  widget->Show();
+
+  aura::Window* window = widget->GetNativeWindow();
+  DesktopWindowTreeHostPlatform* host =
+      static_cast<DesktopWindowTreeHostPlatform*>(window->GetHost());
+
+  // Install a fake move/resize handler to intercept the move/resize call.
+  WindowEventFilter* non_client_filter =
+      host->non_client_window_event_filter_.get();
+  std::unique_ptr<FakeWmMoveResizeHandler> handler =
+      std::make_unique<FakeWmMoveResizeHandler>(host->platform_window());
+  non_client_filter->SetWmMoveResizeHandler(handler.get());
+
+  delegate_->set_can_resize(true);
+
+  // It is not important to use pointer locations corresponding to the hittests
+  // values used in the browser itself, because we fake the hit test results,
+  // which non client frame view sends back. Thus, just make sure the content
+  // window is able to receive these events.
+  gfx::Point pointer_location(10, 10);
+
+  constexpr int hittest_values[] = {
+      HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION,     HTLEFT,
+      HTRIGHT,  HTTOP,        HTTOPLEFT,     HTTOPRIGHT,    HTNOWHERE,
+      HTBORDER, HTCLIENT,     HTCLOSE,       HTERROR,       HTGROWBOX,
+      HTHELP,   HTHSCROLL,    HTMENU,        HTMAXBUTTON,   HTMINBUTTON,
+      HTREDUCE, HTSIZE,       HTSYSMENU,     HTTRANSPARENT, HTVSCROLL,
+      HTZOOM,
+  };
+
+  for (int hittest : hittest_values) {
+    handler->Reset();
+
+    // Set the desired hit test result value, which will be returned, when
+    // WindowEventFilter starts to perform hit testing.
+    frame_view_->set_hit_test_result(hittest);
+
+    gfx::Rect bounds = window->GetBoundsInScreen();
+
+    // The wm move/resize handler receives pointer location in the global screen
+    // coordinate, whereas event dispatcher receives event locations on a local
+    // system coordinate. Thus, add an offset of a new possible origin value of
+    // a window to the expected pointer location.
+    gfx::Point expected_pointer_location(pointer_location);
+    expected_pointer_location.Offset(bounds.x(), bounds.y());
+
+    if (hittest == HTCAPTION) {
+      // Move the window on HTCAPTION hit test value.
+      bounds =
+          gfx::Rect(gfx::Point(bounds.x() + 2, bounds.y() + 4), bounds.size());
+      handler->set_bounds(bounds);
+    } else if (IsNonClientComponent(hittest)) {
+      // Resize the window on other than HTCAPTION non client hit test values.
+      bounds = gfx::Rect(
+          gfx::Point(bounds.origin()),
+          gfx::Size(bounds.size().width() + 5, bounds.size().height() + 10));
+      handler->set_bounds(bounds);
+    }
+
+    // Send mouse down event and make sure the WindowEventFilter calls the
+    // move/resize handler to start interactive move/resize with the |hittest|
+    // value we specified.
+    auto mouse_down_event = CreateMouseEvent(
+        pointer_location, ui::ET_MOUSE_PRESSED, ui::EF_LEFT_MOUSE_BUTTON);
+    host->DispatchEvent(mouse_down_event.get());
+
+    // The test expectation is based on the hit test component. If it is a
+    // non-client component, which results in a call to move/resize, the handler
+    // must receive the hittest value and the pointer location in global screen
+    // coordinate system. In other cases, it must not.
+    SetExpectationBasedOnHittestValue(hittest, *handler.get(),
+                                      expected_pointer_location);
+    // Make sure the bounds of the content window are correct.
+    EXPECT_EQ(window->GetBoundsInScreen().ToString(), bounds.ToString());
+
+    // Dispatch mouse up event to release mouse pressed handler and be able to
+    // consume future events.
+    auto mouse_up_event = CreateMouseEvent(
+        pointer_location, ui::ET_MOUSE_RELEASED, ui::EF_LEFT_MOUSE_BUTTON);
+    host->DispatchEvent(mouse_up_event.get());
+  }
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
index 7350780..2f3f85b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/hit_test.h"
@@ -50,11 +51,8 @@
   // X11PropertyChangeWaiter:
   bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
     std::vector<Atom> hints;
-    if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) {
-      auto it = std::find(hints.cbegin(), hints.cend(), gfx::GetAtom(hint_));
-      bool hint_set = (it != hints.cend());
-      return hint_set != wait_till_set_;
-    }
+    if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints))
+      return base::ContainsValue(hints, gfx::GetAtom(hint_)) != wait_till_set_;
     return true;
   }
 
diff --git a/ui/views/widget/desktop_aura/window_event_filter.cc b/ui/views/widget/desktop_aura/window_event_filter.cc
index 530b079..3b01fb99 100644
--- a/ui/views/widget/desktop_aura/window_event_filter.cc
+++ b/ui/views/widget/desktop_aura/window_event_filter.cc
@@ -6,6 +6,7 @@
 
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_tree_host.h"
@@ -14,6 +15,7 @@
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
+#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
 #include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 #include "ui/views/widget/native_widget_aura.h"
@@ -21,6 +23,28 @@
 
 namespace views {
 
+namespace {
+
+bool CanPerformDragOrResize(int hittest) {
+  switch (hittest) {
+    case HTBOTTOM:
+    case HTBOTTOMLEFT:
+    case HTBOTTOMRIGHT:
+    case HTCAPTION:
+    case HTLEFT:
+    case HTRIGHT:
+    case HTTOP:
+    case HTTOPLEFT:
+    case HTTOPRIGHT:
+      return true;
+    default:
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
 WindowEventFilter::WindowEventFilter(DesktopWindowTreeHost* window_tree_host)
     : window_tree_host_(window_tree_host), click_component_(HTNOWHERE) {}
 
@@ -53,6 +77,12 @@
   }
 }
 
+void WindowEventFilter::SetWmMoveResizeHandler(
+    ui::WmMoveResizeHandler* handler) {
+  DCHECK(!handler_);
+  handler_ = handler;
+}
+
 void WindowEventFilter::OnClickedCaption(ui::MouseEvent* event,
                                          int previous_click_component) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
@@ -149,6 +179,18 @@
 
 void WindowEventFilter::MaybeDispatchHostWindowDragMovement(
     int hittest,
-    ui::MouseEvent* event) {}
+    ui::MouseEvent* event) {
+  if (handler_ && event->IsLeftMouseButton() &&
+      CanPerformDragOrResize(hittest)) {
+    // Some platforms (eg X11) may require last pointer location not in the
+    // local surface coordinates, but rather in the screen coordinates for
+    // interactive move/resize.
+    const gfx::Point last_pointer_location =
+        aura::Env::GetInstance()->last_mouse_location();
+    handler_->DispatchHostWindowDragMovement(hittest, last_pointer_location);
+    event->StopPropagation();
+    return;
+  }
+}
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/window_event_filter.h b/ui/views/widget/desktop_aura/window_event_filter.h
index a9bc0387..d3d7214 100644
--- a/ui/views/widget/desktop_aura/window_event_filter.h
+++ b/ui/views/widget/desktop_aura/window_event_filter.h
@@ -10,6 +10,10 @@
 #include "ui/events/event_handler.h"
 #include "ui/views/views_export.h"
 
+namespace ui {
+class WmMoveResizeHandler;
+}
+
 namespace views {
 class DesktopWindowTreeHost;
 
@@ -24,6 +28,11 @@
   // Overridden from ui::EventHandler:
   void OnMouseEvent(ui::MouseEvent* event) override;
 
+  // Sets a move resize handler. Currently initialized only by ozone platforms.
+  // See WaylandWindow::WaylandWindow in the wayland_window.cc file for an
+  // example.
+  void SetWmMoveResizeHandler(ui::WmMoveResizeHandler* handler);
+
  private:
   // Called when the user clicked the caption area.
   void OnClickedCaption(ui::MouseEvent* event, int previous_click_component);
@@ -52,6 +61,11 @@
   // components.
   int click_component_;
 
+  // A handler, which is used for interactive move/resize events if set and
+  // unless MaybeDispatchHostWindowDragMovement is overridden by a derived
+  // class.
+  ui::WmMoveResizeHandler* handler_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(WindowEventFilter);
 };
 
diff --git a/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
index e0a11a0..a688da8c 100644
--- a/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
+++ b/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -42,9 +43,8 @@
   bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
     std::vector<Atom> wm_states;
     if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) {
-      auto it = std::find(wm_states.cbegin(), wm_states.cend(),
-                          gfx::GetAtom("_NET_WM_STATE_HIDDEN"));
-      return it == wm_states.cend();
+      return !base::ContainsValue(wm_states,
+                                  gfx::GetAtom("_NET_WM_STATE_HIDDEN"));
     }
     return true;
   }
@@ -80,8 +80,7 @@
     std::vector<XID> stack;
     ui::GetXWindowStack(ui::GetX11RootWindow(), &stack);
     for (size_t i = 0; i < expected_windows_.size(); ++i) {
-      auto it = std::find(stack.cbegin(), stack.cend(), expected_windows_[i]);
-      if (it == stack.cend())
+      if (!base::ContainsValue(stack, expected_windows_[i]))
         return true;
     }
     return false;